Element.addMethods({
	within: function(element, x, y) {
		element = $(element);
		var position = element.cumulativeOffset();
		var dims = { width: element.getWidth(), height: element.getHeight() };
		
		var x_within = x > position.left && x < position.left + dims.width;
		var y_within = y > position.top && y < position.top + dims.height;
		
		return x_within && y_within;
	},
	isObserving: function(element, eventName, handler) {
		element = $(element);
		var registry = Element.retrieve(element, 'prototype_event_registry');
		if (Object.isUndefined(registry)) 
			return false;
		var responders = registry.get(eventName);
		if (!responders)
			return false;
		var responder = undefined;
		for (var i = 0; i < responders.length; ++i) {
			if (responders[i].handler === handler) {
				responder = responders[i]
				break;
			}
		}
		return responder != undefined;
	},
	observeIfNot: function(element, eventName, handler) {
		if (!element.isObserving(eventName, handler))
			element.observe(eventName, handler);
	}
});

Effect._transCache = [];

Effect._Disable = function() {
	Effect._transCache.push(Effect.DefaultOptions.transition);
	Effect.DefaultOptions.transition = Effect.Transitions.full;
}

Effect._Enable = function() {
	Effect.DefaultOptions.transition = Effect._transCache.pop()	|| Effect.Transitions.sinoidal;
}

Array.prototype.contains = function(item) {
	return this.indexOf(item) != -1;
}

var CornerSet = Class.create();
CornerSet.prototype = {
	initialize: function(prefix, suffix, size) {
		this.size = size;
		this.prefix = prefix;
		this.suffix = suffix;
		this.tl = this.getFile("tl");
		this.tr = this.getFile("tr");
		this.bl = this.getFile("bl");
		this.br = this.getFile("br");
	},
	getFile: function(which) {
		return this.prefix + which + this.suffix;
	},
	applyTo: function(element, zIndex) {
		var tl = new Element("div"), 
			tr = new Element("div"), 
			bl = new Element("div"), 
			br = new Element("div");
		
		br.style.position   = 
		bl.style.position 	= 
		tr.style.position 	= 
		tl.style.position 	= "absolute";
		
		tl.style.left   	= 
		tl.style.top    	= 
		tr.style.right  	= 
		tr.style.top    	= 
		bl.style.left   	= 
		bl.style.bottom 	= 
		br.style.right  	= 
		br.style.bottom 	= "0px";
		
		tl.style.width 		= 
		tl.style.height 	= 
		tr.style.width 		= 
		tr.style.height 	= 
		bl.style.width 		= 
		br.style.height 	= 
		bl.style.height 	= 
		br.style.width 		= this.size + "px";
		
		tl.style.zIndex		=
		tr.style.zIndex		=
		bl.style.zIndex		=
		br.style.zIndex		= zIndex || "auto";
		
		tl.style.backgroundImage = this.urlize(this.tl);
		tr.style.backgroundImage = this.urlize(this.tr);
		bl.style.backgroundImage = this.urlize(this.bl);
		br.style.backgroundImage = this.urlize(this.br);
		
		element.appendChild(tl);
		element.appendChild(tr);
		element.appendChild(bl);
		element.appendChild(br);
	},
	urlize: function(input) {
		return "url(" + input + ")";
	}
};

var RoundedBorder = Class.create();
RoundedBorder.prototype = {
	initialize: function(cornerSet, color, size) {
		this.cornerSet = cornerSet;
		this.color = color;
		this.size = size;
	},
	applyTo: function(element, zIndex) {
		var border = new Element("div");
		border.style.position = "absolute";
		border.style.top = "0px";
		border.style.left = "0px";
		border.style.width = (element.getWidth() - (this.size * 2)) + "px";
		border.style.height = (element.getHeight() - (this.size * 2)) + "px";
		
		if (this.size > 0)
			border.style.border = this.size + "px solid #" + this.color;
		else border.style.border = "none";
		border.style.zIndex = zIndex || "auto";
		this.cornerSet.applyTo(border);
		element.appendChild(border);
		return border;
	}
};

var OrderedGrid = Class.create();
OrderedGrid.CreateCoverNotice = function(grid, text, corners) {
	var dims = grid.getSize();
	var element = new Element('div');
	element.update(text);
	element.style.position = "absolute";
	element.style.width = dims.width + "px";
	element.style.height = dims.height + "px";
	element.style.lineHeight = dims.height + "px";
	element.style.fontFamily = "Georgia, serif";
	element.style.fontSize = "30px";
	element.style.fontStyle = "italic";
	element.style.textAlign = "center";
	element.style.color = "#b1bfb1";
	element.style.backgroundImage = "url(images/transback.png)";
	if (corners) {
		corners.applyTo(element);
	}
	return element;
};
OrderedGrid.prototype = {
	initialize: function(options) {
		this.options = Object.extend({
			width: 100,
			height: 100,
			boundingWidth: 720,
			padding: 4,
			items: []
		}, options);
		
		var cols = Math.floor(this.options.boundingWidth / this.options.width);
		
		if ((cols * this.options.padding) + this.boundingWidth > this.boundingWidth)
			cols--;
			
		this.options.cols = cols;
	},
	getHeight: function() {
		var rows = Math.ceil(this.options.items.length / this.options.cols);
		var tileHeight = this.options.height * rows;
		var paddingHeight = this.options.padding * (rows - 1 >= 0 ? (rows - 1) : 0);
		
		return tileHeight + paddingHeight;
	},
	getSize: function() {
		return {width: this.options.boundingWidth, height: this.getHeight()};
	},
	getPosition: function(item) {
		var order = this.getOrder(item);
		
		if (order == -1)
			return;
			
		var p_x = (order % this.options.cols) * (this.options.width + this.options.padding);
		var p_y = Math.floor(order / this.options.cols) * (this.options.height + this.options.padding);
		
		if (this.options.centered)
			p_x += Math.floor(this.options.boundingWidth / 2) - Math.floor(this.getEffectiveWidth(this.getRowSize(order)) / 2);
		
		return { x: p_x, y: p_y };
	},
	getEffectiveWidth: function(count) {
		return (count * (this.options.width + this.options.padding)) - this.options.padding;
	},
	getRowSize: function(item) {
		return ((Math.ceil((item+1) / this.options.cols) * this.options.cols) <= this.options.items.length)
					? this.options.cols
					: this.remainder();
	},
	getOrder: function(item) {
		return this.options.items.indexOf(item);
	},
	remainder: function() {
		return this.options.items.length % this.options.cols;
	},
	setFirst: function(item) {
		var currentOrder = this.getOrder(item);
		var newItemArray = [];
		for (var i = 0; i < currentOrder; ++i)
			newItemArray[i + 1] = this.options.items[i];
		newItemArray[0] = this.options.items[currentOrder];
		this.options.items = newItemArray;
	},
	push: function(item) {
		this.options.items.push(item);
	},
	count: function() {
		return this.options.items.length;
	},
	contains: function(item) {
		return this.options.items.contains(item);
	},
	clear: function() {
		this.options.items = [];
	}
};

OrderedGrid.Iterator = Class.create();
OrderedGrid.Iterator.prototype = {
	initialize: function(orderedGrid) {
		this.grid = orderedGrid;
		this.activeIndex = -1;
		this.mutationListeners = [];
		this.disabled = false;
	},
	mutate: function(operation, operationName) {
		if (this.disabled == true)
			return;
			
		var oldIndex = this.activeIndex;
		var oldItem = this.activeItem;
		this.activeIndex = operation(this.activeIndex);
		this.activeItem = this.itemAtIndex(this.activeIndex);
		this.fireMutationListeners(operationName, oldIndex, oldItem);
		//if (this.activeItem == undefined)
		//	this.activeItem = oldItem;
		return this.active();
	},
	active: function() {
		return this.activeItem;
	},
	previewNextItem: function() {
		return this.grid.options.items[(this.activeIndex + 1) % this.grid.options.items.length];
	},
	previewPreviousItem: function() {
		return this.grid.options.items[(this.activeIndex - 1) % this.grid.options.items.length];
	},
	itemAtIndex: function(index) {
		return index < this.grid.options.items.length && index >= 0 
					? this.grid.options.items[index]
					: undefined;
	},
	next: function() {
		return this.mutate(
			OrderedGrid.Iterator.Operation.getOperation(OrderedGrid.Iterator.Operation.ADD).bind(this),
			OrderedGrid.Iterator.Operation.ADD
		);
	},
	previous: function() {
		return this.mutate(
			OrderedGrid.Iterator.Operation.getOperation(OrderedGrid.Iterator.Operation.SUBTRACT).bind(this),
			OrderedGrid.Iterator.Operation.SUBTRACT
		);
	},
	select: function(index) {
		this.mutate(
			OrderedGrid.Iterator.Operation.getOperation(OrderedGrid.Iterator.Operation.SELECT).bindAsEventListener(this, index),
			OrderedGrid.Iterator.Operation.SELECT
		);
	},
	selectItem: function(item) {
		this.select(this.grid.options.items.indexOf(item));
	},
	deactivate: function() {
		this.mutate(
			OrderedGrid.Iterator.Operation.getOperation(OrderedGrid.Iterator.Operation.DEACTIVATE),
			OrderedGrid.Iterator.Operation.DEACTIVATE
		);
	},
	disable: function() {
		this.disabled = true;
	},
	enable: function() {
		this.disabled = false;
	},
	reset: function() {
		this.select(0);
	},
	addMutationListener: function(listener) {
		this.mutationListeners.push(listener);
	},
	fireMutationListeners: function(operation, oldIndex, oldItem) {
		this.mutationListeners.each(function(listener, active, oldActive, operation) {
			listener(active, oldActive, operation);
		}.bindAsEventListener(this, this.active(), oldItem, operation));
	}
};

OrderedGrid.Iterator.Operation = {
	ADD: "add",
	SUBTRACT: "subtract",
	SELECT: "select",
	NEXT: "add",
	PREVIOUS: "subtract",
	ADVANCE: "add",
	RETREAT: "subtract",
	CUSTOM: "custom",
	DEACTIVATE: "deactivate",
	CLOSE: "deactivate",
	getOperation: function(identifier) {
		return this.functions.get(identifier);
	},
	isSelector: function(operationName) {
		return operationName != this.SELECT 
			&& operationName != this.CUSTOM
			&& operationName != this.DEACTIVATE;
	},
	functions: $H({
		add: function (index) {
			return ++index % this.grid.count();
		},
		subtract: function(index) {
			index--; 
			return index >= 0 ? index : (this.grid.count() - 1);
		},
		select: function(index, selIndex) { 
			return selIndex;
		},
		deactivate: function() {
			return -1;
		}
	})
};

var EffectRegistry = Class.create();
EffectRegistry.prototype = {
	initialize: function() {
		this.registry = [];
	},
	add: function(effect) {
		this.registry.push(effect);
		effect.options.afterFinish = this.remove.bind(this);
	},
	remove: function(event) {
		if (event.aggressive)
			this.hasAggressive = false;
		this.registry.splice(this.registry.indexOf(event), 1);
	},
	hasEffect: function() {
		return this.registry.length > 0;
	},
	cancel: function() {
		for (var i = 0; i < this.registry.length; ++i) {
			if (this.registry[i].inviolable)
				continue;
			this.registry[i].cancel();
		}
		this.registry = [];
	},
	addThese: function(effects) {
		$A(effects).each(function(e) {
			this.add(e);
		}.bind(this));
	},
	exclusive: function(effect) {
		if (this.hasAggressive)
			return effect;
		this.cancel();
		this.add(effect);
		return effect;
	},
	inviolable: function(effect) {
		effect.inviolable = true;
		this.add(effect);
		return effect;
	},
	aggressive: function(effect) {
		this.hasAggressive = true;
		effect.aggressive = true;
		this.add(effect);
		return effect;
	}
};

var EffectDock = Class.create();
EffectDock.prototype = {
	initialize: function() {
		this.dock = [];
	},
	add: function(effect) {
		this.dock.push(effect);
		return this;
	},
	addThese: function(effects) {
		$A(effects).each(function(e) {
			this.add(e);
		}.bind(this));
		return this;
	},
	depart: function(duration) {
		var effect = new Effect.Parallel(this.dock, {duration: duration});
		this.dock = [];
		return effect;
	}
}

var JPGallerySelectorSet = Class.create();
JPGallerySelectorSet.prototype = {
	initialize: function(
							imageContainer,
							image,
							imageTitle,
							imageDesc,
							imageLabelDummy,
							thumbContainer,
							thumb,
							thumbLabel,
							content,
							contentImage
						) {
		this.imageContainer = imageContainer;
		this.image = image;
		this.imageTitle = imageTitle;
		this.imageDesc = imageDesc;
		this.imageLabelDummy = imageLabelDummy;
		this.thumbContainer = thumbContainer;
		this.thumb = thumb;
		this.thumbLabel = thumbLabel;
		this.content = content || undefined;
		this.contentImage = contentImage || undefined;
	},
	getElements: function(uid) {
		elements = {};
		elements.image = 				$$(this.selector(this.imageContainer, uid)).first();
		elements.image_image = 			elements.image.down(this.selector(this.image));
		elements.image_title = 			elements.image.down(this.selector(this.imageTitle));
		elements.image_desc = 			elements.image.down(this.selector(this.imageDesc));
		elements.image_label_dummy = 	elements.image.down(this.selector(this.imageLabelDummy));
		
		elements.link = 				$$(this.selector(this.thumbContainer, uid)).first();
		elements.thumb = 				elements.link.down(this.selector(this.thumb));
		elements.hint =					elements.link.down(this.selector(this.thumbLabel));
		
		if (this.content) {
			elements.content = 			elements.image_image.down(this.selector(this.content));
			elements.content_image =    elements.image_image.down(this.selector(this.contentImage));
			elements.contentHeight = 	elements.content.getHeight();
		}
		
		return elements;
	},
	selector: function() {
		var result = "";
		for (var i = 0; i < arguments.length; ++i)
			result += "." + arguments[i];
		return result;
	}
};

var JPGallery = Class.create();
JPGallery.prototype = {
	initialize: function(pageId, pages, tags, selectorSet, options) {
		this.options = Object.extend({
			pageWidth: 720,
			thumbWidth: 140,
			thumbHeight: 140,
			thumbPadding: 5,
			tagWidth: 117,
			tagHeight: 20,
			tagPadding: 3,
			tagsTopPadding: 20,
			tagsTopTipSize: 20,
			tagsBottomPadding: 20,
			gridSpacing: 100,
			tagsTopTip: "test!",
			thumbsTip: "Test, again!",
			unaryGridSpacing: 60,
			relegatedOpacity: 0.2,
			disabledOpacity: 0.5,
			activeOpacity: 1.0,
			tagBorderActive: 0.6,
			tagBorderHighlight: 0.8,
			tagBorderInactive: 0.0,
			curlyBracketFile: "images/curlyBracket.png",
			tagsText: "tags",
			showNext: true,
			showPrevious: true,
			showClose: true,
			tagCloud: true,
			tagCloudMin: 9,
			tagCloudMax: 16,
			tagCloudMinWeight: 400,
			tagCloudMaxWeight: 900,
			tagCloudBias: Effect.Transitions.sinoidal//Prototype.K
		}, options || { })
	
		this.activeGrid = new OrderedGrid({ width: this.options.thumbWidth, height: this.options.thumbHeight, padding: this.options.thumbPadding, centered: true });
		this.activeIterator = new OrderedGrid.Iterator(this.activeGrid);
		this.inactiveGrid = new OrderedGrid({ width: this.options.thumbWidth, height: this.options.thumbHeight, padding: this.options.thumbPadding, centered: true });
		
		console.log(pageId);
		
		if (pageId == 'photography')  {
			$$("._photog_i").each(function(element) {
				element.setAttribute('src', element.getAttribute('_src'));
			});
			$$("._photog_i_0").each(function(element) {
				element.setAttribute('src', element.getAttribute('_src'));
			});
		} else if (pageId == 'development') {
			$$("._dev_i").each(function(element) {
				element.setAttribute('src', element.getAttribute('_src'));
			});
			$$("._dev_b").each(function(element) {
				element.style.backgroundImage = 'url(' + element.getAttribute('_src') + ')';
			});
		} else if (pageId == 'design') {
			$$("._design_i").each(function(element) {
				element.setAttribute('src', element.getAttribute('_src'));
			});
			$$("._design_i_0").each(function(element) {
				element.setAttribute('src', element.getAttribute('_src'));
			});
		}
		
		this.selectorSet = selectorSet;
		this.pageId = pageId;
		this.pages = pages;
		this.tags = tags;
		
		this.thumbs = $(this.pageId + "_thumbs");
		this.frame = $(this.pageId + "_selPhotoFrame");
		this.nextButton = $(this.pageId + "_nextButton");
		this.previousButton = $(this.pageId + "_previousButton");
		this.closeButton = $(this.pageId + "_closeButton");
		this.tagElement = $(this.pageId + "_tags");
		
		this.whiteCorners = new CornerSet("images/", "_white.png", 4);
		this.whiteBorder = new RoundedBorder(this.whiteCorners, "FFFFFF", 1);
		
		this.preset = PortfolioJS.page.hparams != "" && PortfolioJS.page.hval == this.pageId ? PortfolioJS.page.hparams : undefined;
		if (this.preset) {
			this.preset = this.preset.match(/\$.*/)[0].substring(1);
			this.preset = this.parsePreset(this.preset);
		}
		
		this._dismissCoverNotice = this.dismissCoverNotice.bind(this);
		this.pages.each(function(pair) {
			var row = pair.value;
			
			row.elements = this.selectorSet.getElements(pair.key);
			row.effects = new EffectRegistry();
			row.highlights = new EffectRegistry();
			
			/*row.updateHeight = function() {
				if (this.elements.content) {
					var nheight = this.elements.contentHeight() >= 720 ? 720 : this.elements.contentHeight();
					this.height = nheight;
					this.heightWithCaption = nheight + 80;
					this.elements.image_image.style.height = this.height + "px";
					this.elements.image.style.height = this.heightWithCaption + "px";
				} else this.height = parseInt(this.height);
			}.bind(row);
			row.updateHeight();
			if (row.elements.content)
				row.elements.content_image.observe("load", function(empty, row) {
					row.updateHeight();
					if (this.activeUid() == row.uid) {
						console.log(123);
						var na = this.noAnim;
						this.noAnim = true;
						this.updateImage(row.uid);
						this.updateFrame(row.uid);
						this.noAnim = na;
					}
				}.bindAsEventListener(this, row));*/
			row.height = parseInt(row.height);	
				
			if (row.elements.content) {
				row.height += row.elements.contentHeight;
				row.elements.image_image.style.overflow = row.height <= 720 ? "hidden" : "auto";
				row.height = row.height >= 720 ? 720 : row.height;
				row.elements.image_image.style.height = row.height + "px";
			}
			
			row.heightWithCaption = row.height + 80;
			
			this.activeGrid.push(pair.key);
			
			row.listeners = {};
			row.listeners.clickLink = this.activateImageListener.bindAsEventListener(this, pair.key);
			row.listeners.mouseOverLink = this.showHint.bindAsEventListener(this, pair.key);
			row.listeners.mouseOutLink = this.hideHint.bindAsEventListener(this, pair.key);
			row.listeners.showTags = this.showTags.bindAsEventListener(this, pair.key);
			row.listeners.hideTags = this.hideTags.bindAsEventListener(this, pair.key);
			
			if (this.options.cornerSet)
				this.options.cornerSet.applyTo(row.elements.link, 3);
			row.elements.overlay_glow = this.whiteBorder.applyTo(row.elements.link, 2);
			
			row.elements.hintDisabled = false;	
			row.elements.link.observe("click", row.listeners.clickLink);
			row.elements.link.observe("mouseover", row.listeners.mouseOverLink);
			row.elements.link.observe("mouseout", row.listeners.mouseOutLink);
			row.elements.link.observe("mouseover", row.listeners.showTags);
			row.elements.link.observe("mouseout", row.listeners.hideTags);
			row.elements.overlay_glow.setOpacity(0.0);
			
			row.elements.link.setOpacity(this.options.disabledOpacity);
			row.elements.hint.setOpacity(0.75);
			row.elements.image.style.height = row.heightWithCaption + "px";
			row.elements.image.style.bottom = "-" + row.heightWithCaption + "px";
			
			var position = this.activeGrid.getPosition(pair.key);
			row.elements.link.style.left = position.x + "px";
			row.elements.link.style.top = position.y + "px";
		}.bind(this));
		
		this.dismissListener = this.dismissTip.bind(this);
		this.tagGrid = new OrderedGrid({ width: 117, height: 20, padding: 3, centered:true});
		this.tags.each(function(pair) {
			var row = pair.value;
			row.element = $(row.uid);
			row.element.observe("click", this.toggleTag.bindAsEventListener(this, pair.key));
			row.element.observe("click", this.dismissListener);
			row.element.observe("mouseover", this.highlightTag.bindAsEventListener(this, pair.key));
			row.element.observe("mouseout", this.dehighlightTag.bindAsEventListener(this, pair.key));
			
			if (this.preset)
				row.active = this.preset.tags.contains(pair.key);
			else row.active = true;
			
			this.tagGrid.push(pair.key);
			
			if (this.options.cornerSet)
				this.options.cornerSet.applyTo(row.element, 3);
				
			row.border = this.whiteBorder.applyTo(row.element, 2);
			row.border.setOpacity(0.0);
			row.borderEffects = new EffectRegistry();
			
			if (this.options.tagCloud) {
				this.tagRefMax = (this.tagRefMax || 0) < row.refs.length ? row.refs.length : this.tagRefMax;
				this.tagRefMin = this.tagRefMin ? (this.tagRefMin > row.refs.length ? row.refs.length : this.tagRefMin) : row.refs.length;
			}
		}.bind(this));
		
		if (this.options.tagCloud)
			this.tagRange = this.tagRefMax - this.tagRefMin;
		
		this.dock = new EffectDock();
		
		if (this.options.cornerSet)
			this.options.cornerSet.applyTo(this.frame, 10);
		PortfolioJS.content.setContentHeight(this.pageId, this.getThumbsHeight());
		
		this.options.calculatedTopMargin = this.options.tagsTopPadding + this.options.tagsTopTipSize + this.options.tagsBottomPadding + this.tagGrid.getHeight();
		this.thumbs.style.marginTop = this.options.calculatedTopMargin + "px";
		this.createThumbTopTip();
		
		this.tags.each(function(pair) {
			var position = this.tagGrid.getPosition(pair.key);
			position.y -= this.tagGrid.getHeight() + this.options.tagsBottomPadding;
			pair.value.element.style.left = position.x + "px";
			pair.value.element.style.top = position.y + "px";
			
			if (this.options.tagCloud) {
				var tagRangePos = pair.value.refs.length - this.tagRefMin;
				var rangePercent = this.options.tagCloudBias(tagRangePos / this.tagRange);
				var fontSize = Math.round(this.options.tagCloudMin + ((this.options.tagCloudMax - this.options.tagCloudMin) * rangePercent));
				pair.value.element.style.fontSize = fontSize + "px";
			}
		}.bind(this));
		
		this.nextButton.observe("click", this.nextImage.bind(this));
		this.previousButton.observe("click", this.previousImage.bind(this));
		this.closeButton.observe("click", this.closeImage.bind(this));
		
		if (!this.options.showNext)
			this.nextButton.hide();
		if (!this.options.showPrevious)
			this.previousButton.hide();
		if (!this.options.showClose)
			this.closeButton.hide();
		
		this.effects = {
			helpers: new EffectRegistry(),
			frame: new EffectRegistry(),
			tags: new EffectRegistry()
		};
		
		this.helperOnListener = this.toggleHelpers.bindAsEventListener(this, true);
		this.helperOffListener = this.toggleHelpers.bindAsEventListener(this, false);
		
		this.frame.observe("mouseover", this.helperOnListener);
		this.frame.observe("mouseout", this.helperOffListener);
		
		
		this.allowChanges = true;
					
		Effect._Disable();

		if (this.preset != undefined) {
			this.noAnim = true;
			this.pages.each(function (pair) {
				if (pair.value.id == this.preset.image) {
					this.activeIterator.selectItem(pair.key);
					this.activateImage(pair.key);
				}
			}.bind(this));
			this.noAnim = false;

			this.setActiveTags(this.preset.tags);
			if (this.preset.tags.length > 0)
				this.dismissTip();
		} else {
			this.setActiveTags([]);
			//this._createCoverNotice(1.0);
		}
		
		Effect._Enable();
		
		this.activeIterator.addMutationListener(this.changeImage.bind(this));
		this.setAnimsEnabled();
		
		this.applyStateCodes();
	},
	showTags: function(event, uid) {
		this.tags.each(function(pair, uid) {
			var row = pair.value;
			if (!row.refs.contains(uid)) {
				if (!row.refs.contains(this.activeUid()))
					return;
				else
					this.dock.add(
						row.borderEffects.exclusive(
							new Effect.Opacity(row.border, {sync:true, from: row.border.getOpacity(), to: this.options.tagBorderInactive})
						)
					);
			} else if (row.refs.contains(this.activeUid()))
				return;
			else
				this.dock.add(
					row.borderEffects.exclusive(
						new Effect.Opacity(row.border, {sync:true, from: row.border.getOpacity(), to: this.options.tagBorderHighlight})
					)
				);
		}.bindAsEventListener(this,uid));
		
		this.dock.depart(0.2);
	},
	hideTags: function(event, uid) {
		this.tags.each(function(pair, uid) {
			var row = pair.value;
			if (!row.refs.contains(uid)) {
				if (!row.refs.contains(this.activeUid()))
					return;
				else
					this.dock.add(
						row.borderEffects.exclusive(
							new Effect.Opacity(row.border, {sync:true, from: row.border.getOpacity(), to: this.options.tagBorderActive})
						)
					);
			} else if (row.refs.contains(this.activeUid()))
				return;
			else
				this.dock.add(
					row.borderEffects.exclusive(
						new Effect.Opacity(row.border, {sync:true, from: row.border.getOpacity(), to: this.options.tagBorderInactive})
					)
				);
		}.bindAsEventListener(this,uid));
		
		this.dock.depart(0.2);
	},
	highlightTagsFor: function(uid) {
		this.tags.each(function(pair, uid) {
			var row = pair.value;
			if (row.refs.contains(uid))
				this.dock.add(
					row.borderEffects.exclusive(
						new Effect.Opacity(row.border, {sync:true, from: row.border.getOpacity(), to: this.options.tagBorderActive})
					)
				);
			else 
				this.dock.add(
					row.borderEffects.exclusive(
						new Effect.Opacity(row.border, {sync:true, from: row.border.getOpacity(), to: this.options.tagBorderInactive})
					)
				);
		}.bindAsEventListener(this, uid));
	},
	_createCoverNotice: function(startOpacity) {
		this._coverNotice = OrderedGrid.CreateCoverNotice(this.inactiveGrid, this.options.thumbsTip, this.options.cornerSet);
		this._coverNotice.style.top = this.options.unaryGridSpacing + "px";
		this._coverNotice.style.left = "0px";
		
		this.coverNotice = new Element("div");
		this.coverNotice.style.position = "absolute";
		this.coverNotice.style.width = this.options.pageWidth + "px";
		this.coverNotice.style.height = (this.options.gridSpacing + this.inactiveGrid.getHeight()) + "px";
		this.coverNotice.style.backgroundImage = "url(" + this.options.curlyBracketFile + ")";
		this.coverNotice.style.backgroundRepeat = "no-repeat";
		this.coverNotice.style.backgroundPosition = "top center";
		this.coverNotice.style.lineHeight = this.options.unaryGridSpacing + "px";
		this.coverNotice.style.fontSize = "24px";
		this.coverNotice.style.fontStyle = "italic";
		this.coverNotice.style.textAlign = "center";
		this.coverNotice.style.fontFamily = "Georgia, serif";
		this.coverNotice.style.color = "#222522";
		this.coverNotice.update(this.options.tagsText);
		this.coverNotice.appendChild(this._coverNotice);
		this.coverNotice.setOpacity(startOpacity);
		
		this.coverNoticeReg = new EffectRegistry();
		this.thumbs.appendChild(this.coverNotice);
			
		this.tags.each(function(pair) {
			pair.value.element.observeIfNot("click", this._dismissCoverNotice);
		}.bind(this));
	},
	createCoverNotice: function() {
		if (!this.coverNotice) 
			this._createCoverNotice(0.0);
			
		this.coverNoticeReg.exclusive(new Effect.Parallel([
			new Effect.Opacity(this.coverNotice, {sync: true, from: this.coverNotice.getOpacity(), to: 1.0}),
			new Effect.BlindDown(this.coverNotice, {sync: true})
		], {duration: 0.5, delay: 0.5}));
		
		this.tags.each(function(pair) {
			pair.value.element.observeIfNot("click", this._dismissCoverNotice);
		}.bind(this));
	},
	dismissCoverNotice:  function() {
		this.tags.each(function(pair) {	
			pair.value.element.stopObserving("click", this._dismissCoverNotice);
		}.bind(this));
		
		if (!this.coverNotice)
			return;
		
		this.coverNoticeReg.exclusive(new Effect.Parallel([
			new Effect.Opacity(this.coverNotice, {sync: true, from: this.coverNotice.getOpacity(), to: 0.0}),
			new Effect.BlindUp(this.coverNotice, {sync: true})
		], {duration: 0.3}));
	},
	createThumbTopTip: function() {
		this.topTip = new Element("div");
		this.topTip.style.top = "-" + (this.options.calculatedTopMargin - this.options.tagsTopPadding) + "px";
		this.topTip.style.position = "absolute";
		this.topTip.style.left = "0px";
		this.topTip.style.width = this.options.pageWidth + "px";
		this.topTip.style.height = this.options.tagsTopTipSize + "px";
		this.topTip.style.lineHeight = this.options.tagsTopTipSize + "px";
		this.topTip.style.fontFamily = "Georgia, serif";
		this.topTip.style.textAlign = "center";
		this.topTip.style.fontStyle = "italic";
		this.topTip.setOpacity(0.5);
		this.topTip.update(this.options.tagsTopTip);
		this.thumbs.appendChild(this.topTip);
	},
	dismissTip: function() {
		this.tags.each(function(pair) {
			pair.value.element.stopObserving("click", this.dismissListener);
		}.bind(this));
		this.options.calculatedTopMargin -= this.options.tagsTopTipSize;
		new Effect.Morph(this.thumbs, {style: "margin-top: " + this.options.calculatedTopMargin + "px", duration: 2.0});
		new Effect.Opacity(this.topTip, {from: 0.5, to: 0.0, duration: 2.0});
	},
	setAnimsEnabled: function() {
		this.noAnim = false;
	},
	getThumbsHeight: function() {
		var agh = this.activeGrid.getHeight();
		var igh = this.inactiveGrid.getHeight();
		var tgh = this.tagGrid.getHeight();
		var fh = this.frame.getHeight();
		var paddings = this.options.tagsTopPadding + this.options.tagsBottomPadding + this.options.tagsTopTipSize + this.options.gridSpacing;
		return paddings + agh + igh + tgh + fh;
	},
	changeImage: function(uid, oldUid, operation) {
		if (!this.allowChanges)
			return;
	
		if (uid == oldUid) 
			return;
			
			
		if (operation != OrderedGrid.Iterator.Operation.DEACTIVATE) {
			this.activateImage(uid, oldUid);
			this.disableChangesFor(1250);
		} else {
			this.dock.addThese([
				new Effect.Morph(this.frame, {sync: true, style: "height: 0px;"}),
				new Effect.Morph(this.thumbs, {sync: true, style: "top: 0px;"})
			]);
			this.updateThumbnail(oldUid, false);
			this.dock.depart(1.0);
			setTimeout(function(empty, row) {
				row.elements.image_image.style.top = "720px";
				row.elements.image_title.style.left = "720px";
				row.elements.image_desc.style.left = "-720px";
				row.elements.image.style.bottom = "-" + row.heightWithCaption + "px";
				row.elements.image.zIndex = 1;
				PortfolioJS.content.setContentHeight(this.pageId, this.getThumbsHeight());
			}.bindAsEventListener(this, this.pages.get(oldUid)), 1500);
			PortfolioJS.content.page.scrollTo();
		}
	},
	enableChanges: function() {
		this.activeIterator.enable();
		this.allowChanges = true;
	},
	disableChanges: function() {
		this.activeIterator.disable();
		this.allowChanges = false;
	},
	disableChangesFor: function(time) {
		if (this.noAnim)
			return;
		PortfolioJS.content.disableFor(time);
		this.disableChanges();
		setTimeout(this.enableChanges.bind(this), time);
	},
	nextImage: function() {
		this.activeIterator.next();
	},
	previousImage: function() {
		this.activeIterator.previous();
	},
	closeImage: function() {
		this.activeIterator.deactivate();
	},
	showHelpers: function() {
		this.effects.helpers.exclusive(
			this.dock.addThese([
				new Effect.Morph(this.nextButton, {sync: true, style: "right: 0px"}),
				new Effect.Morph(this.previousButton, {sync: true, style: "left: 0px"}),
				new Effect.Morph(this.closeButton, {sync: true, style: "right: 0px"})
			]).depart(0.4)
		);
	},
	hideHelpers: function() {
		this.effects.helpers.exclusive(
			this.dock.addThese([
				new Effect.Morph(this.nextButton, {sync: true, style: "right: -60px"}),
				new Effect.Morph(this.previousButton, {sync: true, style: "left: -60px"}),
				new Effect.Morph(this.closeButton, {sync: true, style: "right: -60px"})
			]).depart(0.4)
		);
	},
	toggleHelpers: function(event, showing) {
		if (showing)
			this.showHelpers();
		else {
			if (this.frame.within(event.pointerX(), event.pointerY())) {
				this.frame.stopObserving("mouseover", this.helperOnListener);
				return;
			} else if (!this.frame.isObserving("mouseover", this.helperOnListener)) {
				this.frame.observe("mouseover", this.helperOnListener);
			}
			this.hideHelpers();
		}
	},
	active: function() {
		return this.pages.get(this.activeIterator.active());
	},
	activeUid: function() {
		return this.activeIterator.active();
	},
	showHint: function(event, uid) {
		this.justShowHint(uid);
	},
	justShowHint: function(uid) {
		var row = this.pages.get(uid);
		if (this.noAnim)
			row.elements.hint.style.bottom = "0px";
		else row.effects.exclusive(new Effect.Morph(row.elements.hint, {style: "bottom: 0px", duration: 0.2}));
	},
	hideHint: function(event, uid) {
		var row = this.pages.get(uid);
		
		if (row.elements.link.within(event.pointerX(), event.pointerY())) {
			row.elements.link.stopObserving("mouseover", row.listeners.mouseOverLink);
			return;
		} else if (!row.elements.link.isObserving("mouseover", row.listeners.mouseOverLink)) {
			row.elements.link.observe("mouseover", row.listeners.mouseOverLink);
		}
		
		this.justHideHint(uid);
	},
	disableHint: function(uid) {
		var row = this.pages.get(uid);
		if (row.elements.hintDisabled == true)
			return;
		
		row.elements.hintDisabled = true;
		row.elements.link.style.cursor = "default";
		if (row.elements.link.isObserving("mouseover", row.listeners.mouseOverLink))
			row.elements.link.stopObserving("mouseover", row.listeners.mouseOverLink);
		if (row.elements.link.isObserving("mouseout", row.listeners.mouseOutLink)) {
			row.elements.link.stopObserving("mouseout", row.listeners.mouseOutLink);
		}
	},
	enableHint: function(uid) {
		var row = this.pages.get(uid);
		if (row.elements.hintDisabled == false)
			return;
		
		row.elements.hintDisabled = false;
		row.elements.link.style.cursor = "pointer";
		if (!row.elements.link.isObserving("mouseover", row.listeners.mouseOverLink))
			row.elements.link.observe("mouseover", row.listeners.mouseOverLink);
		if (!row.elements.link.isObserving("mouseout", row.listeners.mouseOutLink)) {
			row.elements.link.observe("mouseout", row.listeners.mouseOutLink);
		}
		
	},
	justHideHint: function(uid) {
		var row = this.pages.get(uid);
		row.effects.exclusive(new Effect.Morph(row.elements.hint, {style: "bottom: -25px", duration: 0.2}));
	},
	activateImageListener: function(event, uid) {
		if (this.activeGrid.contains(uid))
			this.activeIterator.selectItem(uid);
		else {
			var row = this.pages.get(uid);
			this.setActiveTags(this.getActiveTags().concat(row.tags).uniq());
			this.activeIterator.selectItem(uid);
		}
	},
	toggleTag: function(event, tag) {
		if (!this.allowChanges)
			return;
		var row = this.tags.get(tag);
		if (row.active) {
			this.deactivateTag(tag);
		} else this.activateTag(tag);
		this.applyStateCodes();
	},
	__getActiveTags: function() {
		return this.__atags || (this.__atags = this.getActiveTags());
	},
	getActiveTags: function() {
		var activeTags = [];
		this.tags.each(function(pair, activeTags) {
			if (pair.value.active == true)
				activeTags.push(pair.key);
		}.bindAsEventListener(this, activeTags));
		return activeTags;
	},
	activateTag: function(tag) {
		var row = this.tags.get(tag);
		row.active = true;
		
		var activeTags = this.getActiveTags();
		activeTags.push(tag);
		
		this.setActiveTags(activeTags);
	},
	deactivateTag: function(tag) {
		var row = this.tags.get(tag);
		row.active = false;
		
		this.setActiveTags(this.getActiveTags().without(tag));
	},
	getStateCode: function(active, atags) {
		var activeTags = atags || this.__getActiveTags();
		var activeCode = (active ? (active == -1 ? "n" : active.id) : (this.activeUid() ? this.active().id : "n"));
		var activeName = (active ? (active == -1 ? "n" : active.caption) : (this.activeUid() ? this.active().caption : "n"));
		if (activeCode != "n") {
			var inTags = false;
			for (var i = 0; i < activeTags.length; ++i) {
				if (this.tags.get(activeTags[i]).refs.contains(active ? active.uid : this.activeUid()))
					inTags = true;
			}
			if (!inTags) {
				activeName = "n";
				activeCode = "n";
			}
		}
		activeName = activeName.replace(/[^a-zA-Z0-9]+/g, "_");
		var serialized = activeCode + "$" + (activeTags.length == 0 ? "a" : activeTags.join(","));
		return "#" + this.pageId + "/" + activeName + "$" + _e(serialized);
	},
	parsePreset: function(str) {
		if (!str)
			return undefined;
		str = _d(str);
		var img = str.match(/.*\$/);
		if (!img)
			return undefined;
		img = img[0];
		img = img.substring(0, img.length - 1);
		img = img == "n" ? undefined : parseInt(img);
		var _tags = str.match(/\$.*/);
		if (!_tags)
			return undefined;
		_tags = _tags[0];
		_tags = _tags.substring(1);
		if (_tags == "a")
			_tags = [];
		else _tags = _tags.split(",");
		return {image: img, tags: _tags};
	},
	applyStateCodes: function() {
		setTimeout(function() {
			this.pages.each(function(pair) {
				pair.value.elements.link.href = this.getStateCode(pair.value);
			}.bind(this));
			this.tags.each(function(pair) {
				if (pair.value.active)
					pair.value.element.href = this.getStateCode(undefined, this.getActiveTags().without(pair.key));
				else {
					var atags = this.getActiveTags();
					atags.push(pair.key);
					pair.value.element.href = this.getStateCode(undefined, atags);
				}
			}.bind(this));
			this.nextButton.href = this.getStateCode(this.pages.get(this.activeIterator.previewNextItem()));
			this.previousButton.href = this.getStateCode(this.pages.get(this.activeIterator.previewPreviousItem()));
			this.closeButton.href = this.getStateCode(-1);
		}.bind(this), 100);
	},
	highlightTag: function(event, tag) {
		var row = this.tags.get(tag);
		row.refs.each(function (uid) {
			if (this.activeUid() == uid)
				return;
			var prow = this.pages.get(uid);
			this.dock.add(
				prow.highlights.exclusive(
					new Effect.Opacity(prow.elements.overlay_glow, {sync: true, from: prow.elements.overlay_glow.getOpacity(), to: 1.0})
				)
			);
		}.bind(this));
		if (!row.refs.contains(this.activeUid()) && this.activeUid() != undefined) {
			var arow = this.active();
			this.dock.add(
				arow.highlights.exclusive(
					new Effect.Opacity(arow.elements.overlay_glow, {sync: true, from: arow.elements.overlay_glow.getOpacity(), to: 0.0})
				)
			);
		}
		this.dock.depart(0.2);
	},
	dehighlightTag: function(event, tag) {
		var row = this.tags.get(tag);
		row.refs.each(function (uid) {
			if (this.activeUid() == uid)
				return;
			var prow = this.pages.get(uid);
			this.dock.add(
				prow.highlights.exclusive(
					new Effect.Opacity(prow.elements.overlay_glow, {sync: true, from: prow.elements.overlay_glow.getOpacity(), to: 0.0})
				)
			);
		}.bind(this));
		if (!row.refs.contains(this.activeUid())  && this.activeUid() != undefined) {
			var arow = this.active();
			this.dock.add(
				arow.highlights.exclusive(
					new Effect.Opacity(arow.elements.overlay_glow, {sync: true, from: arow.elements.overlay_glow.getOpacity(), to: 0.5})
				)
			);
		}
		this.dock.depart(0.2);
	},
	setActiveTags: function(tags) {
		this.__atags = undefined;
		var tagRefs = [];
		for (var i = 0; i < tags.length; i++)
			tagRefs = tagRefs.concat(this.tags.get(tags[i]).refs);
		
		var activeUid = this.activeUid();
			
		tagRefs.uniq();
		this.activeGrid.clear();
		this.inactiveGrid.clear();
		
		this.pages.each(function(pair, selected) {
			if (selected.contains(pair.key)) {
				this.activeGrid.push(pair.key);
				this.enableHint(pair.key);
			} else {
				this.inactiveGrid.push(pair.key);
				this.disableHint(pair.key);
			}
		}.bindAsEventListener(this, tagRefs));
		
		this.dock.depart(0.5);
		
		if (!this.activeGrid.contains(activeUid))
			this.activeIterator.deactivate();
		
		
		this.tags.each(function(pair, selected) {
			if (selected.contains(pair.key)) {
				pair.value.active = true;
				pair.value.element.setOpacity(1.0);
			}
			else {
				pair.value.active = false;
				pair.value.element.setOpacity(0.5);
			}
		}.bindAsEventListener(this, tags));
		
		this.rearrange();
	},
	rearrange: function(uid) {
		this.activeGrid.options.items.each(function(uid) {
			var row = this.pages.get(uid);
			var position = this.activeGrid.getPosition(uid);
			this.dock.add(new Effect.Morph(row.elements.link, {sync: true, style: "top: " + position.y + "px; left: " + position.x + "px"}));
			this.dock.add(new Effect.Opacity(row.elements.link, {synd: true, from: row.elements.link.getOpacity(), to: this.options.disabledOpacity}));
		}.bind(this));
		if (this.activeGrid.options.items.length == 0)
			this.createCoverNotice();
		this.inactiveGrid.options.items.each(function(uid) {
			var row = this.pages.get(uid);
			var position = this.inactiveGrid.getPosition(uid);
			position.y += this.activeGrid.getHeight() + (this.activeGrid.options.items.length != 0 ? this.options.gridSpacing : this.options.unaryGridSpacing);
			this.dock.add(new Effect.Morph(row.elements.link, {sync: true, style: "top: " + position.y + "px; left: " + position.x + "px"}));
			this.dock.add(new Effect.Opacity(row.elements.link, {synd: true, from: row.elements.link.getOpacity(), to: this.options.relegatedOpacity}));
		}.bind(this));
		
		PortfolioJS.content.setContentHeight(this.pageId, this.getThumbsHeight());
		
		this.effects.tags.exclusive(this.dock.depart(1.0));
	},
	activateImage: function(uid, olduid) {
		
		if (olduid != undefined)
			this.updateThumbnail(olduid, false);
			
		this.updateThumbnail(uid, true);
		this.updateFrame(uid);
		this.updateImage(uid, olduid);
		
		this.effects.frame.exclusive(this.dock.depart(1.5));
		this.applyStateCodes();
	},
	updateThumbnail: function(uid, active) {
		var row = this.pages.get(uid);
		
		if (active) {
			if (row.elements.hint.style.top != "152px")
				this.justShowHint(uid);
			row.elements.link.stopObserving("mouseover", row.listeners.mouseOverLink);
			row.elements.link.stopObserving("mouseout", row.listeners.mouseOutLink);
		} else {
			if (!row.elements.hintDisabled) {
				row.elements.link.observe("mouseover", row.listeners.mouseOverLink);
				row.elements.link.observe("mouseout", row.listeners.mouseOutLink);
			}
			this.justHideHint(uid);
		}
		
		row.elements.overlay_glow.setOpacity(active ? 0.5 : 0.0);
		
		if (this.noAnim) Effect._Disable();
		
		this.dock.add(new Effect.Opacity(row.elements.link, {
			sync: true, 
			from: (active ? this.options.disabledOpacity : this.options.activeOpacity), 
			to: (active ? this.options.activeOpacity : this.options.disabledOpacity)
		}));
		this.highlightTagsFor(active ? uid : undefined);
		
		if (this.noAnim) Effect._Enable();
	},
	updateImage: function(newuid, olduid) {
		this.pages.each(function(pair, newuid, olduid) {
			if (!(pair.key == newuid || pair.key == olduid))
				pair.value.elements.image.hide();
			else pair.value.elements.image.show();
			if (pair.key == newuid)
				pair.value.elements.image_label_dummy.hide();
			pair.value.elements.image_label_dummy.hide();
			pair.value.elements.image.style.zIndex = pair.key == newuid ? 2 : 1;
		}.bindAsEventListener(this, newuid, olduid));
		
		if (olduid == undefined) {
			var row = this.pages.get(newuid);
			row.elements.image.style.bottom = "0px";
			row.elements.image_image.style.top = "0px";
			row.elements.image_title.style.left = "0px";
			row.elements.image_desc.style.left = "0px";
			row.elements.image.zIndex = 2;
			return;
		}
	
		var row_old = this.pages.get(olduid);
		var row_new = this.pages.get(newuid);
	
		row_new.elements.image.style.zIndex = 2;
		
		row_new.elements.image_title.style.left = "-720px";
		row_new.elements.image_desc.style.left = "720px";
		
		row_old.elements.image_title.style.left = "0px";
		row_old.elements.image_desc.style.left = "0px";
		
		var heightDifference = row_new.height - row_old.height;
		row_new.elements.image_image.style.top = "-" + (row_new.height - heightDifference) + "px";
		
		row_new.elements.image.style.bottom = "0px";
		
		row_old.elements.image_label_dummy.show();
		
		if (this.noAnim) Effect._Disable();
		
		this.dock.addThese([
		
			new Effect.Morph(row_old.elements.image_image, {sync: true, style: "top: " + row_old.height + "px"}),
			new Effect.Morph(row_new.elements.image_image, {sync: true, style: "top: 0px"}),
			
			new Effect.Morph(row_old.elements.image_title, {sync: true, style: "left: 720px"}),
			new Effect.Morph(row_new.elements.image_title, {sync: true, style: "left: 0px"}),
			
			new Effect.Morph(row_old.elements.image_desc, {sync: true, style: "left: -720px"}),
			new Effect.Morph(row_new.elements.image_desc, {sync: true, style: "left: 0px"})
			
		]);
		
		if (this.noAnim) Effect._Enable();
	},
	updateFrame: function(uid, noAnims) {
		var height = this.pages.get(uid).heightWithCaption;
		var difference = height - this.frame.getHeight();
		
		var currentHeight = PortfolioJS.content.container.getHeight();
		var incumbentHeight = this.getThumbsHeight() + difference;
		if (currentHeight > incumbentHeight)
			setTimeout(function(empty, incumbentHeight) {
				PortfolioJS.content.setContentHeight(this.pageId, incumbentHeight);
			}.bindAsEventListener(this, incumbentHeight), 1500);
		else PortfolioJS.content.setContentHeight(this.pageId, incumbentHeight);
		if (this.noAnim) {
			this.frame.style.height = height + "px";
			this.thumbs.style.top = height + "px";
		} else this.dock.addThese([
			new Effect.Morph(this.frame, {sync: true, style: "height: " + height + "px;"}),
			new Effect.Morph(this.thumbs, {sync: true, style: "top: " + height + "px;"})
		]);
	}
};

PortfolioJS = {
	page: {
		construct: function() {
			this.active = this.getActiveHashAnchor();
			this.cornerSet = new CornerSet("images/", ".png", 4);
			PortfolioJS.content.construct();
			PortfolioJS.nav.construct();
			PortfolioJS.title.construct();
			this.applyCorners();
			
			this.aodcallbacks = $H({
				photography: this.photography,
				development: this.development,
				design: this.design
			});
			
			this.contact.construct();
			this.activate(this.active);
		},
		applyCorners: function() {
			$$(".__rounded").each(function(element){
				this.cornerSet.applyTo(element);
			}.bind(this));
		},
		activate: function(pageName) {
			if (this.aodcallbacks.get(pageName)) {
				var page = this.aodcallbacks.get(pageName);
				page.aod.bind(page)();
			}
		},
		willActivateOnDemand: function(pageName) {
			return !(this.aodcallbacks.get(pageName) || {constructed:true}).constructed;
		},
		addActivateFinishedCallback: function(pageName, callback) {
			(this.aodcallbacks.get(pageName) || {}).activateFinished = callback;
		},
		defaultPage: "about",
		getActiveHashAnchor: function() {
			var hash = window.location.hash;
			var hval = hash == undefined || hash == "" ? this.defaultPage : hash.substring(1);
			this.hparams = "";
			if (hval.match(/\/.*/)) {
				this.hparams = hval.match(/\/.*/)[0].substring(1);
				hval = hval.match(/.*\//)[0];
				hval = hval.substring(0, hval.length - 1);
			}
			this.hval = hval;
			return hval;
		},
		photography: {
			aod: function() {
				if (!this.constructed)
					this.construct();
			},
			construct: function() {
				this.selectorSet = new JPGallerySelectorSet("jpgallery_img", 
															"jpgallery_img_img", 
															"jpgallery_img_title", 
															"jpgallery_img_desc", 
															"jpgallery_img_label_dummy", 
															"jpgallery_img_link", 
															"jpgallery_img_thumb", 
															"jpgallery_img_hint");
				this.gallery = new JPGallery("photography", this.photos, this.tags, this.selectorSet, {thumbsTopPadding: 80,
					tagsTopTip: "Click a tag to toggle the images relating to that tag. All tags are deselected by default.",
					thumbsTip: "Select a tag to activate and select related photos.",
					cornerSet: PortfolioJS.page.cornerSet});
				this.constructed = true;
				(this.activateFinished || function() {})();
			}
		},
		development: {
			aod: function() {
				if (!this.constructed)
					this.construct();
			},
			construct: function() {
				this.selectorSet = new JPGallerySelectorSet("jpgallery_img", 
															"jpgallery_img_img", 
															"jpgallery_img_title", 
															"jpgallery_img_desc", 
															"jpgallery_img_label_dummy", 
															"jpgallery_img_link", 
															"jpgallery_img_thumb", 
															"jpgallery_img_hint",
															"jpgallery_content_content",
															"jpgallery_content_image");
				this.gallery = new JPGallery("development", this.pages, this.tags, this.selectorSet, {thumbsTopPadding: 80,
					tagsTopTip: "Click a tag to toggle the pages relating to that tag. All tags are deselected by default.",
					thumbsTip: "Select a tag to activate and select related pages.",
					cornerSet: PortfolioJS.page.cornerSet,
					showNext: false,
					showPrevious: false,
					showClose: true});
				this.constructed = true;
				(this.activateFinished || function() {})();
			}
		},
		design: {
			aod: function() {
				if (!this.constructed)
					this.construct();
			},
			construct: function() {
				this.selectorSet = new JPGallerySelectorSet("jpgallery_img", 
															"jpgallery_img_img", 
															"jpgallery_img_title", 
															"jpgallery_img_desc", 
															"jpgallery_img_label_dummy", 
															"jpgallery_img_link", 
															"jpgallery_img_thumb", 
															"jpgallery_img_hint",
															"jpgallery_content_content",
															"jpgallery_content_image");
				this.gallery = new JPGallery("design", this.pages, this.tags, this.selectorSet, {thumbsTopPadding: 80,
					tagsTopTip: "Click a tag to toggle the pages relating to that tag. All tags are deselected by default.",
					thumbsTip: "Select a tag to activate and select related pages.",
					cornerSet: PortfolioJS.page.cornerSet,
					showNext: false,
					showPrevious: false,
					showClose: true});
				this.constructed = true;
				(this.activateFinished || function() {})();
			}
		},
		contact: {
			construct: function() {
				this.constructed = true;
				this.contentBlock = $("content_contact");
				this.tab = $("contact_emailBlindLabel");
				this.blind = $("contact_emailBlindContainer");
				this.showingBlind = false;
				
				this.emailForm.construct();
				
				this.boundToggleEventHandler = this.toggleBlind.bind(this);
				
				//PortfolioJS.content.setContentHeight("contact", this.contentBlock.getHeight());
				
				document.observe("jp10:emailDetachBlindLink", this.detach.bind(this));
				document.observe("jp10:emailAttachBlindLink", this.attach.bind(this));
				this.attach();
			},
			detach: function() {
				this.tab.stopObserving("click", this.boundToggleEventHandler);
				this.tab.style.cursor = "default";
			},
			attach: function() {
				this.tab.observe("click", this.boundToggleEventHandler);
				this.tab.style.cursor = "pointer";
			},
			toggleBlind: function() {
				if (this.showingBlind == true) {
					setTimeout(function() {
						PortfolioJS.content.decreaseCurrentHeight(332);
					}, 1000);
					new Effect.Morph(this.blind, {style: "height: 20px;"});
					this.tab.update("Click here to email me directly!");
				} else {
					PortfolioJS.content.increaseCurrentHeight(332);
					new Effect.Morph(this.blind, {style: "height: 352px;"});
					this.tab.update("Click here to hide this tab");
				}
				this.showingBlind = !this.showingBlind;
			},
			emailForm: {
				construct: function() {
					//this.form = $F("contact_emailForm");
					this.blind = $("contact_emailBlindContainer");
					this.formElement = $("contact_emailForm");
					
					this.nameField = $("contact_emailNameField");
					this.nameLabel = $("contact_emailBlind_nameArea").down(".contact_emailBlind_upperLabel");
					
					this.subjectField = $("contact_emailSubjectField");
					this.subjectLabel = $("contact_emailBlind_subjectArea").down(".contact_emailBlind_upperLabel");
					this.subjectConfirm = $("contact_emailConfirmSubject");
					
					this.fromEmailField = $("contact_emailEmailField");
					this.fromEmailLabel = $("contact_emailBlind_emailArea").down(".contact_emailBlind_upperLabel");
					this.fromEmailConfirm = $("contact_emailConfirmEmail");
					
					this.contentArea = $("contact_emailContentField");
					this.contentLabel = $("contact_emailBlind_contentArea").down(".contact_emailBlind_contentLabel");
					this.contentConfirm = $("contact_emailConfirmContent");
					
					this.submitButton = $("contact_emailSubmitButton");
					this.buttonArea = $("contact_emailBlind_buttonArea");
					
					this.confirmButton = $("contact_emailConfirmButton");
					this.backButton = $("contact_emailConfirmBack");
					
					this.formPage = $("contact_emailForm");
					this.confirmPage = $("contact_emailConfirmForm");
					
					this.confirmArea = $("contact_emailBlind_confirmArea");
					this.statusArea = $("contact_emailBlind_statusArea");
					
					this.nameField.observe("focus", this.upperFieldFocus.bind(this));
					this.nameField.observe("blur", this.upperFieldBlur.bind(this));
					this.nameField.observe("keyup", this.validate.bind(this));
					
					this.subjectField.observe("focus", this.upperFieldFocus.bind(this));
					this.subjectField.observe("blur", this.upperFieldBlur.bind(this));
					this.subjectField.observe("keyup", this.validate.bind(this));
					
					this.fromEmailField.observe("focus", this.upperFieldFocus.bind(this));
					this.fromEmailField.observe("blur", this.upperFieldBlur.bind(this));
					this.fromEmailField.observe("keyup", this.validate.bind(this));
					
					this.contentArea.observe("focus", this.contentAreaFocus.bind(this));
					this.contentArea.observe("blur", this.contentAreaBlur.bind(this));
					this.contentArea.observe("keyup", this.validate.bind(this));
					
					this.submitButton.observe("click", this.confirm.bind(this));
					
					this.backButton.observe("click", this.switchPageEvent.bindAsEventListener(this, this.confirmPage, this.formPage, false));
					this.confirmButton.observe("click", this.sendEmail.bind(this));
					
					this.currentStep = this.steps.invalid;
					this.validate();
				},
				
				steps: {
					invalid: 1,
					valid: 2
				},
				
				upperFieldFocus: function(event) {
					var field = event.element();
					new Effect.Morph(field.previous(".contact_emailBlind_upperLabel"), {style: "left: -120px;", duration: 0.4});
					new Effect.Morph(field, {style: "left: 20px; width: 700px;", duration: 0.4});
					new Effect.Morph(field.up(), {style: "background-color: #727b72", duration: 0.4});
					field.up().style.borderBottom = "2px solid #818988";
					field.up().style.height = "28px";
				},
				upperFieldBlur: function(event) {
					var field = event.element();
					new Effect.Morph(field.previous(".contact_emailBlind_upperLabel"), {style: "left: 0px;", duration: 0.4});
					new Effect.Morph(field, {style: "left: 120px; width: 600px;", duration: 0.4});
					new Effect.Morph(field.up(), {style: "background-color: #666e66", duration: 0.4});	
					field.up().style.borderBottom = "none";
					field.up().style.height = "30px";
				},
				contentAreaFocus: function(event) {
					new Effect.Morph(this.contentArea, {style: "background-color: #454845; border: #454845; border-left: 20px solid #454845; width: 670px", duration: 0.4});
					new Effect.Morph(this.contentLabel, {style: "left: -120px;", duration: 0.4});
				},
				contentAreaBlur: function(event) {
					new Effect.Morph(this.contentArea, {style: "background-color: #666e66; border: #666e66; border-left: 120px solid #666e66; width: 570px", duration: 0.4});
					new Effect.Morph(this.contentLabel, {style: "left: 0px;", duration: 0.4});
				},
				validate: function() {
					var isValid = this.textFieldsValid() && this.emailFieldValid();
					if (isValid && this.currentStep == this.steps.invalid) {
						this.submitButton.disabled = false;
						new Effect.Morph(this.buttonArea, {style: "background-color: #62de62;", duration: 0.4});
						this.currentStep = this.steps.valid;
					} else if (!isValid && this.currentStep == this.steps.valid) {
						this.submitButton.disabled = true;
						new Effect.Morph(this.buttonArea, {style: "background-color: #d26b6b;", duration: 0.4});
						this.currentStep = this.steps.invalid;
					} 
				},
				textFieldsValid: function() {
					return this.nameField.value != "" && this.contentArea.value != "";
				},
				emailFieldValid: function() {
					return this.fromEmailField.value.match("^[A-Za-z0-9](([_\\.\\-]?[a-zA-Z0-9]+)*)@([A-Za-z0-9]+)(([\\.\\-]?[a-zA-Z0-9]+)*)\\.([A-Za-z]{2,})$") != undefined;
				},
				confirm: function() {
					this.subjectConfirm.update(this.subjectField.value);
					this.fromEmailConfirm.update(this.fromEmailField.value);
					this.contentConfirm.innerHTML = this.contentArea.value.replace(/\r\n|\r|\n/g, "<br/>");
					
					this.confirmArea.setOpacity(1.0);
					
					this.switchPage(this.formPage, this.confirmPage, true);
				},
				switchPage: function(from, to, left) {
					var toLeft = left ? "left: -720px;" : "left: 720px;";
					new Effect.Morph(to, {style: "left: 0px;"});
					new Effect.Morph(from, {style: toLeft});
				},
				switchPageEvent: function(empty, from, to, left) {
					this.switchPage(from, to, left);
				},
				sendEmail: function() {
					
					new Effect.Fade(this.confirmArea, {from: 1.0, to: 0.0, duration: 0.5});
					new Effect.Morph(this.blind, {style: "height: 70px", duration: 1.0});
					setTimeout(function () {
						new Effect.Morph(this.statusArea, {style: "height: 50px", duration: 0.5});
					}.bind(this), 500);
					setTimeout(function () {
						new Ajax.Request("util/email.php", {
							method: "post",
							parameters: this.formPage.serialize(),
							onComplete: this.completeSending.bind(this)
						});
					}.bind(this), 1000);
				},
				completeSending: function() {
					new Effect.Morph(this.statusArea.down(".contact_emailStatusLoading"), {style: "height: 0px; top: -30px;", duration: 0.4});
					new Effect.Morph(this.statusArea.down(".contact_emailStatusSent"), {style: "height: 30px; top: 10px;", duration: 0.4});
					new Effect.Morph(this.statusArea, {style: "background-color: #62de62", duration: 0.4});
					document.fire("jp10:emailDetachBlindLink");
					setTimeout(function() {
						new Effect.Morph(this.statusArea, {style: "background-color: #454845", duration: 2.0});
					}.bind(this), 1000);
					setTimeout(function() {
						new Effect.Morph(this.statusArea.down(".contact_emailStatusSent"), {style: "top: -40px", duration: 0.4});
					}.bind(this), 2600);
					setTimeout(PortfolioJS.page.contact.toggleBlind.bind(PortfolioJS.page.contact), 2800);
					setTimeout(this.reset.bind(this), 3800);
				},
				reset: function() {
				
					this.currentStep = 1;
					
					this.nameField.value = "";
					this.subjectField.value = "";
					this.fromEmailField.value = "";
					this.contentArea.value = "";
					
					this.formPage.style.left = "0px";
					this.confirmPage.style.left = "720px";
					this.confirmArea.show();
					this.confirmArea.setOpacity(1.0);
					this.statusArea.style.height = "40px";
					this.statusArea.down(".contact_emailStatusLoading").style.height = "35px";
					this.statusArea.down(".contact_emailStatusLoading").style.top = "0px";
					this.statusArea.down(".contact_emailStatusSent").style.height = "0px";
					this.statusArea.down(".contact_emailStatusSent").style.top = "50px";
					this.statusArea.style.backgroundColor = "#a7a869";
					this.confirmArea.setOpacity
					
					this.buttonArea.style.backgroundColor = "#d26b6b";
					this.submitButton.disabled = true;
					
					setTimeout(function() {
						document.fire("jp10:emailAttachBlindLink");
					}, 1000);
				}
			}
		}
	},

	nav: {
		/* Attach the JS Nav animations and responses to the page */
		construct: function() {
			this.elements = $H();
			this.elements.set("about", $("navItem_about"));
			this.elements.set("development", $("navItem_development"));
			this.elements.set("design", $("navItem_design"));
			this.elements.set("photography", $("navItem_photography"));
			this.elements.set("contact", $("navItem_contact"));
			
			this.blocks = $H();
			this.blocks.set(this.elements.get("about").id, $("nav_aboutBlock"));
			this.blocks.set(this.elements.get("development").id, $("nav_developmentBlock"));
			this.blocks.set(this.elements.get("photography").id, $("nav_photographyBlock"));
			this.blocks.set(this.elements.get("design").id, $("nav_designBlock"));
			this.blocks.set(this.elements.get("contact").id, $("nav_contactBlock"));
			
			this.pageNames = $H();
			this.pageNames.set(this.elements.get("about").id, "about");
			this.pageNames.set(this.elements.get("development").id, "development");
			this.pageNames.set(this.elements.get("photography").id, "photography");
			this.pageNames.set(this.elements.get("design").id, "design");
			this.pageNames.set(this.elements.get("contact").id, "contact");
			
			//write the initial link order
			this.order = $H();
			this.order.set(this.elements.get("about").id, 1);
			this.order.set(this.elements.get("development").id, 2);
			this.order.set(this.elements.get("photography").id, 3);
			this.order.set(this.elements.get("design").id, 4);
			this.order.set(this.elements.get("contact").id, 5);
			
			this.activeItem = this.elements.get(PortfolioJS.page.active);
			this.initialActivation();
			
			//add actionlisteners
			this.elements.each(function(pair) {
				pair.value.observe("click", PortfolioJS.nav.activate.bindAsEventListener(PortfolioJS.nav, pair.key));
				pair.value.style.zIndex = 1;
			});
			this.blocks.values().each(function(block) {
				block.style.zIndex = 1;
			});
		},
		
		initialActivation: function() {
			this.updateOrder(this.activeItem);
			this.item.getSelectedBackground(this.activeItem).style.width = "250px";
			this.item.getText(this.activeItem, true).setOpacity(1.0);
			this.item.getText(this.activeItem, false).setOpacity(0.0);
			this.item.getTextContainer(this.activeItem).style.bottom = "14px";
			this.blocks.get(this.activeItem.id).style.top = "0px";
			var elements = this.getArray();
			for (var i = 0; i < elements.length; i++) {
				elements[i].style.top = this.calculateTopDistance(elements[i]);
			}
		},
		
		/* Get an array of the elements - not in order */
		
		getArray: function() {
			return this.elements.values();	
		},
		
		/* Activate the clicked nav item */ 
		activate: function(event, itemName) {
			//console.log(arguments);
			if (PortfolioJS.page.willActivateOnDemand(itemName)) {
				PortfolioJS.page.addActivateFinishedCallback(itemName, this.activate.bindAsEventListener(this, itemName));
				PortfolioJS.page.activate(itemName);
				return;
			}
		
			var item = this.getNavItemByName(itemName);
			
			//check if the item is already active
			if (this.getOrder(item) == 1)
				return;
			
			this.updateOrder(item);
			
			this.animateTextSelection(item, true);
			this.animateTextSelection(this.activeItem, false);
			this.animateTabSelection(item, true);
			this.animateTabSelection(this.activeItem, false);
			this.animateReshuffle(item);
			this.changeSubjectBlock(this.activeItem, item);
			
			PortfolioJS.title.activate(this.getNameByNavItem(item));
			PortfolioJS.content.activate(this.getNameByNavItem(item));
			
			this.activeItem = item;
		},
		
		/* Get a Nav Item by its name - i.e. 'contact' returns the navItem_contact element */
		getNavItemByName: function(itemName) {
			return this.elements.get(itemName);
		},
		
		getNameByNavItem: function(navItem) {
			return this.pageNames.get(navItem.id);
		},
		
		/* Update the stored order of the items to reflect the new order after
		   the user has made a new selection */
		updateOrder: function(active) {
			var absentIndex = this.getOrder(active);
			this.setOrder(active, 1);
			var elements = this.getArray();
			for (var i = 0; i < elements.length; i++) {
				if (elements[i] == active)
					continue;
				if (this.getOrder(elements[i]) < absentIndex)
					this.incrementOrder(elements[i]);
			}
		},
		
		/* Retrieve the current order of the given item */
		getOrder: function(item) {
			return parseInt(this.order.get(item.id));
		},
		
		/* Set the current order of the given item */
		setOrder: function(item, order) {
			this.order.set(item.id, order);
		},
		
		/* Increment the order of the given item by one */
		incrementOrder: function(item) {
			this.order.set(item.id, this.getOrder(item) + 1);
		},
		
		/* Calculate the distance from the top the item should be given the
		   current stored order */
		calculateTopDistance: function(item) {
			return (52 + (this.getOrder(item) * 52)) + "px";
		},
		
		/* Transitions to selected or deselected text */
		animateTextSelection: function(item, selected) {
			//fade text
			this.item.getText(item, !selected).style.zIndex = "1";
			this.item.getText(item, selected).style.zIndex = "2";
			new Effect.Opacity(this.item.getText(item, selected), {from: 0.0, to: 1.0, duration: 0.5});
			setTimeout(function(empty, selItem, deselItem) {
				deselItem.setOpacity(0.0);
				selItem.style.zIndex = "1";
			}.bindAsEventListener(this, this.item.getText(item, selected), this.item.getText(item, !selected)), 500);
			
			//move text
			if (selected == true)
				new Effect.Morph(this.item.getTextContainer(item), {style: "bottom: 14px;", duration: 0.5});
			else
				new Effect.Morph(this.item.getTextContainer(item), {style: "bottom: 2px;", duration: 0.5});
		},
		
		/* Morph a tab to correctly represent its state */
		animateTabSelection: function(item, selected) {
			new Effect.Morph(this.item.getSelectedBackground(item), {style: (selected ? "width: 250px;" : "width: 0px;")});
		},
		
		/* Reshuffle the navigation items to represent their new order */
		animateReshuffle: function(selectedItem) {
			selectedItem.style.zIndex = 2;
			var elements = this.getArray();
			for (var i = 0; i < elements.length; i++) {
				var newTop = this.calculateTopDistance(elements[i]);
				if (elements[i].style.top == newTop)
					continue;
				else new Effect.Morph(elements[i], {style: "top:" + newTop});
			}
			setTimeout(function (empty, item) {
				item.style.zIndex = 1;
			}.bindAsEventListener(this, selectedItem), 1000);
		},
		
		/* Switch the subject block image, rolling it over */
		changeSubjectBlock: function(fromItem, toItem) {
			var fromBlock = this.blocks.get(fromItem.id);
			var toBlock = this.blocks.get(toItem.id);
			
			new Effect.Morph(fromBlock, {style: "top: -50px;", duration: 1.0});
			new Effect.Morph(toBlock, {style: "top: 0px;", duration: 1.0});
			
			var reset = function(empty, fromBlock) {
				fromBlock.style.top = "50px";
			}.bindAsEventListener(this, fromBlock);
			
			//this is buggy in some browsers, gotta reset it a few times incase the morph lags
			setTimeout(reset, 1000);
			setTimeout(reset, 1100);
			setTimeout(reset, 1250);
		},
		
		/* Item utility functions */
		item: {
			/* Get a text object of the item */
			getText: function(item, selected) {
				return selected ? item.down(".navTextSelected") : item.down(".navTextDeselected");
			},
			
			/* Get the container around the text objects */
			getTextContainer: function(item) {
				return item.down(".navTextContainer, .navTextContainerSelected");
			},
			
			/* Get the selected background object */
			getSelectedBackground: function(item) {
				return item.down(".navItemSelected");
			}
		}
	},
	title: {
		construct: function() {
			this.distances = $H();
			this.distances.set("photography", "20px");
			this.distances.set("design", "-442px");
			this.distances.set("about", "-663px");
			this.distances.set("development", "-978px");
			this.distances.set("contact", "-1418px");
			
			this.widths = $H();
			this.widths.set("photography", "462px");
			this.widths.set("design", "221px");
			this.widths.set("about", "314px");
			this.widths.set("development", "440px");
			this.widths.set("contact", "296px");
			
			this.slider = $("contentHeadingBackground");
			this.mask = $("contentHeadingMask")
			this.selSlider = $("contentHeadingSelected");
			
			this.initialActivation();
		},
		initialActivation: function() {
			this.slider.style.left = this.distances.get(PortfolioJS.page.active);
			this.selSlider.style.left = this.distances.get(PortfolioJS.page.active);
			this.mask.style.width = this.widths.get(PortfolioJS.page.active);
		},
		activate: function(pageName) {
			new Effect.Morph(this.slider, {style: "left: " + this.distances.get(pageName)});
			new Effect.Morph(this.selSlider, {style: "left: " + this.distances.get(pageName)});
			new Effect.Morph(this.mask, {style: "width: " + this.widths.get(pageName)});
		}
	},
	content: {
		construct: function() {
			this.titleOffset = 120;
			this.elements = $H();
			this.elements.set("photography", $("content_photography"));
			this.elements.set("design", $("content_design"));
			this.elements.set("about", $("content_about"));
			this.elements.set("development", $("content_development"));
			this.elements.set("contact", $("content_contact"));
			
			this.page = $("pageContainer");
			this.container = $("contentArea");
			this.disabler = $("content_disabler");
			
			this.active = PortfolioJS.page.active;
			
			this.setRelativeHeights();
			
			this.initialActivation();
			
		},
		disable: function() {
			this.disabler.show();
		},
		enable: function() {
			this.disabler.hide();
		},
		disableFor: function(time) {
			this.disable();
			setTimeout(function() { this.enable() }.bind(this), time);
		},
		initialActivation: function() {
			this.elements.get(this.active).style.left = "0px";
			this.updateCurrentHeight(this.elements.get(this.active).getHeight());
		},
		setRelativeHeights: function() {
			/*var element = this.container.down(".contentArea_item");
			var cumulativeHeight = 0;
			this.container.style.height = this.elements.get(this.active).getHeight() + "px";
			do {
				console.log(cumulativeHeight + "px");
				element.style.top = cumulativeHeight + "px";
				cumulativeHeight -= element.getHeight();
			} while ((element = element.next(".contentArea_item")) != undefined);*/
		},
		updateCurrentHeight: function(amount) {
			//console.log("updated current height: " + amount);
			this.container.style.height = amount + "px";
			this.setMinHeight(amount);
		},
		increaseCurrentHeight: function(amount) {
			this.updateCurrentHeight(this.container.getHeight() + amount);
		},
		decreaseCurrentHeight: function(amount) {
			this.updateCurrentHeight(this.container.getHeight() - amount);
		},
		setMinHeight: function(amount) {
			amount += this.titleOffset;
			document.body.style.minHeight = amount + "px";
			document.body.style.height = amount + "px";
			if (amount > document.viewport.getHeight())
				this.page.style.height = amount + "px";
			else if (amount < document.viewport.getHeight())
				this.page.style.height = "100%";
		},
		activate: function(pageName) {
			PortfolioJS.page.activate(pageName);
			
			this.updateCurrentHeight(this.elements.get(pageName).getHeight());
			new Effect.Parallel([
				new Effect.Morph(this.elements.get(this.active), {sync: true, style: "left: 720px"}),
				new Effect.Morph(this.elements.get(pageName), {sync: true, style: "left: 0px"}),
				new Effect.Morph(this.container, {sync: true, style: "height: " + this.elements.get(pageName).getHeight() + "px"})
			]);
			
			var reset = function(empty, oldContent) {
				oldContent.style.left = "-720px";
			}.bindAsEventListener(this, this.elements.get(this.active));
			
			//this is buggy in some browsers, gotta reset it a few times incase the morph lags
			setTimeout(reset, 1000);
			setTimeout(reset, 1100);
			setTimeout(reset, 1250);
			
			this.active = pageName;
		},
		isActive: function(pageName) {
			return this.active == pageName;
		},
		setContentHeight: function(pageName, height) {
			var page = this.elements.get(pageName);
			var prevHeight = page.getHeight();
			
			page.style.height = height + "px";
			
			if (this.isActive(pageName)) {
				this.updateCurrentHeight(height);
			}
		},
		increaseContentHeight: function(pageName, height) {
			this.setContentHeight(pageName, this.elements.get(pageName).getHeight() + height);
		},
		decreaseContentHeight: function(pageName, height) {
			this.setContentHeight(pageName, this.elements.get(pageName).getHeight() - height);
		}
		
	},
	util: {
		load: function() {
			LOAD_RESTART();
			PortfolioJS.page.construct();
			LOAD_FINISH();
		}
	}
};

if (Prototype.Browser.MobileSafari)
	Effect._Disable();

document.observe("dom:loaded", PortfolioJS.util.load);
