var Papercut = {
	version:'0.1.9'
	,objectCount:0
};

Papercut.Base = Class.create({
	initialize: function(options) {
		this.setOptions(options);
	}
	,setOptions: function(options) {
		for (var i in options) {
			this[i] = options[i];
		}
	}
	
	,listeners:{}
	,on: function(eventType, fn) {
		if (this.listeners[eventType] == null) {
			this.listeners[eventType] = [];
		}
		this.listeners[eventType].push(fn);
	}
	
	,fire: function(eventType) {
		if (this.listeners[eventType] != null) {
			for (var i = 0; i < this.listeners[eventType].length; i++) {
				this.listeners[eventType][i](arguments);
			}
		}
	}
	,run: function(eventType) {
		this.fire(eventType);
	}
});

String.prototype.urlEncode = function() {
	return this.replace(/\+/g, "&#43;").replace(/\\/g, "&#92;");
};

String.prototype.trim = function() {
	var	str = this.replace(/^\s\s*/, ''),
		ws = /\s/,
		i = this.length;
	while (ws.test(this.charAt(--i)));
	return str.slice(0, i + 1);
};

String.prototype.safeTitle = function() {
	return this.toLowerCase().replace(/[^0-9a-z\-]/g, '-').replace(/\-+/g, '-').replace(/^\-|\-$/g, '').trim();
};

var dayName = new Array('Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday');
var monthName = new Array('January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December');

Date.prototype.makeDateIndex = function() {
	return this.getFullYear() + '/' + this.getMonth() + '/' + this.getDate();
};

Date.prototype.getYearMonth = function() {
	return this.getFullYear() + '/' + this.getMonth();
};

Date.prototype.getMonthName = function() {
	return ['January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December'][this.getMonth()];
};

Date.prototype.getDayName = function() {
	return ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday'][this.getDay()];
};

Papercut.addLink = function(href) {
	// make sure css isn't already in the page
	$$('link').each(function(el) {
		if (el.href == href) {
			return false;
		}
	});
	
	var newScript = new Element('link', {
		rel:'stylesheet',
		type:'text/css',
		href:href
	});
	
	$$('head')[0].appendChild(newScript);
	
	return newScript;
};

Papercut.addScript = function(src) {
	needToAdd = true;
	// make sure script isn't already in the page
	$$('script').each(function(el) {
		if (el.src.replace(src, '') != el.src) {
			needToAdd = false;
		}
	});
	if (needToAdd) {
		var newScript = new Element('script', {
			type:'text/javascript',
			src:src
		});
		$$('head')[0].appendChild(newScript);
		return newScript;
	}
	else {
		return false;
	}
};

Date.prototype.firstSunday = function() {
	var d = new Date();
	d.setTime(this.getTime()); 
	d.setFullYear(d.getFullYear(), d.getMonth(), 1);
	d.setFullYear(d.getFullYear(), d.getMonth(), 1 - d.getDay());
	return d;
};

Date.prototype.lastSaturday = function() {
	var d = new Date();
	d.setTime(this.getTime());
	d.setFullYear(d.getFullYear(), d.getMonth()+1, 0);
	d.setFullYear(d.getFullYear(), d.getMonth(), d.getDate() + 6 - d.getDay());
	return d;
};

Date.prototype.nextDay = function() {
	var d = new Date();
	d.setTime(this.getTime());
	d.setFullYear(d.getFullYear(), d.getMonth(), d.getDate() + 1);
	return d;
};

Date.prototype.getAmpm = function() {
	if (this.getHours() >= 12) {
		return 'pm';
	}
	else {
		return 'am';
	}
};

Date.prototype.getHours12 = function() {
	if (this.getHours() == 0) {
		return 12;
	}
	else if(this.getHours() > 12) {
		return this.getHours() - 12;
	}
	else {
		return this.getHours();
	}
};

Date.prototype.getFullSeconds = function() {
	if (this.getSeconds() < 10) {
		return '0' + this.getSeconds().toString();
	}
	else {
		return this.getSeconds();
	}
};

Date.prototype.getMinutes2 = function() {
	if (this.getMinutes() < 10) {
		return '0' + this.getMinutes().toString();
	}
	else {
		return this.getMinutes();
	}
};

Papercut.TimeSelector = Class.create({
	version: 0.1,
	container:false,
	textField:false,
	isHovering:false,
	currentTime:false,
	times:['12:00am','12:15am', '12:30am', '12:45am', '1:00am','1:15am', '1:30am', '1:45am', '2:00am','2:15am', '2:30am', '2:45am', '3:00am','3:15am', '3:30am', '3:45am', '4:00am','4:15am', '4:30am', '4:45am', '5:00am','5:15am', '5:30am', '5:45am', '6:00am','6:15am', '6:30am', '6:45am', '7:00am','7:15am', '7:30am', '7:45am', '8:00am','8:15am', '8:30am', '8:45am', '9:00am','9:15am', '9:30am', '9:45am', '10:00am','10:15am', '10:30am', '10:45am', '11:00am','11:15am', '11:30am', '11:45am', '12:00pm','12:15pm', '12:30pm', '12:45pm', '1:00pm','1:15pm', '1:30pm', '1:45pm', '2:00pm','2:15pm', '2:30pm', '2:45pm', '3:00pm','3:15pm', '3:30pm', '3:45pm', '4:00pm','4:15pm', '4:30pm', '4:45pm', '5:00pm','5:15pm', '5:30pm', '5:45pm', '6:00pm','6:15pm', '6:30pm', '6:45pm', '7:00pm','7:15pm', '7:30pm', '7:45pm', '8:00pm','8:15pm', '8:30pm', '8:45pm', '9:00pm','9:15pm', '9:30pm', '9:45pm', '10:00pm','10:15pm', '10:30pm', '10:45pm', '11:00pm','11:15pm', '11:30pm', '11:45pm'],
	slider:false,
	
	initialize: function(id, options) {
		this.setOptions();
		this.textField = $(id);
		this.createContainer();
		var d = new Date();
		var time = d.getHours12() + ':' + d.getMinutes2() + d.getAmpm(); 
		this.currentTime = time;
		
		this.textField.observe('focus', function() {
			this.show();
		}.bindAsEventListener(this));
		
		this.textField.observe('blur', function() {
			this.hide();
		}.bindAsEventListener(this));
		
		this.container.observe('mouseover', function() {
			this.isHovering = true;
		}.bindAsEventListener(this));
		
		this.container.observe('mouseout', function() {
			this.isHovering = false;
		}.bindAsEventListener(this));
		
		Event.observe(window, 'mousedown', function() {
			this.hide();
		}.bindAsEventListener(this));
	},
	createContainer:function() {
		var div = document.createElement('div');
		document.body.appendChild(div);
		this.container = $(div);
		this.container.innerHTML = 'time!';
		this.container.hide();
		this.container.addClassName('timeSelector');
		this.container.innerHTML = '<div id="timeTrack'+this.textField.id+'" class="timeTrack"><div id="timeHandle'+this.textField.id+'" class="timeHandle"></div></div>';
	},
	show: function() {
		if (this.container) {
			var p = this.textField.cumulativeOffset();
			if (p.top + this.textField.offsetHeight + this.container.offsetHeight > document.viewport.getHeight()) {
				
			}
			
			this.container.setStyle({
				left:(p.left + 1) + 'px',
				top:(p.top + this.textField.offsetHeight + 1) + 'px'
			});
			
			this.container.show();
			
			if (!this.slider) {
				this.slider = new Control.Slider('timeHandle'+this.textField.id, 'timeTrack'+this.textField.id, {
					range:$R(0,this.times.length-1),
					onSlide: function(v){
						this.toTime(v);
					}.bind(this),
					onChange: function(v){
						//this.toTime(v);
					}.bind(this)
				});
			}
			this.setTime();
		}
	},
	
	toTime:function(value) {
		var percent = parseFloat(value).toFixed(0);
		this.textField.value = this.times[percent];
	},
	
	setTime:function() {
		var minutes = this.calculateMinutes(this.textField.value);
		var index = (minutes/1440*this.times.length); // 1440 = minutes in a day
		this.slider.setValue(index);
	},
	hide: function(override) {
		if (override == null) {
			override = false;
		}
		if (override || (this.container && !this.isHovering)) {
			this.container.hide();
		}
	},
	
	setOptions: function(options) {
		for (var i in options) {
			this[i] = options[i];
		}
	},
	calculateMinutes: function(str) {
		var minutes = 0;
		var pieces = this.parseTime(str);
		minutes += pieces.minutes;
		if ((pieces.ampm == 'am' && pieces.hours < 12) || (pieces.ampm == 'pm' && pieces.hours == 12)) {
			// 1am-12pm
			minutes += pieces.hours*60;
		}
		else if (pieces.ampm == 'pm' && pieces.hours < 12) {
			// 1pm-11pm
			minutes += (pieces.hours+12) * 60;
		}
		return minutes;
	},
	parseTime: function(str) {
		if (this.textField.value.match(/\d\d?:\d\d[ap]m/)) {
			var pieces = this.textField.value.split(':');
			return {
				hours:parseInt(pieces[0]),
				minutes:parseInt(pieces[1].substr(0,2)),
				ampm:pieces[1].substr(2)
			};
		}
		return {};
	}
});

Papercut.DateSelector = Class.create({
	version: 0.1,
	container:false,
	textField:false,
	isHovering:false,
	currentDate:false,
	textboxDates:false,
	
	initialize: function(id, options) {
		this.setOptions();
		this.textField = $(id);
		this.createContainer();
		this.textboxDates = new Array();
		this.currentDate = new Date();
		
		this.textField.observe('focus', function() {
			this.showCalendar();
		}.bindAsEventListener(this));
		
		this.textField.observe('blur', function() {
			this.hideCalendar();
		}.bindAsEventListener(this));
		
		this.container.observe('mouseover', function() {
			this.isHovering = true;
		}.bindAsEventListener(this));
		
		this.container.observe('mouseout', function() {
			this.isHovering = false;
		}.bindAsEventListener(this));
		
		Event.observe(window, 'mousedown', function() {
			this.hideCalendar();
		}.bindAsEventListener(this));
	},
	
	createContainer:function() {
		var div = document.createElement('div');
		document.body.appendChild(div);
		this.container = $(div);
		this.container.hide();
		this.container.addClassName('dateSelector');
		var str = '';
		str += '<table cellpadding="0" cellspacing="0" width="140" class="calendarSmall">';
		str += '<tr><td>';
		str += '<table width="100%"><tr>';
		str += '<td width="10%" class="smallArrow leftArrow">&lt;&lt;</td>';
		str += '<td width="80%" class="monthName" align="center"></td>';
		str += '<td width="10%" class="smallArrow rightArrow">&gt;&gt;</td>';
		str += '</tr></table></td></tr>';
		str += '<tr><td><table width="100%" class="calendarDayNames">';
		str += '<tr><td width="20">S</td><td width="20">M</td><td width="20">T</td><td width="20">W</td><td width="20">T</td><td width="20">F</td><td width="20">S</td></tr></table></td></tr>';
		str += '<tr><td><div class="calendarDates">';
		str +='</div></td></tr></table>';
		this.container.innerHTML = str;
		
		this.previousArrows = this.container.select('.leftArrow')[0];
		this.monthName = this.container.select('.monthName')[0];
		this.nextArrows = this.container.select('.rightArrow')[0];
		this.calendarDates = this.container.select('.calendarDates')[0];
		
		this.previousArrows.observe('click', function() {
			this.currentDate.setFullYear(this.currentDate.getFullYear(), this.currentDate.getMonth()-1, 1);
			this.draw();
		}.bindAsEventListener(this));
		
		this.nextArrows.observe('click', function() {
			this.currentDate.setFullYear(this.currentDate.getFullYear(), this.currentDate.getMonth()+1, 1);
			this.draw();
		}.bindAsEventListener(this));
		
		
	},
	
	draw:function() {
		var str = '<table>';
		this.monthName.innerHTML = this.currentDate.getMonthName() + ' ' + this.currentDate.getFullYear();
		var firstSunday = this.currentDate.firstSunday();
		var lastSaturday = this.currentDate.lastSaturday();
		for (var i = firstSunday; i.getTime() < lastSaturday.getTime(); i = i.nextDay()) {
			if (i.getDay() == 0) {
				str += '<tr>';
			}
			str += '<td id="date-' + (i.getMonth() + 1) + '/' + i.getDate() + '/' + i.getFullYear().toString().substr(2) + '" width="20" class="smallDate">' + i.getDate() + '</td>';
		}
		str += '</table>';
		this.calendarDates.innerHTML = str;
		this.calendarDates.select('.smallDate').each(function(e) {
			e.onclick = function(e) {
				this.textField.value = e.id.substr(5);
				e.addClassName('active');
				this.hideCalendar(true);
			}.bind(this, e);
		}.bind(this));
		
		this.getDates();
		this.highlightDates();
	},
	
	getDates:function() {
		this.textboxDates = new Array();
		if (this.textField) {
			var dates = this.textField.value.split(',');
			for (var i = 0; i < dates.length; i++) {
				if (dates[i].match(/(\d{1,2}\/){2}\d{2,4}/)) {
					this.textboxDates.push(dates[i]);
				}
			}
		}
	},
	highlightDates:function() {
		for (var i = 0; i < this.textboxDates.length; i++) {
			if ($('date-' + this.textboxDates[i]) != null) {
				$('date-' + this.textboxDates[i]).addClassName('active');
			}
		}
	},
	showCalendar: function() {
		if (this.container) {
			this.setDate();
			this.draw();
			var p = this.textField.cumulativeOffset();
			if (p.top + this.textField.offsetHeight + this.container.offsetHeight > document.viewport.getHeight()) {
				
			}
			
			this.container.setStyle({
				left:(p.left + 1) + 'px',
				top:(p.top + this.textField.offsetHeight + 1) + 'px'
			});
			
			this.container.show();
		}
	},
	setDate:function() {
		if (this.textField.value.match(/(\d{1,2}\/){2}\d{2,4}/)) {
			var parts = this.textField.value.split('/');
			this.currentDate.setFullYear('20'+parts[2], parts[0]-1, parts[1]);
		}
	},
	hideCalendar: function(override) {
		if (override == null) {
			override = false;
		}
		if (override || (this.container && !this.isHovering)) {
			this.container.hide();
		}
	},
	
	setOptions: function(options) {
		for (var i in options) {
			this[i] = options[i];
		}
	}
});

Papercut.AjaxContent = Class.create({
	version: 0.1,
	
	initialize: function(url, pars, id) {
		if (typeof pars != 'string') {
			pars = Object.toQueryString(pars);
		}
		new Ajax.Request(url, {
			method: 'post',
			postBody: pars,
			onComplete: function(transport) {
				$(id).innerHTML = transport.responseText;
				this.fire('complete');
			}.bind(this)
		});
	}
});

Papercut.Ajax = Class.create({
	version: 0.1,
	url: false,
	response: false,
	onComplete: false,
	
	initialize: function(url, pars) {
		//this.setOptions(options);
		pars = Object.toQueryString(pars);
		new Ajax.Request(url, {
			method: 'post',
			postBody: pars,
			onComplete: function(transport) {
				this.response = transport.responseJSON;
				if (this.onComplete) {
					this.onComplete(this.reponse);
				}
			}.bind(this)
		});
	}
});


Papercut.FormSubmit = Class.create(Papercut.Base, {
	version: '0.1.2',
	redirect: false,
	waitTime: false,
	removeSubmit:true,
	timeoutSeconds:120,
	
	initialize: function(formId, options) {
		this.setOptions(options);
		
		this.form = $(formId);
		this.messageDiv = this.form.select('.message')[0];
		if (this.messageDiv == null) {
			this.messageDiv = new Element('div').addClassName('message');
			this.form.insert(this.messageDiv);
		}
		this.url = this.form.readAttribute('action');
		
		this.messageDiv.innerHTML = '<span class="loadingSpinner"></span><span class="messageText">Submitting...</span>';
		this.messageText = this.messageDiv.select('.messageText')[0];
		
		if (this.waitTime) {
			setTimeout(function() {
				this.sendRequest();
			}.bind(this), 0);
		}
		else {
			this.sendRequest();
		}
		
		this.statusTimeout = setTimeout(function() {
			this.messageText.innerHTML += 'Still working...';
		}.bind(this), 30000);
		this.unlockTimeout = setTimeout(function() {
			this.messageDiv.innerHTML = 'There was a problem processing your request. Please wait a few minutes and try submitting again.';
			this.request.transport.abort();
			Form.enable(this.form);
		}.bind(this), this.timeoutSeconds * 1000);
	},
	
	sendRequest: function() {
		this.pars = Form.serialize(this.form);
		Form.disable(this.form);
		this.request = new Ajax.Request(this.url, {
			method: 'post',
			postBody: this.pars,
			onLoading: function() { },
			onComplete: function(transport) {
				var status = transport.responseJSON;
				if (status.good) {
					this.messageDiv.innerHTML = status.messages;
					this.fire('success', status);
					if (this.removeSubmit) {
						this.form.select('input[type=submit]').each(Element.remove);
					}
					if (status.redirect) {
						location.href = status.redirect;
					}
				}
				else {
					this.messageDiv.innerHTML = status.errors;
				}
				Form.enable(this.form);
				clearTimeout(this.statusTimeout);
				clearTimeout(this.unlockTimeout);
			}.bind(this)
		});
	}
});

Papercut.TabContainer = Class.create(Papercut.Base, {
	version: 0.1,
	tabs:[],
	tabMap:{},
	items:[],
	activeTab:null,
	className:'papercutTabs',
	containerSize:'auto',
	containerHeight:false,
	
	initialize: function(containerId, options) {
		this.container = $(containerId);
		this.setOptions(options);
		this.container.addClassName('tabContainer');
		this.container.innerHTML = '<div class="papercutTabSection"><ul class="'+this.className+'"></ul></div><div class="papercutTabContentSection"></div>';
		this.tabSection = this.container.childNodes[0];
		this.tabContentSection = this.container.childNodes[1];
		this.tabList = this.tabSection.childNodes[0];
		if (this.containerHeight) {
			
			// set height or min-height and scroll setting
			if (this.containerSize == 'auto') {
				// treat the height as min-height
				this.tabContentSection.style.minHeight = this.containerHeight;
			}
			else if (this.containerSize == 'fixed') {
				this.tabContentSection.style.height = this.containerHeight;
				this.tabContentSection.style.overflow = 'auto';
			}
		}
		this.addInitialItems();
		this.showTab(0);
	},
	
	setOptions: function(options) {
		for (var i in options) {
			this[i] = options[i];
		}
	},
	
	addInitialItems: function() {
		for (var i = 0; i < this.items.length; i++) {
			this.addTab(this.items[i]);
		}
	},
	
	getTab:function(tab) {
		var tabObject;
		if (typeof tab == 'string') {
			tabObject = this.tabMap[tab];
		}
		else if (typeof tab == 'number') {
			tabObject = this.tabs[tab]; 
		}
		else if (typeof tab == 'object') {
			
		}
		return tabObject;
	},
	
	addTab: function(tabObject) {
		if (tabObject.length && typeof tabObject != 'string') {
			for (var i = 0; i < tabObject.length; i++) {
				this.addTab(tabObject[i]);
			}
			return;
		}
		if (typeof tabObject == 'string' || tabObject.innerHTML != null) {
			tabObject = $(tabObject);
			tabObject = {
				title:tabObject.title,
				contentEl:tabObject
			};
		}
		else if (tabObject.html != null) {
			tabObject.contentEl = document.createElement('div');
			tabObject.contentEl.innerHTML = tabObject.html;
		}
		else if (typeof tabObject.contentEl == 'string') {
			tabObject.contentEl = $(tabObject.contentEl);
		}
		if (this.getTab(tabObject.title) != null) {
			tabObject.contentEl = new Element('div').insert(tabObject.contentEl);
			this.getTab(tabObject.title).contentEl.appendChild(tabObject.contentEl);
		}
		else {
			tabObject.contentEl.style.display = 'none';
			this.tabContentSection.appendChild(tabObject.contentEl);
			if (tabObject.enabled == null) {
				tabObject.enabled = true;
			}
			if (tabObject.title != null) {
				newTab = new Element('li');
				newTab.innerHTML = '<a>' + tabObject.title + '</a>';
				this.tabList.appendChild(newTab);
				tabObject.index = this.tabs.length;
				tabObject.tab = $(newTab);
				this.tabs.push(tabObject);
				this.tabMap[tabObject.title] = tabObject;
				
				// set event handlers
				if (!tabObject.enabled) {
					$(tabObject.tab).addClassName('disabled');
				}
				
				tabObject.tab.observe('mouseover', function(tab) {
					tab.addClassName('hover');
				}.bind(this, tabObject.tab));
				
				tabObject.tab.observe('mouseout', function(tab) {
					tab.removeClassName('hover');
				}.bind(this, tabObject.tab));
				
				tabObject.tab.observe('click', function(tabObject) {
					this.showTab(tabObject.index);
				}.bind(this, tabObject));
			}
		}
		
		//fire event
		this.fire("add", tabObject);
		
		//remove the title
		tabObject.contentEl.title = '';
	},
	
	showTab: function(tab) {
		var tabObject = this.getTab(tab);
		if (tabObject != null && tabObject.enabled) {
			if (this.activeTab != null) {
				this.tabs[this.activeTab].tab.removeClassName('active');
				this.tabs[this.activeTab].contentEl.hide();
			}
			
			tabObject.tab.addClassName('active');
			this.activeTab = tabObject.index;
			tabObject.contentEl.show();
			this.fire("change", tabObject);
		}
	},
	
	enable:function(tabObject) {
		if (typeof tabObject != 'object') {
			tabObject = this.getTab(tabObject);
		}
		tabObject.enabled = true;
		tabObject.tab.removeClassName('disabled');
		this.fire("enable", tabObject);
	},
	
	disable:function(tabObject) {
		if (typeof tabObject != 'object') {
			tabObject = this.getTab(tabObject);
		}
		tabObject.enabled = false;
		tabObject.tab.addClassName('disabled');
		this.fire("disable", tabObject);
	},
	
	enableAll: function() {
		for (var i = 0; i < this.tabs.length; i++) {
			this.enable(this.tabs[i]);
		}
		this.fire("enableAll");
	},
	
	disableAll: function() {
		for (var i = 0; i < this.tabs.length; i++) {
			this.disable(this.tabs[i]);
		}
		this.fire("disableAll");
	}
});

Papercut.Scroller = Class.create({
	version: 0.1,
	scrollSpeed: .05,
	scrollDistance: 1,
	width:0,
	height:0,
	scrollTimer:null,
	direction:'down',
	overControls:false,
	overScroller:false,
	defaultDirection:'down',
	
	initialize: function(containerId, options) {
		this.container = $(containerId);
		this.setOptions(options);
		this.container.style.overflow = 'hidden';
		this.container.style.position = 'relative';
		
		if (this.width) {
			this.container.style.width = this.width;
		}
		if (this.height) {
			this.container.style.height = this.height;
		}
		
		this.container.observe('mouseover', function() {
			this.overScroller = true;
		}.bindAsEventListener(this));
		this.container.observe('mouseout', function() {
			this.overScroller = false;
		}.bindAsEventListener(this));
		
		this.container.innerHTML = '<div>'+this.container.innerHTML+'</div><div class="topControl"></div><div class="bottomControl"></div>';
		this.scrollingContent = $(this.container.childNodes[0]);
		this.scrollingContentHeight = this.scrollingContent.offsetHeight * -1;
		this.scrollingContent.setStyle({
			position:'absolute',
			top:'0'
		});
		
		// set listeners on controllers
		this.topControl = this.container.select('.topControl')[0];
		this.bottomControl = this.container.select('.bottomControl')[0];
		this.topControl.setStyle({
			position:'absolute',
			width:'100%',
			height:'20px',
			top:0
		});
		this.topControl.observe('mouseover', function() {
			this.direction = 'up';
			this.overControls = true;
		}.bindAsEventListener(this));
		this.bottomControl.setStyle({
			position:'absolute',
			width:'100%',
			height:'20px',
			top:this.container.offsetHeight - 20 + 'px'
		});
		this.bottomControl.observe('mouseover', function() {
			this.direction = 'down';
			this.overControls = true;
		}.bindAsEventListener(this));
		this.bottomControl.observe('mouseout', function() {
			this.direction = this.defaultDirection;
			this.overControls = false;
		}.bindAsEventListener(this));
		this.topControl.observe('mouseout', function() {
			this.direction = this.defaultDirection;
			this.overControls = false;
		}.bindAsEventListener(this));
		
		this.unpause();
	},
	setOptions: function(options) {
		for (var i in options) {
			this[i] = options[i];
		}
	},
	
	scroll: function() {
		switch (this.direction) {
			case 'down':
				newTop = parseInt(this.scrollingContent.style.top) - this.scrollDistance;
				if (this.overControls) {
					newTop = parseInt(this.scrollingContent.style.top) - (this.scrollDistance * 3);
				}
				if (!this.overScroller || this.overControls) {
					if (newTop > this.scrollingContentHeight) {
						this.scrollingContent.style.top = newTop + 'px';
					}
					else {
						this.scrollingContent.style.top = this.container.offsetHeight + 'px';
					}
				}
			break;
			case 'up':
				newTop = parseInt(this.scrollingContent.style.top) + this.scrollDistance;
				if (this.overControls) {
					newTop = parseInt(this.scrollingContent.style.top) + (this.scrollDistance * 3);
				}
				if (!this.overScroller || this.overControls) {
					if (newTop < this.scrollingContent.offsetHeight) {
						this.scrollingContent.style.top = newTop + 'px';
					}
					else {
						this.scrollingContent.style.top = 0 - this.scrollingContent.offsetHeight + 'px';
					}
				}
			break;
		}
	},
	
	unpause: function() {
		this.pause();
		this.scrollTimer = new PeriodicalExecuter(this.scroll.bind(this), this.scrollSpeed);
	},
	
	pause: function() {
		if (this.scrollTimer != null) {
			this.scrollTimer.stop();
			delete this.scrollTimer;
		}
	}
});

Papercut.SlideShows = new Array();

Papercut.SlideShow = Class.create(Papercut.Base, {
	version: 0.12,
	fadeTime: 1,
	waitTime: 5,
	directory: './',
	items: [],
	preloadedImages: [],
	totalItems: 0,
	currentIndex:0,
	interval:null,
	paused:false,
	stopped:false,
	repeat:true,
	pauseOnMouse:true,
	changeOnClick:true,
	direction:'forward',
	controllers:[],
	width:0,
	
	inTransition:false,
		
	initialize: function($super, containerId, options) {
		$super(options);
		
		this.container = $(containerId);
		this.container.addClassName('slideShowContainer');
		
		if (this.height) {
			this.container.style.height = this.height;
		}
		if (this.width) {
			this.container.style.width = this.width;
		}
		
		this.container.style.position = 'relative';
		this.container.style.overflow = 'hidden';
		this.container.observe('mouseover', function() {
			if (this.pauseOnMouse) {
				this.pause();
			}
		}.bindAsEventListener(this));
		this.container.observe('mouseout', function() {
			this.resume();
		}.bindAsEventListener(this));
		
		this.container.observe('mousedown', function(e) {
			if (this.changeOnClick) {
				if (e.isLeftClick()) {
					if (this.urls != null && this.urls[this.currentIndex] != null && this.urls[this.currentIndex].length) {
						location.href = this.urls[this.currentIndex];
					}
					else{
						this.next();
					}
				}
				else {
					this.previous();
				}
			}
		}.bindAsEventListener(this));
		
		for (var i = 0; i < this.items.length; i++) {
			if (typeof this.items[i] == 'string') {
				if (this.isImage(this.items[i])) {
					this.container.innerHTML += '<img style="display:none;" />';
					this.preloadImage(i, this.directory + this.items[i]);
				}
				else {
					$(this.items[i]).hide();
					this.container.appendChild($(this.items[i]));
				}
			}
			else if (typeof this.items[i] == 'object') {
				if (this.isImage(this.items[i].item)) {
					str = '';
					if (this.items[i].url) {
						str += '<a href="'+this.items[i].url+'" style="display:none;">';
					}
					str += '<img title="'+this.items[i].title+'" alt="'+this.items[i].title+'" border="0" />';
					this.preloadImage(i, this.directory + this.items[i].item);
					if (this.items[i].url) {
						str += '</a>';
					}
					this.container.innerHTML += str;
				}
				else {
					$(this.items[i]).hide();
					this.container.appendChild($(this.items[i]));
				}
			}
		}
		
		this.totalItems = this.container.childNodes.length;
		for (var i = 0; i < this.totalItems; i++) {
			this.items[i] = $(this.container.childNodes[i]);
			// set ids if they don't exist
			if (!this.items[i].id) {
				this.items[i].id = this.container.id + '-' + i;
			}
			this.items[i].style.position = 'absolute';
			this.items[i].style.display = 'none';
			this.items[i].style.left = 0;
			this.items[i].style.top = 0;
			if (this.customSize) {
				if (this.customSize[0]) {
					this.items[i].style.width = this.customSize[0] + 'px';
				}
				if (this.customSize[1]) {
					this.items[i].style.height = this.customSize[1] + 'px';
				}
			}
		}
		
		if (this.totalItems) {
			this.items[0].show();
			if (this.container.getStyle('height') == '0px') {
				this.container.style.height = this.items[0].offsetHeight + 'px';
			}
			this.start();
		}
		Papercut.SlideShows.push(this);
	},
	
	preloadImage: function(i, path) {
		var img = new Image(parseInt(this.width), parseInt(this.height));
		img = $(img);
		img.observe('load', function(i, path, img) {
			this.items[i].src = img.src;
			this.preloadedImages[i] = true;
		}.bind(this, i, path, img));
		if (i == 0) {
			img.src = path;
		}
		else {
			Event.observe(window, 'load', function(img, path) {
				img.src = path;
			}.bind(this, img, path));
		}
	},
	
	pause: function() {
		this.paused = true;
	},
	resume: function() {
		this.paused = false;
	},
	
	start: function() {
		clearInterval(this.interval);
		if (this.totalItems > 1) {
			this.stopped = false;
			this.interval = setInterval(function() {
				if (!this.paused && !this.stopped) {
					if (this.direction == 'forward') {
						this.next();
					}
					else if (this.direction == 'random') {
						this.random();
					}
					else {
						this.previous();
					}
				}
			}.bind(this), (this.waitTime + this.fadeTime) * 1000);
		}
	},
	
	showSlide: function(num) {
		num = (parseInt(num) % this.totalItems);
		if (num == this.currentIndex) return;
		clearInterval(this.interval);
		if (!this.inTransition) {
			this.inTransition = true;
			this.fire('beforeChange', num);
			new Effect.Fade(this.items[this.currentIndex].id, {
				duration: this.fadeTime,
				afterFinish: function() {
					this.currentIndex = num;
					this.inTransition = false;
					this.start();
					this.fire('afterChange')
				}.bind(this)
			});
			new Effect.Appear(this.items[num].id, {
				duration: this.fadeTime
			});
		}
		return num;
	},
	
	next: function() {
		var nextIndex = this.getNextIndex();
		if (nextIndex || this.repeat) {
			this.showSlide(nextIndex);
		}
		else {
			this.stop();
		}
	},
	previous: function() {
		var previousIndex = this.getPreviousIndex();
		this.showSlide(previousIndex);
	},
	random: function() {
		var randomIndex = this.getRandomIndex();
		this.showSlide(randomIndex);
	},
	
	stop: function() {
		this.stopped = true;
	},
	
	getNextIndex: function() {
		return ((this.currentIndex+1) % this.totalItems);
	},
	getPreviousIndex: function() {
		return ((this.totalItems + this.currentIndex - 1) % this.totalItems);
	},
	getRandomIndex: function() {
		randomIndex = this.currentIndex;
		if (this.totalItems > 1) {
			while (randomIndex == this.currentIndex) {
				randomIndex = Math.floor(Math.random() * (this.totalItems));
			}
		}
		return randomIndex;
	},
	
	isImage: function(str) {
		return str.match(/\.\w{3,4}$/);
	},
	
	addController: function(controller) {
		this.controllers.push(controller);
		controller.slideShow = this;
		controller.display();
	}
});

Papercut.SlideShowController = Class.create(Papercut.Base, {
	version: 0.1,
	container:false,
	slideShow:false,
	type:'number',
	generateList:true,
	oneAtATime:false,
	
	initialize: function(containerId, options) {
		this.setOptions(options);
		this.container = $(containerId);
	},
	
	display: function() {
		if (this.generateList) {
			switch(this.type) {
				case 'title':
					this.initFrom('titles');
				break;
				
				case 'caption':
					this.initFrom('captions');
				break;
				
				case 'numbersWithNav':
					this.initNumbers(true);
				break;
				
				default:
					this.initNumbers(false);
				break;
			}
		}
		else {
			this.list = this.container.select('ul');
			if (this.list.length) this.list = this.list[0];
		}
		if (this.oneAtATime) {
			this.container.select('li').each(function(el) {el.hide();});
			this.container.select('li')[0].show();
		}
		this.initActions();
	},
	initActions: function() {
		this.slideShow.on('beforeChange', function(a) {
			var index = a[1];
			if (this.oneAtATime) {
				this.container.select('li').each(function(el) {
					el.hide();
				});
				this.container.select('li')[index].show();
			}
			this.list.select('.active').each(function(el) {
				el.removeClassName('active');
			});
			this.list.select('a')[index].addClassName('active');
		}.bind(this));
		
		var eachA = this.list.select('a');
		for (var i = 0; i < eachA.length; i++) {
			eachA[i].observe('click', function(i) {
				this.slideShow.showSlide(i);
			}.bind(this, i));
		}
	},
	initNumbers: function(withArrows) {
		var numItems = this.slideShow.items.length;
		this.list = new Element('ul', {
			className:'slideShowNav'
		});
		for (var i = 0; i < numItems; i++) {
			this.list.insert(new Element('li').insert(new Element('a').insert((i+1))));
		}
		this.container.insert(this.list);
		this.container.select('a')[0].addClassName('active');
		
		this.navWrap = new Element('div');
		this.list.wrap(this.navWrap);
		this.navWrap.addClassName('numberNavWrap');
		
		if (withArrows) {
			this.nextArrow = (new Element('a')).insert('Next');
			this.navWrap.insert(this.nextArrow);
			this.nextArrow.observe('click', this.slideShow.next.bind(this.slideShow));
		}
	},
	initFrom: function(property) {
		var numItems = this.slideShow.items.length;
		this.list = new Element('ul', {
			className:'slideShowNav'
		});
		for (var i = 0; i < numItems; i++) {
			this.list.insert(new Element('li').insert(new Element('a').insert(this.slideShow[property][i])));
		}
		this.container.insert(this.list);
	}
});

Papercut.Menu = Class.create();
Papercut.Menu.defaultOptions = {
	displayStyle:'popup',
	click:function() {return true; },
	styleClass:'default',
	hideTime:1000,
	direction:'down',
	subPosition:'right',
	subAlign:'top',
	divider:false,
	dividerHTML:'',
	reverseTop:false,
	numLevels:-1,
	onlyShowActiveBranch:false,
	showMoreArrow:false,
	showIcon:false
};

Papercut.Menu.hideTimer = null;
Papercut.Menu.currentZ = 1;
Papercut.Menu.menus = [];

var thisPage = getThisPage(location.href);
function getComparePage(page) {
	page = page.toLowerCase();
	if (page.charAt(page.length-1) == '/') {
		page = page.substr(0, page.length-1);
	}
	return page;
}
function getThisPage(page) {
	page = page.toLowerCase();
	page = page.replace(/#.*/, '');
	if (page.charAt(page.length-1) == '/') {
		page = page.substr(0, page.length-1);
	}
	return page;
}

Papercut.Menu.prototype = {
	version: 0.2,
	initialize: function(menuId) {
		this.menu = $(menuId);
		this.options = Object.extend(Object.clone(Papercut.Menu.defaultOptions), arguments[1] || {});
		if (this.menu != null) {
			this.apply();
		}
		Papercut.Menu.menus.push(this);
	},
	apply: function() {
		// hide the menu
		this.menu.style.visibility = 'hidden';
		this.menu.style.display = '';
		
		// apply the style
		this.menu.addClassName(this.options.styleClass);
		/*if (!this.menu.getStyle('height')) {
			this.menu.style.height = this.menu.offsetHeight + 'px';
		}
		*/
		
		switch(this.options.displayStyle) {
			case 'popup':
				this.initPopups();
			break;
			case 'tree':
				this.initTree();
			break;
			default:
				
			break;
		}
		
		this.reverseTop();
		// set active page
		var anchors = this.menu.select('a');
		
		// add title class names to links
		for (var i = 0; i < anchors.length; i++) {
			$(anchors[i].parentNode).addClassName(anchors[i].innerHTML.safeTitle());
			anchors[i].addClassName(anchors[i].innerHTML.safeTitle());
		}
		
		// show more arrow
		if (this.options.showMoreArrow) {
			for (var i = 0; i < anchors.length; i++) {
				if (anchors[i].siblings().length) {
					anchors[i].addClassName('hasChildren');
					var moreArrowSpan = new Element('span', {className:'moreArrow'});
					anchors[i].insert(moreArrowSpan);
				}
			}	
		}
		
		// show icon
		if (this.options.showIcon) {
			for (var i = 0; i < anchors.length; i++) {
				var menuIconSpan = new Element('span', {className:'menuIcon'});
				menuIconSpan.addClassName('menuIcon');
				menuIconSpan.addClassName('pngFix');
				anchors[i].insert(menuIconSpan);
			}	
		}
		
		for (i = 0; i < anchors.length; i++) {
			if (getComparePage(anchors[i].href) == thisPage) {
				$(anchors[i]).addClassName('active');
				if (this.options.displayStyle == 'tree') {
					subContainers = anchors[i].parentNode.select('div');
					if (subContainers.length) {
						Element.show(subContainers[0]);
					}
				}
				for (j = this.getLevel(anchors[i].parentNode), parentAnchor = anchors[i]; j > 0; j--) {
					parentAnchor = $(parentAnchor.parentNode.parentNode.parentNode.parentNode.select('a')[0]);
					parentAnchor.addClassName('active');
					
					if (this.options.displayStyle == 'tree') {
						subContainers = parentAnchor.parentNode.select('div');
						if (subContainers.length) {
							Element.show(subContainers[0]);
						}
					}
				}
			}
		}
		// set last class names
		this.menu.select('li:last-child > a').each(function(el){
			el.addClassName('last');
		});
		this.initDividers();
		this.showActiveBranch();
		
		// show the menu
		this.menu.style.visibility = 'visible';
	},
	
	showActiveBranch: function() {
		if (this.options.onlyShowActiveBranch) {
			actives = this.menu.select('.active');
			if (actives.length) {
				actives[0].hide();
				$(actives[0].parentNode).siblings().each(function(el) {el.hide();});
			}
		}
	},
	
	reverseTop: function() {
		if (this.options.reverseTop) {
			var topEls = $(this.menu.childNodes[0]).childElements();
			var numTop = topEls.length;
			for (var i = 0; i < numTop-1; i++) {
				topEls[numTop-1].insert({after:topEls[i]});
			}
		}
	},
	
	initDividers: function() {
		if (this.options.divider) {
			// get top level lis
			var topLevels = $(this.menu.childNodes[0]).childElements();
			for (var i = 0; i < topLevels.length-1; i++) {
				topLevels[i].insert({after:'<li class="divider">'+this.options.dividerHTML+'</li>'});
			}
		}
	},
	
	initTree: function() {
		// set mouse click for each anchor
		var anchors = this.menu.select('a');
		for (var i = 0; i < anchors.length; i++) {
			anchors[i].onclick = function(e, anchor, clickHandler) {
				if (anchor.href.match(/#$/)) {
					subLists = anchor.parentNode.select('div');
					if (subLists.length) {
						Element.toggle(subLists[0]);
					}
					return false;
				}
				return clickHandler(anchor);
			}.bindAsEventListener(this, anchors[i], this.options.click);
		}
		
		// set mouseover for each li
		/*var liArray = this.menu.select('li');
		for (var i = 0; i < liArray.length; i++) {
			liArray[i].onmouseover = function(e, hoveredLi) {
				this.setActiveLi(hoveredLi);
			}.bindAsEventListener(this, liArray[i]);
		}*/
	},
	
	positionSubList: function(subList) {
		var linkHeight = subList.parentNode.offsetHeight;
		if ($(subList.parentNode.parentNode.parentNode).hasClassName(this.options.styleClass)) {
			switch (this.options.direction) {
				case 'down':
					subList.style.top = linkHeight.toString() + 'px';
					subList.style.left = '0';
				break;
				case 'up':
					subList.style.visibility = 'hidden';
					subList.style.display = '';
					subList.style.top = 0 - parseInt(subList.offsetHeight) + 'px';
					subList.style.display = 'none';
					subList.style.visibility = 'visible';
					subList.style.left = '0';
				break;
				case 'right':
					subList.style.left = subList.parentNode.parentNode.offsetWidth + 'px';
					subList.style.top = '0';
				break;
				case 'left':
					subList.style.left = (0 - subList.parentNode.parentNode.offsetWidth) + 'px';
					subList.style.top = '0';
				break;
				
			}
		}
		else {
			switch (this.options.subPosition) {
				case 'right':
					subList.style.left = subList.parentNode.parentNode.offsetWidth + 'px';
				break;
				case 'left':
					subList.style.visibility = 'hidden';
					subList.style.display = '';
					subList.style.left = 0 - parseInt(subList.offsetWidth) + 'px';
					subList.style.display = 'none';
					subList.style.visibility = 'visible';
				break;
			}
			switch (this.options.subAlign) {
				case 'top':
					subList.style.top = '0';
				break;
				case 'bottom':
					subList.style.visibility = 'hidden';
					subList.style.display = '';
					subList.style.top = linkHeight - parseInt(subList.offsetHeight) + 'px';
					subList.style.display = 'none';
					subList.style.visibility = 'visible';
				break;
			}
		}
	},
	
	activeLis:[],
	zIndex:10000,
	
	getLevel: function(hoveredLi) {
		level = 0;
		while(!$(hoveredLi.parentNode.parentNode).hasClassName(this.options.styleClass)) {
			hoveredLi = hoveredLi.parentNode.parentNode.parentNode;
			level++;
		}
		return level;
	},
	
	showMenuTimer:null,
	
	showPopup: function(hoveredLi) {
		// check if li has a sublist
		subList = false;
		subLists = hoveredLi.select('div');
		if (subLists.length) {
			subList = subLists[0];
			if (subList.style.display == 'none') {
				level = this.getLevel(hoveredLi);
				if (this.options.numLevels == -1 || level < this.options.numLevels) {
					this.positionSubList(subList);
					subList.style.zIndex = this.zIndex++;
					subList.style.position = 'absolute';
					Element.show(subList);
				}
			}
		}
	},
		
	showActives: function() {
		str = '';
		for (var i = 0; i < this.activeLis.length; i++) {
			str += this.activeLis[i].childNodes[0].innerHTML + ', ';
		}
		return str;
	},
	
	setActiveLi: function(hoveredLi) {
		hoveredLi.style.position = 'relative';
		level = this.getLevel(hoveredLi);
		
		// check for sublist to know whether or not to hide
		subLists = hoveredLi.select('div');
		if (subLists.length) {
			subList = subLists[0];
			if (subList.style.display == 'none') {
				this.hideOtherPopups(level);
				this.activeLis[level] = hoveredLi;
			}
		}
		else {
			this.hideOtherPopups(level);
			this.activeLis[level] = hoveredLi;
		}
	},
	
	hideOtherPopups: function(level) {
		for (var i = this.activeLis.length-1; i >= level; i--) {
			subLists = this.activeLis[i].select('div');
			if (subLists.length) {
				subList = subLists[0];
				Element.hide(subList);
			}
		}
		this.showActives();
	},
	
	hideMenuTimer:null,
	
	hideAll:function() {
		for (var i = 0; i < this.activeLis.length; i++) {
			subLists = this.activeLis[i].select('div');
			if (subLists.length) {
				Element.hide(subLists[0]);
			}
		}
		this.activeLis = [];
	},
	
	initPopups: function() {
		// hide inner list so it doesn't take up space
		this.menu.childNodes[0].style.height = '1px';
		this.menu.childNodes[0].style.overflow = 'hidden';
		
		var anchors = this.menu.select('a');
		
		// set mouse click for each anchor
		for (var i = 0; i < anchors.length; i++) {
			anchors[i].onclick = function(e, anchor, clickHandler) {
				return clickHandler(anchor);
			}.bindAsEventListener(this, anchors[i], this.options.click);
		}
		
		// set mouseover for each li
		var liArray = this.menu.select('li');
		for (var i = 0; i < liArray.length; i++) {
			liArray[i].onmouseover = function(e, hoveredLi) {
				this.setActiveLi(hoveredLi);
				this.showPopup(hoveredLi);
				
				/*clearTimeout(this.showMenuTimer);
				this.showMenuTimer = setTimeout(function() {
					
				}.bind(this), 100);*/
			}.bindAsEventListener(this, liArray[i]);
		}
		
		this.menu.onmouseover = function() {
			clearTimeout(this.hideMenuTimer);
		}.bindAsEventListener(this);
		
		this.menu.onmouseout = function(a, b) {
			this.hideMenuTimer = setTimeout(function() {
				this.hideAll();
			}.bind(this), 1000);
		}.bindAsEventListener(this);
		
		this.menu.style.overflow = 'visible';
		
		// reset to a visible height
		this.menu.childNodes[0].style.height = 'auto';
		this.menu.childNodes[0].style.overflow = 'visible';
	}
};

Papercut.ProgressBar = Class.create(Papercut.Base, {
	parent:false
	,pbarContainer:false
	,pbar:false
	,height:'12px'
	,width:'100%'
	,percent:0
	,className:false
	
	,initialize: function(parent, options) {
		this.setOptions(options);
		this.parent = $(parent);
		
		this.pbarContainer = new Element('div', {
			'class':'pbarContainer'
		}).insert(new Element('div', {
			'class':'pbar'
		}));
		
		this.pbarContainer.insert(new Element('div', {
			'class':'pbarText'
		}));
		
		
		this.pbar = $(this.pbarContainer.childNodes[0]);
		this.pbarText = $(this.pbarContainer.childNodes[1]);
		
		if (this.className) {
			this.pbarContainer.addClassName(this.className);
		}
		
		this.parent.insert(this.pbarContainer);
	}
	
	,setValue: function(value) {
		if (value <= 1) {
			value *= 100;
		}
		this.percent = value;
		this.pbar.setStyle({width:this.percent + '%'});
	}
	,setText: function(value){
		this.pbarText.innerHTML = value;
	}
	,getValue: function() {
		return this.percent/100;
	}
	,slideTo: function(value, duration) {
		if (value <= 1) {
			value *= 100;
		}
		if (duration == null) {
			duration = .5;
		}
		if (value > 100) {
			value = 100;
		}
		else if (value < 0) {
			value = 0;
		}
		this.percent = value;
		new Effect.Morph(this.pbar, {
			style:'width:'+value+'%',
			duration:duration
		});
	}
});

Papercut.Date = function(format, timestamp) {
    // Format a local date/time  
    // 
    // version: 905.3122
    // discuss at: http://phpjs.org/functions/date
    // +   original by: Carlos R. L. Rodrigues (http://www.jsfromhell.com)
    // +      parts by: Peter-Paul Koch (http://www.quirksmode.org/js/beat.html)
    // +   improved by: Kevin van Zonneveld (http://kevin.vanzonneveld.net)
    // +   improved by: MeEtc (http://yass.meetcweb.com)
    // +   improved by: Brad Touesnard
    // +   improved by: Tim Wiel
    // +   improved by: Bryan Elliott
    // +   improved by: Brett Zamir (http://brett-zamir.me)
    // +   improved by: David Randall
    // +      input by: Brett Zamir (http://brett-zamir.me)
    // +   bugfixed by: Kevin van Zonneveld (http://kevin.vanzonneveld.net)
    // +   improved by: Brett Zamir (http://brett-zamir.me)
    // +   improved by: Brett Zamir (http://brett-zamir.me)
    // +   derived from: gettimeofday
    // %        note 1: Uses global: php_js to store the default timezone
    // *     example 1: date('H:m:s \\m \\i\\s \\m\\o\\n\\t\\h', 1062402400);
    // *     returns 1: '09:09:40 m is month'
    // *     example 2: date('F j, Y, g:i a', 1062462400);
    // *     returns 2: 'September 2, 2003, 2:26 am'
    // *     example 3: date('Y W o', 1062462400);
    // *     returns 3: '2003 36 2003'
    // *     example 4: x = date('Y m d', (new Date()).getTime()/1000); // 2009 01 09
    // *     example 4: (x+'').length == 10
    // *     returns 4: true
    var jsdate=(
        (typeof(timestamp) == 'undefined') ? new Date() : // Not provided
        (typeof(timestamp) == 'number') ? new Date(timestamp*1000) : // UNIX timestamp
        new Date(timestamp) // Javascript Date()
    ); // , tal=[]
    var pad = function(n, c){
        if( (n = n + "").length < c ) {
            return new Array(++c - n.length).join("0") + n;
        } else {
            return n;
        }
    };
    var _dst = function (t) {
        // Calculate Daylight Saving Time (derived from gettimeofday() code)
        var dst=0;
        var jan1 = new Date(t.getFullYear(), 0, 1, 0, 0, 0, 0);  // jan 1st
        var june1 = new Date(t.getFullYear(), 6, 1, 0, 0, 0, 0); // june 1st
        var temp = jan1.toUTCString();
        var jan2 = new Date(temp.slice(0, temp.lastIndexOf(' ')-1));
        temp = june1.toUTCString();
        var june2 = new Date(temp.slice(0, temp.lastIndexOf(' ')-1));
        var std_time_offset = (jan1 - jan2) / (1000 * 60 * 60);
        var daylight_time_offset = (june1 - june2) / (1000 * 60 * 60);

        if (std_time_offset === daylight_time_offset) {
            dst = 0; // daylight savings time is NOT observed
        }
        else {
            // positive is southern, negative is northern hemisphere
            var hemisphere = std_time_offset - daylight_time_offset;
            if (hemisphere >= 0) {
                std_time_offset = daylight_time_offset;
            }
            dst = 1; // daylight savings time is observed
        }
        return dst;
    };
    var ret = '';
    var txt_weekdays = ["Sunday","Monday","Tuesday","Wednesday",
        "Thursday","Friday","Saturday"];
    var txt_ordin = {1:"st",2:"nd",3:"rd",21:"st",22:"nd",23:"rd",31:"st"};
    var txt_months =  ["", "January", "February", "March", "April",
        "May", "June", "July", "August", "September", "October", "November",
        "December"];

    var f = {
        // Day
            d: function(){
                return pad(f.j(), 2);
            },
            D: function(){
                var t = f.l();
                return t.substr(0,3);
            },
            j: function(){
                return jsdate.getDate();
            },
            l: function(){
                return txt_weekdays[f.w()];
            },
            N: function(){
                return f.w() + 1;
            },
            S: function(){
                return txt_ordin[f.j()] ? txt_ordin[f.j()] : 'th';
            },
            w: function(){
                return jsdate.getDay();
            },
            z: function(){
                return (jsdate - new Date(jsdate.getFullYear() + "/1/1")) / 864e5 >> 0;
            },

        // Week
            W: function(){
                var a = f.z(), b = 364 + f.L() - a;
                var nd2, nd = (new Date(jsdate.getFullYear() + "/1/1").getDay() || 7) - 1;

                if(b <= 2 && ((jsdate.getDay() || 7) - 1) <= 2 - b){
                    return 1;
                } 
                if(a <= 2 && nd >= 4 && a >= (6 - nd)){
                    nd2 = new Date(jsdate.getFullYear() - 1 + "/12/31");
                    return date("W", Math.round(nd2.getTime()/1000));
                }
                return (1 + (nd <= 3 ? ((a + nd) / 7) : (a - (7 - nd)) / 7) >> 0);
            },

        // Month
            F: function(){
                return txt_months[f.n()];
            },
            m: function(){
                return pad(f.n(), 2);
            },
            M: function(){
                var t = f.F();
                return t.substr(0,3);
            },
            n: function(){
                return jsdate.getMonth() + 1;
            },
            t: function(){
                var n;
                if( (n = jsdate.getMonth() + 1) == 2 ){
                    return 28 + f.L();
                }
                if( n & 1 && n < 8 || !(n & 1) && n > 7 ){
                    return 31;
                }
                return 30;
            },

        // Year
            L: function(){
                var y = f.Y();
                return (!(y & 3) && (y % 1e2 || !(y % 4e2))) ? 1 : 0;
            },
            o: function(){
                if (f.n() === 12 && f.W() === 1) {
                    return jsdate.getFullYear()+1;
                }
                if (f.n() === 1 && f.W() >= 52) {
                    return jsdate.getFullYear()-1;
                }
                return jsdate.getFullYear();
            },
            Y: function(){
                return jsdate.getFullYear();
            },
            y: function(){
                return (jsdate.getFullYear() + "").slice(2);
            },

        // Time
            a: function(){
                return jsdate.getHours() > 11 ? "pm" : "am";
            },
            A: function(){
                return f.a().toUpperCase();
            },
            B: function(){
                // peter paul koch:
                var off = (jsdate.getTimezoneOffset() + 60)*60;
                var theSeconds = (jsdate.getHours() * 3600) +
                                 (jsdate.getMinutes() * 60) +
                                  jsdate.getSeconds() + off;
                var beat = Math.floor(theSeconds/86.4);
                if (beat > 1000) {
                    beat -= 1000;
                }
                if (beat < 0) {
                    beat += 1000;
                }
                if ((String(beat)).length == 1) {
                    beat = "00"+beat;
                }
                if ((String(beat)).length == 2) {
                    beat = "0"+beat;
                }
                return beat;
            },
            g: function(){
                return jsdate.getHours() % 12 || 12;
            },
            G: function(){
                return jsdate.getHours();
            },
            h: function(){
                return pad(f.g(), 2);
            },
            H: function(){
                return pad(jsdate.getHours(), 2);
            },
            i: function(){
                return pad(jsdate.getMinutes(), 2);
            },
            s: function(){
                return pad(jsdate.getSeconds(), 2);
            },
            u: function(){
                return pad(jsdate.getMilliseconds()*1000, 6);
            },

        // Timezone
            e: function () {
/*                var abbr='', i=0;
                if (this.php_js && this.php_js.default_timezone) {
                    return this.php_js.default_timezone;
                }
                if (!tal.length) {
                    tal = timezone_abbreviations_list();
                }
                for (abbr in tal) {
                    for (i=0; i < tal[abbr].length; i++) {
                        if (tal[abbr][i].offset === -jsdate.getTimezoneOffset()*60) {
                            return tal[abbr][i].timezone_id;
                        }
                    }
                }
*/
                return 'UTC';
            },
            I: function(){
                return _dst(jsdate);
            },
            O: function(){
               var t = pad(Math.abs(jsdate.getTimezoneOffset()/60*100), 4);
               t = (jsdate.getTimezoneOffset() > 0) ? "-"+t : "+"+t;
               return t;
            },
            P: function(){
                var O = f.O();
                return (O.substr(0, 3) + ":" + O.substr(3, 2));
            },
            T: function () {
/*                var abbr='', i=0;
                if (!tal.length) {
                    tal = timezone_abbreviations_list();
                }
                if (this.php_js && this.php_js.default_timezone) {
                    for (abbr in tal) {
                        for (i=0; i < tal[abbr].length; i++) {
                            if (tal[abbr][i].timezone_id === this.php_js.default_timezone) {
                                return abbr.toUpperCase();
                            }
                        }
                    }
                }
                for (abbr in tal) {
                    for (i=0; i < tal[abbr].length; i++) {
                        if (tal[abbr][i].offset === -jsdate.getTimezoneOffset()*60) {
                            return abbr.toUpperCase();
                        }
                    }
                }
*/
                return 'UTC';
            },
            Z: function(){
               return -jsdate.getTimezoneOffset()*60;
            },

        // Full Date/Time
            c: function(){
                return f.Y() + "-" + f.m() + "-" + f.d() + "T" + f.h() + ":" + f.i() + ":" + f.s() + f.P();
            },
            r: function(){
                return f.D()+', '+f.d()+' '+f.M()+' '+f.Y()+' '+f.H()+':'+f.i()+':'+f.s()+' '+f.O();
            },
            U: function(){
                return Math.round(jsdate.getTime()/1000);
            }
    };

    return format.replace(/[\\]?([a-zA-Z])/g, function(t, s){
        if( t!=s ){
            // escaped
            ret = s;
        } else if( f[s] ){
            // a date function exists
            ret = f[s]();
        } else{
            // nothing special
            ret = s;
        }
        return ret;
    });
}


Papercut.ExtGrid = Class.create(Papercut.Base, {
	processingPage: ''
	,primaryKey:'id'
	,defaultAction: 'grid'
	,fields:[]
	
	,initialize: function($super, renderTo, options) {
		$super(options);
		this.renderTo = renderTo;
		this.init();
	}
	,init: function(){
		this.initExt();	
		this.prepareState();
		this.initFields();	
		this.initColumns();
		this.initToolbar();
		this.initGrid();
		this.render();
		this.loadData();
	}
	,render: function(){
	    //Render it all and load it
		//this.optionToolbar.render(this.renderTo);
	    setTimeout(function() {this.grid.render(this.renderTo);}.bind(this), 0);
	    
	}
	,initExt: function(){
		//Initialize items	
		Ext.QuickTips.init();	
	    
		//Save the states
		var cp = new Ext.state.CookieProvider({
			expires: new Date(new Date().getTime()+(1000*60*60*24*30)) //30 days
		});
		Ext.state.Manager.setProvider(cp);
	    
	    
	    Ext.ux.menu.RangeMenu.prototype.icons = {
		  gt: '/js/ext/resources/icons/greater_then.png', 
		  lt: '/js/ext/resources/icons/less_then.png',
		  eq: '/js/ext/resources/icons/equals.png'
		};
		//Ext.grid.filter.StringFilter.prototype.icon = '/js/ext/resources/icons/find.png';
		

	}
	,initColumns: function(){
		this.columns.splice(0, 0, new Ext.grid.CheckboxSelectionModel());
	}
	,initFields: function(){
		for(var i = 0; i < this.columns.length; i++){
			this.fields.push(this.columns[i].dataIndex);
		}
	}
	,initToolbar: function(){
		this.optionToolbar = new Ext.Toolbar();
		
		//make option menu
		var perPageMenu = new Ext.menu.Menu();
		var perItems = new Array(10, 20, 50, 100);
		for(var i = 0; i < perItems.length; i++){
			var count = perItems[i];
			var checked = false;
			if(perItems[i] == this.filter.per){
				checked = true;
			}
			perPageMenu.add({
				scope: this,
				text: count,
				data: count,
				checked: checked,
				group: 'perPageItems',
				checkHandler: this.setPerPage
			});
		}
		this.optionMenu = new Ext.menu.Menu();
		this.optionMenu.add({
			scope: this,
			text: "Clear Filters",
			handler: this.clearFilters
		},{
			scope: this,
			text: "Per Page",
			menu: perPageMenu
		});
		
		//Make the action menu
		this.actionMenu = new Ext.menu.Menu();
		for(i = 0; i < this.actions.length; i++){
			var actionItem = this.actions[i];
			this.actionMenu.add({
				scope: this,
				text: actionItem.display,
				cls:"x-btn-text-icon",
	        	icon: actionItem.icon,
				data: actionItem,
				handler: this.doAction
			});
		}
		
		this.optionToolbar.add({ text: "Options", menu: this.optionMenu });
	    if (this.actions.length) {
	    	this.optionToolbar.add({ text: 'Actions', menu: this.actionMenu} );
	    }
	}
	,initGrid: function(){
		var t = this;
	    // create the Data Store
	    this.store = new Ext.data.Store({
	        proxy: new Ext.data.HttpProxy({
	        	url: this.processingPage + this.defaultAction
	        }),
	        reader: new Ext.data.JsonReader({
	            root: 'records',
	            totalProperty: 'total',
	            fields: this.fields
	        }),
	        remoteSort: true,
		    sortInfo: { field: this.filter.sortBy, direction: this.filter.sortOrder }
		    //baseParams: { start: this.filter.start, limit: this.filter.per }
	    });
	    
	    this.store.on('beforeload', function(store, options){
	    	
	    });
	    
	    this.store.on('load', function(store, options){
	    	
	    	//save the cookie data
	    	var state = Ext.state.Manager.getProvider();
			var stateData = state.get(t.getStateName());
			if(stateData == null){
		    	stateData = {};
			}
			stateData.start = store.lastOptions.params.start;
		    stateData.limit = store.lastOptions.params.limit;
		    state.set(t.getStateName(), stateData);
		    
		    //check if the filter has caused the limit to be greater than the total amount of records
		    var lastStart = store.lastOptions.params.start;
		    var newTotal = store.reader.jsonData.total;
		    if(newTotal < lastStart){
		    	store.load({params: { start: 0, limit: store.lastOptions.params.limit } } );	
		    }
	    });
	    
		//Possible Types: numeric, string, date, list
	    this.filters = new Ext.ux.grid.GridFilters({
	    	scope: this,
	    	stateful: true,
	    	id: this.createCustomId("grid-filters"),
			filters: this.getGridFilters()
	    });
	    
	    this.pager = new Ext.PagingToolbar({
	        pageSize: this.filter.per,
	        store: this.store,
	        displayInfo: true,
	        displayMsg: 'Displaying {0} - {1} of {2}',
	        emptyMsg: "No items to display"
	    });
	    
	    
	    ////////////////////////////////////////////////////////////////////	
	    // Main Grid
	    ////////////////////////////////////////////////////////////////////
	    this.grid = new Ext.grid.GridPanel({
	    	id: this.createCustomId("grid-state"),
	    	scope: this,
	    	layout:'fit',
	    	stateful: true,
	    	stateId: this.createCustomId("grid-state-"),
	        store: this.store,
	        sm: new Ext.grid.CheckboxSelectionModel(),
	        cm: new Ext.grid.ColumnModel(this.columns),
	        stripeRows: true,
	        height: 400,
	        plugins: this.filters,
	        enableDragDrop: true,
	        ddGroup: 'grid-drag-drop',
	        //disableSelection: true,
	        
	        //View Config
	        viewConfig: {
		        forceFit: true
		    },
	        tbar: new Ext.Panel({items: [this.optionToolbar, this.pager]})
	    });
	    
	    this.grid.getColumnModel().id = this.createCustomId("column-model-");
	    
	    this.grid.on('beforestatesave', function(grid, state){
	    	state.start = grid.store.lastOptions.params.start;
	    	state.limit = grid.store.lastOptions.params.limit;
	    });
	    
	    this.grid.on('render', function(grid){
	    	var ddrow = new Ext.ux.dd.GridReorderDropTarget(grid, {
		        copy: false,
		        listeners: {
		            beforerowmove: function(objThis, oldIndex, newIndex, records) {
		                
		            },
		            afterrowmove: function(objThis, oldIndex, newIndex, records) {
		                
		            },
		            beforerowcopy: function(objThis, oldIndex, newIndex, records) {
		                
		            },
		            afterrowcopy: function(objThis, oldIndex, newIndex, records) {
		                
		            }
		        }
	    	});
	    });
	   	
	}
	,prepareState: function(){
		var i;
		var t = this;
		
		//add ids to all columns
		for(i = 0; i < this.columns.length; i++){
			var c = this.columns[i];
			c.id = this.createCustomId("columns") + i; 
		}
		
		//add ids to all filters
		var filters = this.getGridFilters();
		for(i = 0; i < filters; i++){
			var f = filters[i];
			f.id = this.createCustomId("filters") + i; 
		}
		this.getGridFilters = function(){ return filters; };
		
		//get limit and page
		var state = Ext.state.Manager.getProvider();
		var stateData = state.get(t.getStateName());
		if(stateData != null){
			if(stateData.limit != null){
				this.filter.per = stateData.limit;
			}
			if(stateData.start != null){
				this.filter.start = stateData.start;
			}
		}		
		
	}
	
	,loadData: function(){
		this.store.load({params: { start: this.filter.start, limit: this.filter.per} } );	
	}
	,reload: function(){
		this.store.reload();
	}
	,getSelectedIds: function(){
		var selected = this.grid.getSelectionModel().getSelections();
		var returnSelected = [];
		for(var i = 0; i < selected.length; i++){
			returnSelected.push(selected[i].data[this.primaryKey]);
		}
		return returnSelected;
	}
	,getSelectedRecords: function(){
		var selected = this.grid.getSelectionModel().getSelections();
		return selected;
	}
	,setSelected: function(){
		
	}
	,doAction: function(actionItem){
		var selected = this.getSelectedIds();
		var action = actionItem.data.action;
		if (selected.length && action.length) {
			var confirmMessage = actionItem.data.confirmMessage;
			var confirmed = true;
			
			if(confirmMessage != null){
				Ext.Msg.show({
	        	   scope: this,
				   title:'Confirm Delete',
				   msg: 'Are you sure you want to delete these items?',
				   buttons: Ext.Msg.YESNOCANCEL,
				   animEl: 'elId',
				   fn: function(id){
				   		if(id == "yes"){
				   			Ext.Ajax.request({
								scope: this,
								url: this.processingPage + action,
								params: { 'selected[]' : selected },
								success: function(r){
									this.onAjaxSuccess(r);
							   		var response = Ext.util.JSON.decode(r.responseText);
							   		if(actionItem.data.onComplete != null){
							   			actionItem.data.onComplete(response);
							   		}				   		
								}
							});
				   		}
				   }
				});
			}
			else {
				Ext.Ajax.request({
					scope: this,
					url: this.processingPage + action,
					params: { 'selected[]' : selected },
					success: function(r){
						this.onAjaxSuccess(r);
				   		var response = Ext.util.JSON.decode(r.responseText);
				   		if(actionItem.data.onComplete != null){
				   			actionItem.data.onComplete(response);
				   		}				   		
					}
				});
			}
		}
	}
	,createFilterStore: function(action){
		var filterStore = new Ext.data.JsonStore({
			scope: this,
	      	url: this.processingPage + action,
	      	root: action,
	      	fields: ['text', 'id']
		});
		return filterStore;
	}
	,setPerPage: function(pageItem){
		var per = parseInt(pageItem.data);
		this.filter.per = per;
		this.pager.pageSize = this.filter.per;
		this.loadData();
	}
	,clearFilters: function(){
		this.filters.clearFilters();
	}
	,getStateName: function(){
		return "grid-state-" + this.processingPage.replace(/\//g, "-");
	}
	,createCustomId: function(prefix){
		return prefix + this.processingPage.replace(/\//g, "-") + "-" + this.defaultAction;
	}
	,onAjaxSuccess: function(r){
		var response = Ext.util.JSON.decode(r.responseText);
		if(response.good){
   			this.reload();
   			
   			if(response.messages.length){
	   			new Ext.ux.Notify({
		 			title: '',
		 			msg: response.messages
		 		}).show(document);
   			}
   		}
	}
});

Papercut.MenuArray = new Array();
Papercut.MenuBase = Class.create(Papercut.Base, {
	className:false,
	divider:false,
	dividerHTML:'',
	reverseTop:false,
	numLevels:-1,
	autoSetup:true,
	
	initialize: function(menuId, options) {
		this.setOptions(options);
		this.menu = $(menuId);
		if (this.menu == null) {
			return false;
		}
		if (this.menu.id == null || !this.menu.id.length) {
			this.menu.id = 'PapercutObject-' + Papercut.objectCount++;
		}
		this.menuIndex = Papercut.MenuArray.length;
		Papercut.MenuArray[this.menuIndex] = this;
		this.fire('beforeSetup');
		if (this.autoSetup) {
			this.setup();
		}
	}
	,setup: function() {
		if (this.className) {
			this.menu.addClassName(this.className);
		}
		this.container = this.menu.wrap('div', {'class': 'papercutMenuWrap'});
		this.container.addClassName('clearFix');
		
		this.menu.select('ul').invoke('addClassName', 'subMenu');
		this.menu.select('li:last-child>a').invoke('addClassName', 'last');
		this.menu.select('li:first-child>a').invoke('addClassName', 'first');
		this.menu.childElements().invoke('down', 'a').invoke('addClassName', 'mainLinks');
		this.menu.select('.subMenu a').invoke('addClassName', 'subLinks');
		
		this.pageUrl = this.getPageUrl(location.href);
		
		if (this.reverseTop) {
			var topEls = this.menu.childElements();
			var numTop = topEls.length;
			for (var i = 0; i < numTop-1; i++) {
				topEls[numTop-1].insert({after:topEls[i]});
			}
		}
		
		var anchors = this.menu.select('a');
		
		for (var i = 0; i < anchors.length; i++) {
			// add title class names to links
			$(anchors[i].parentNode).addClassName(anchors[i].innerHTML.safeTitle());
			anchors[i].addClassName(anchors[i].innerHTML.safeTitle());
			
			// show more arrow
			if (anchors[i].siblings().length) {
				anchors[i].addClassName('hasChildren');
				var moreArrowSpan = new Element('span');
				moreArrowSpan.addClassName('moreArrow pngFix');
				anchors[i].insert(moreArrowSpan);
			}
			
			// show icon
			var menuIconSpan = new Element('span', {className:'menuIcon'});
			menuIconSpan.addClassName('menuIcon pngFix');
			anchors[i].insert(menuIconSpan);
			
			// set active page
			if (this.getComparePage(anchors[i].href) == this.pageUrl) {
				anchors[i].addClassName('activePage');
				
				// apply active class to parents
				var node = anchors[i];
				while (node.id != this.menu.id) {
					node.up('li').down('a').addClassName('active');
					node = node.up('ul');
					if (node == null) return false;
				}
			}
		}
		
		// init dividers
		if (this.divider) {
			// get top level lis
			var topLevels = this.menu.childElements();
			for (var i = 0; i < topLevels.length-1; i++) {
				topLevels[i].insert({after:'<li class="divider">'+this.dividerHTML+'</li>'});
			}
		}
	}
	
	,getComparePage: function(url) {
		url = url.toLowerCase();
		if (url.charAt(url.length-1) == '/') {
			url = url.substr(0, url.length-1);
		}
		return url;
	}
	,getPageUrl: function(url) {
		url = url.toLowerCase();
		url = url.replace(/#.*/, '');
		if (url.charAt(url.length-1) == '/') {
			url = url.substr(0, url.length-1);
		}
		if (url.match(/pages\//)) {
			url = url.replace(/pages\/([^\/]+).*/, 'pages/$1');
		}
		return url;
	}
	
	,getLevel: function(node) {
		var level = 0;
		for (level = 0; node.id != this.menu.id; level++) {
			node = node.up('ul');
			if (node == null) return false;
		}
		return level;
	}
});

Papercut.PopupMenu = Class.create(Papercut.MenuBase, {
	direction:'horizontal'
	,autoWidth:'true'
	,numLevels:-1
	,center: false
	
	,initialize: function($super, menuId, options) {
		$super(menuId, options);
		if (this.menu != null) {
			this.initMarkup();
			this.initEvents();
		}
	}
	
	,setAutoWidth: function() {
		if (this.autoWidth) {
			var fixWidth = function(sub) {
				if (!sub.offsetWidth) return false;
				var widest = 0;
				var items = sub.childElements();
				var originalWidth = sub.getWidth();
				sub.setStyle({width:'3000px'});
				items.each(function(item) {
					widest = Math.max(widest, item.getWidth());
				});
				if (widest != 3000) {
					items.invoke('setStyle', {width:widest+'px'});
					sub.setStyle({width:widest+'px'});
				}
				else {
					sub.setStyle({width:originalWidth+'px'});
				}
			};
			if (this.direction == 'horizontal') {
				this.menu.select('ul').each(fixWidth);
			}
		}
	}
	
	,hideAll: function() {
		this.menu.select('ul').invoke('addClassName', 'hidden');
	}
	
	,initMarkup: function() {
		this.menu.addClassName('popupMenu horizontal');
		this.setAutoWidth();
		this.hideAll();
		this.centerMenu();
	}
	
	,centerMenu: function(){
		if(this.center){
			var totalWidth = 0;
			
			//compute the total width
			this.menu.childElements().each(function(el){
				totalWidth += el.offsetWidth;
			});
			
			//Add 10 for mac
			totalWidth += 10;
			
			//center the menu
			this.menu.parentNode.setStyle({
				width: totalWidth + "px"
				,margin: "0px auto"
			});
			
			// chrome width bug fix
			if (totalWidth >= 1400) {
				setTimeout(this.centerMenu.bind(this), 200);
			}
		}
	}
	
	,initEvents: function() {
		this.container.observe('mouseenter', function() {
			clearTimeout(this.hideTimeout);
		}.bind(this));
		this.container.observe('mouseleave', function() {
			this.hideTimeout = setTimeout(this.hideAll.bind(this), 500);
		}.bind(this));
		this.menu.select('a').each(function(el) {
			el.observe('mouseenter', this.mouseEnter.bind(this, el));
		}.bind(this));
		this.menu.select('a').each(function(el) {
			el.observe('click', this.mouseClick.bind(this, el));
		}.bind(this));
	}
	,mouseClick: function(el) {
		this.fire('click', el);
	}
	,mouseEnter:function(el) {
		el.up('li').siblings().invoke('down', 'ul').each(function(el) {
			if (el != null) {
				el.addClassName('hidden');
			}
		});
		if (this.numLevels == -1 || this.numLevels >= this.getLevel(el)) {
			if (el.hasClassName('hasChildren')) {
				var x = y = 0;
				var sub = el.up('li').down('ul');
				
				if (this.direction == 'horizontal' && !el.up('ul').hasClassName('subMenu')) {
					y = el.getHeight();
				}
				else {
					x = el.getWidth();
				}
				
				sub.setStyle({
					left:x+'px',
					top:y+'px'
				}).removeClassName('hidden').select('>ul').invoke('addClassName', 'hidden');
			}
			this.setAutoWidth();
		}
	}
});

Papercut.TrackSelector = Class.create(Papercut.Base, {
	selectorContainer:false
	,selector:false
	,placementContainer:false
	,topId:''
	,parentId:0
	,urlField:'url'
	,menuId:false
	,menuObject:false
	
	,initialize: function($super, menuId, options) {
		this.selectorContainer = $$('.trackSelector').last();
		this.selector = this.selectorContainer.down('select');
		this.placementContainer = this.selectorContainer.down('.menuPlacementContainer');
		this.menuId = this.placementContainer.down('ul');
		if (this.menuId == null) {
			this.placementContainer.insert(new Element('ul'));
			this.menuId = this.placementContainer.down('ul');
		}
		$super(options);
		if (this.menuId != null) {
			this.initMenu();
			this.initEvents();
		}
	}

	,initMenu: function() {
		this.menuObject = new Papercut.PopupMenu(this.menuId);
		this.initMarkup();
		this.initMenuEvents();
	}
	
	,initMarkup: function() {
		this.menuObject.menu.addClassName('trackSelector');
	}
	
	,initEvents: function() {
		this.selector.observe('change', this.changeTrack.bind(this));
	}
	
	,initMenuEvents: function() {
		
	}
	
	,changeTrack: function() {
		this.placementContainer.innerHTML = '<span class="loadingSpinner"></span> Loading...';
		var url = '/ajax/simon/process/load-menu';
		var pars = {
			trackId:this.selector.value
			,topId:this.topId
			,parentId:this.parentId
			,urlField:this.urlField
		};
		new Ajax.Request(url, {
			method: 'post',
			postBody: Object.toQueryString(pars),
			onComplete: function(transport) {
				var status = transport.responseJSON;
				if (status.good) {
					this.placementContainer.innerHTML = status.html;
					this.menuId = this.placementContainer.down('ul');
					this.initMenu();
					this.fire('loadTrack');
				}
			}.bind(this)
		});
	}
});

Papercut.ParentSelector = Class.create(Papercut.TrackSelector, {
	initialize: function($super, menuId, options) {
		$super(menuId, options);
		//this.on('loadTrack', this.initMenu.bind(this));
		this.parentInfoInput = this.selectorContainer.down('.parentInfo');
		this.parentInfoDisplay = this.selectorContainer.down('.parentInfoDisplay');
		this.parentInfoList = new Element('ul');
		this.parentInfoDisplay.insert(this.parentInfoList);
		this.updateParentDisplay();
	}
	,clearList: function() {
		this.parentInfoList.select('li').each(Element.remove);
	}
	,updateParentDisplay: function () {
		this.currentParents = this.parentInfoInput.value.evalJSON();
		this.clearList();
		for (var i = 0; i < this.currentParents.length; i++) {
			this.parentInfoList.insert(
				new Element('li').insert(
					new Element('span').insert(
						this.currentParents[i].display + ' '
					)
				).insert(
					new Element('span', {
						className:'links'
					}).insert('Remove').observe('click', function(i) {
						this.currentParents.splice(i, 1);
						this.updateParentField();
						this.updateParentDisplay();
					}.bind(this, i))
				)
			);
		}
	}
	,getTrackName: function(trackId) {
		for (var i = 0; i < this.selector.options.length; i++) {
			if (trackId == this.selector.options[i].value) {
				return this.selector.options[i].innerHTML;
			}
		}
		return false;
	}
	,updateParentField: function() {
		this.parentInfoInput.value = Object.toJSON(this.currentParents);
	}
	,initMenu: function() {
		this.menuId.insert(new Element('li', {
			name:'item_' + this.selector.value + '-0'
		}).insert(new Element('a', {
			className:'topLevelLink'
		}).insert('Top Level')));
		this.menuObject = new Papercut.PopupMenu(this.menuId);
		this.initMarkup();
		this.initMenuEvents();
	}
	,initMarkup: function($super) {
		$super();
		this.menuObject.menu.addClassName('parentSelector');
	}
	,initEvents: function($super) {
		$super();
	}
	,initMenuEvents: function() {
		var links = this.menuObject.menu.select('a');
		for (var i = 0; i < links.length; i++) {
			links[i].select('span').each(Element.remove);
			links[i].onclick = function(el) {
				var li = el.up('li');
				var name = li.readAttribute('name');
				var display = el.innerHTML;
				var trackId = parseInt(name.split('_')[1].split('-')[0]);
				var parentId = parseInt(name.split('_')[1].split('-')[1]);
				if (!parentId) {
					display += ' (' + this.getTrackName(trackId) + ')';
				}
				// make sure this doesn't already exist
				var good = true;
				for (var j = 0; j < this.currentParents.length; j++) {
					if (this.currentParents[j].trackId == trackId && this.currentParents[j].parentId == parentId) {
						good = false;
					}
				}
				if (good) {
					this.currentParents.push({
						display:display,
						trackId:trackId,
						parentId:parentId
					});
					this.currentParents = this.currentParents.uniq();
					this.updateParentField();
					this.updateParentDisplay();
				}
				return false;
			}.bind(this, links[i]);
		}
	}
});

Papercut.TreeMenu = Class.create(Papercut.MenuBase, {
	expandToActive:true
	,showTopLevelSiblings:true
	,showTopLevelLink:true
	,topLevelTextContainer:false
	
	,initialize: function($super, menuId, options) {
		$super(menuId, options);
		this.initMarkup();
		this.initEvents();
	}
	
	,initMarkup: function() {
		this.menu.addClassName('treeMenu');
		this.menu.select('ul').invoke('addClassName', 'hidden');
		
		if (this.expandToActive) {
			var activeLinks = this.menu.select('.active');
			activeLinks.each(function(el) {
				el.up('ul').removeClassName('hidden');
			}.bind(this));
			
			var activePages = this.menu.select('.activePage');
			activePages.each(function(el) {
				if (el.next() != null) {
					el.next().removeClassName('hidden');
				}
			}.bind(this));
		}
		
		var topLevelLink = this.menu.down('.active');
		if (topLevelLink != null) {
			if (!this.showTopLevelSiblings) {
				topLevelLink.up('li').siblings().each(Element.hide);
			}
			if (!this.showTopLevelLink) {
				topLevelLink.up('li').siblings().each(Element.hide);
				topLevelLink.hide();
			}
			if (this.topLevelTextContainer && $(this.topLevelTextContainer) !=  null) {
				$(this.topLevelTextContainer).innerHTML = topLevelLink.innerHTML.stripTags();
			}
		}
	}
	
	,initEvents:function() {
		this.menu.select('a').each(function(el) {
			el.observe('click', this.clickItem.bind(this, el));
		}.bind(this));
	}
	
	,clickItem:function(el) {
		if (el.href.match(/#$/)) {
			if (el.hasClassName('hasChildren')) {
				el.up('li').down('ul').toggleClassName('hidden');
			}
		}
	}
});

Papercut.Cookie = Class.create({

	/**
	 * Append before all cookie names to differntiate them.
	 */
	appendString: "__CJ_",

	/**
	 * Initializes the cookie jar with the options.
	 */
	initialize: function(options) {
		this.options = {
			expires: 3600,		// seconds (1 hr)
			path: '',			// cookie path
			domain: '',			// cookie domain
			secure: ''			// secure ?
		};
		Object.extend(this.options, options || {});

		if (this.options.expires != '') {
			var date = new Date();
			date = new Date(date.getTime() + (this.options.expires * 1000));
			this.options.expires = '; expires=' + date.toGMTString();
		}
		if (this.options.path != '') {
			this.options.path = '; path=' + escape(this.options.path);
		}
		if (this.options.domain != '') {
			this.options.domain = '; domain=' + escape(this.options.domain);
		}
		if (this.options.secure == 'secure') {
			this.options.secure = '; secure';
		} else {
			this.options.secure = '';
		}
	},

	/**
	 * Adds a name values pair.
	 */
	put: function(name, value) {
		name = this.appendString + name;
		cookie = this.options;
		var type = typeof value;
		switch(type) {
		  case 'undefined':
		  case 'function' :
		  case 'unknown'  : return false;
		  case 'boolean'  : 
		  case 'string'   : 
		  case 'number'   : value = String(value.toString());
		}
		var cookie_str = name + "=" + escape(Object.toJSON(value));
		try {
			document.cookie = cookie_str + cookie.expires + cookie.path + cookie.domain + cookie.secure;
		} catch (e) {
			return false;
		}
		return true;
	},

	/**
	 * Removes a particular cookie (name value pair) form the Cookie Jar.
	 */
	remove: function(name) {
		name = this.appendString + name;
		cookie = this.options;
		try {
			var date = new Date();
			date.setTime(date.getTime() - (3600 * 1000));
			var expires = '; expires=' + date.toGMTString();
			document.cookie = name + "=" + expires + cookie.path + cookie.domain + cookie.secure;
		} catch (e) {
			return false;
		}
		return true;
	},

	/**
	 * Return a particular cookie by name;
	 */
	get: function(name) {
		name = this.appendString + name;
		var cookies = document.cookie.match(name + '=(.*?)(;|$)');
		if (cookies) {
			return (unescape(cookies[1])).evalJSON();
		} else {
			return null;
		}
	},

	/**
	 * Empties the Cookie Jar. Deletes all the cookies.
	 */
	empty: function() {
		keys = this.getKeys();
		size = keys.size();
		for(i=0; i<size; i++) {
			this.remove(keys[i]);
		}
	},

	/**
	 * Returns all cookies as a single object
	 */
	getPack: function() {
		pack = {};
		keys = this.getKeys();

		size = keys.size();
		for(i=0; i<size; i++) {
			pack[keys[i]] = this.get(keys[i]);
		}
		return pack;
	},

	/**
	 * Returns all keys.
	 */
	getKeys: function() {
		keys = $A();
		keyRe= /[^=; ]+(?=\=)/g;
		str  = document.cookie;
		CJRe = new RegExp("^" + this.appendString);
		while((match = keyRe.exec(str)) != undefined) {
			if (CJRe.test(match[0].strip())) {
				keys.push(match[0].strip().gsub("^" + this.appendString,""));
			}
		}
		return keys;
	}
});

Papercut.PhotoSelector = Class.create(Papercut.Base, {
	url:'/ajax/photo-selector/process/'
		
	,initialize: function($super, options) {
		$super(options);
		this.albumSelectorContainer.hide();
		this.photoListContainer.hide();
		
		this.initMarkup();
		this.initEvents();
		this.getCurrentPhoto();
		this.loadAlbumList();
	}
	
	,getCurrentPhoto: function() {
		new Ajax.Request(this.url + 'get-current-photo', {
			postBody: Object.toQueryString({
				photoId:this.hiddenPhotoId.value
			})
			,onComplete: function(transport) {
				var status = transport.responseJSON;
				if (status.good) {
					this.currentPhoto.innerHTML = status.html;
					this.fire('currentPhotoChanged');
				}
			}.bind(this)
		});
	}
	
	,loadAlbumList: function() {
		new Ajax.Request(this.url + 'get-album-list', {
			onComplete: function(transport) {
				var status = transport.responseJSON;
				if (status.good) {
					this.albumSelectorContainer.innerHTML = status.html;
					this.albumSelector = this.albumSelectorContainer.down('select');
					this.albumSelector.observe('change', this.getPhotos.bind(this));
					this.fire('loadedAlbumList');
				}
			}.bind(this)
		});
	}
	
	,initMarkup: function() {
		
	}
	
	,initCurrentPhotoClick: function() {
		this.currentPhoto.down('img').observe('click', this.clickedToggle.bind(this));
	}
	
	,initEvents: function() {
		this.on('imagesInserted', this.initPhotoClickEvent.bind(this));
		this.on('imageSelected', this.clickedToggle.bind(this));
		this.on('loadedAlbumList', this.getPhotos.bind(this));
		this.on('currentPhotoChanged', this.initCurrentPhotoClick.bind(this));
		
		this.toggleLink.observe('click', this.clickedToggle.bind(this));
		this.initPhotoClickEvent();
	}
	
	,initPhotoClickEvent: function() {
		var images = this.photoListContainer.select('img');
		for (var i = 0; i < images.length; i++) {
			images[i].observe('click', this.imageClicked.bind(this, images[i]));
		}
	}
	
	,clickedToggle: function() {
		this.albumSelectorContainer.toggle();
		this.photoListContainer.toggle();
	}
	
	,imageClicked: function(img) {
		this.currentPhoto.innerHTML = '<img src="'+img.src+'" />';
		this.hiddenPhotoId.value = img.id.split('-')[1];
		this.fire('imageSelected');
		this.fire('currentPhotoChanged');
	}
	
	,getPhotos: function() {
		new Ajax.Request(this.url + 'get-photos', {
			method: 'post',
			postBody: Object.toQueryString({
				albumId:this.albumSelector.value
			}),
			onComplete: function(transport) {
				var status = transport.responseJSON;
				if (status.good) {
					this.photoListContainer.innerHTML = status.html;
					this.fire('imagesInserted');
				}
			}.bind(this)
		});
	}
});

Effect.SmoothScroll = Class.create();
Object.extend(Object.extend(Effect.SmoothScroll.prototype, Effect.Base.prototype), {
	initialize: function (element) {
		this.element = $(element);
		var options = Object.extend({ x: 0, y: 0, mode: 'absolute' } , arguments[1] || {});
		this.start(options);
    },

	setup: function () {
		if (this.options.continuous && !this.element._ext) {
			this.element.cleanWhitespace();
			this.element._ext = true;
			this.element.appendChild(this.element.firstChild);
        }

		this.originalLeft = this.element.scrollLeft;
		this.originalTop  = this.element.scrollTop;

		if (this.options.mode == 'absolute') {
			this.options.x -= this.originalLeft;
			this.options.y -= this.originalTop;
        }
    },

	update: function (position) {
    	this.element.scrollLeft = this.options.x * position + this.originalLeft;
		this.element.scrollTop  = this.options.y * position + this.originalTop;
    }
});


//this will replace tabs, slideshows, and provide carousels and other options
Papercut.SectionedContent = Class.create(Papercut.Base, {
	width:'auto'
	,height:'auto'
	,sections:[]
	,activeIndex:0
	,effect:false // fade, scroll
	,inTransition:false
	,currentZ:0
	,currentEffect:false
	,autoStart:false
	,isSlideshow:false
	,slideshowInterval:false
	,intervalTime:5000
	,childSelector:false
	,isCircular:true
	,duplicateMade:false
	,duplicate:null
	
	,initialize: function($super, container, options) {
		$super(options);
		this.container = $(container);
		this.initMarkup();
		this.initEvents();
	}

	,initMarkup: function() {
		this.container.setStyle({
			width:this.width
			,height:this.height
		});
		this.container.addClassName('sectionedContent');
		
		if (this.container.getStyle('position') != 'absolute') {
			this.container.style.position = 'relative';
		}
		if (!this.childSelector) {
			this.sections = this.container.childElements();
		}
		else {
			this.sections = this.container.select(this.childSelector);
		}
		
		switch (this.effect) {
			case 'fade':
				for (var i = 1; i < this.sections.length; i++) {
					this.sections[i].hide();
				}
				this.sections.each(function(el) {
					el.setStyle({
						position:'absolute'
						,top:0
						,left:0
						,width:this.width
						,height:this.height
					});
					el.addClassName('sectionedContentItem');
				}.bind(this));
			break;
			case 'scroll':
				
			break;
			default:
				for (var i = 1; i < this.sections.length; i++) {
					this.sections[i].hide();
				}
			break;
		}
	}
	
	,initEvents: function() {
		if (this.isSlideshow) {
			var urls = this.container.select('.url');
			this.sections.each(function(el) {
				el.down('img').wrap('a');
			});
			for (var i = 0; i < this.sections.length; i++) {
				this.sections[i].down('a').href = urls[i].innerHTML;
			}
			if (this.autoStart) {
				this.play();
			}
		}
	}
	
	,play: function() {
		if (!this.slideshowInterval) {
			this.slideshowInterval = setInterval(this.next.bind(this, true), this.intervalTime);
		}
	}
	,pause: function() {
		if (this.slideshowInterval) {
			clearInterval(this.slideshowInterval);
			this.slideshowInterval = false;
		}
	}
	,stop: function() {
		this.pause();
	}

	,next: function() {
		this.showSection(this.getNextIndex());
	}
	,prev: function() {
		this.showSection(this.getPrevIndex());
	}
	,first: function() {
		this.showSection(0);
	}
	,last: function() {
		this.showSection(this.sections.length-1);
	}
	,getNextIndex: function() {
		return (this.activeIndex + 1) % this.sections.length;
	}
	,getPrevIndex: function() {
		if (this.activeIndex) {
			return (this.activeIndex - 1);
		}
		else {
			return (this.sections.length - 1);
		}
	}
	
	,showSection: function(index, slideshowTriggered) {
		if (slideshowTriggered != null && slideshowTriggered) {
			
		}
		else {
			//this.pause();
		}
		this.fire('beforeShowSection', index);
		switch (this.effect) {
			case 'fade':
				this.sections[index].hide();
				this.sections[index].style.zIndex = this.currentZ++;
				new Effect.Appear(this.sections[index], {
					afterFinish: function(oldIndex) {
						if (oldIndex != this.activeIndex) {
							this.sections[oldIndex].hide();
						}
					}.bind(this, this.activeIndex)
				});
			break;
			case 'scroll':
				var transition;

				if (this.currentEffect) {
					this.currentEffect.cancel();
				}
				
				transition = Effect.Transitions.spring;
				transition = Effect.Transitions.sinoidal;
				var scrollerOffset = this.container.cumulativeOffset();
				
				var elementOffset  = this.sections[index].cumulativeOffset();
				if (index == 0 && this.activeIndex == this.sections.length-1) {
					
					if (!this.duplicateMade) {
						this.duplicate = $(this.sections[0].cloneNode(true));
						this.duplicate.setStyle({
							position:'absolute'
							,top:0
							,left:(parseFloat(this.width) * this.sections.length) + 'px'
							,width:this.width
							,height:this.height
						});
						this.container.insert(this.duplicate);
						this.duplicateMade = true;
					}
					
					elementOffset  = this.duplicate.cumulativeOffset();
				}
				
				this.currentEffect = new Effect.SmoothScroll(this.container, {
					duration: 1,
					x: (elementOffset[0] - scrollerOffset[0]),
					y: (elementOffset[1] - scrollerOffset[1]),
					transition: transition,
					afterFinish: (function (index, activeIndex) {
						if (index == 0 && activeIndex == this.sections.length-1) {
							this.container.scrollLeft = 0;
						}
					}).bind(this, index, this.activeIndex)
				});
			break;
			default:
				this.sections[this.activeIndex].hide();
				this.sections[index].show();
			break;
		}
		this.activeIndex = index;
		this.fire('afterShowSection', index);
	}
});

Papercut.Carousel = Class.create(Papercut.SectionedContent, {
	effect:'scroll'
	,isSlideshow:true
	,autoStart:true
	
	,initEvents: function($super) {
		this.sections.each(function(el, i) {
			el.setStyle({
				position:'absolute'
				,top:0
				,left:(parseFloat(this.width) * i) + 'px'
				,width:this.width
				,height:this.height
			});
		}.bind(this));
		$super();
	}
});

Papercut.SectionedContentController = Class.create(Papercut.Base, {
	sectionedContentObject:false
	,nextButton:null
	,prevButton:null
	,lastButton:null
	,firstButton:null
	,playButton:null
	,pauseButton:null
	,stopButton:null
	,listController:null
	
	,controlItems:['nextButton', 'prevButton', 'lastButton', 'firstButton', 'playButton', 'pauseButton', 'stopButton']
	
	,initialize: function($super, sectionedContentObject, options) {
		$super(options);
		this.sectionedContentObject = sectionedContentObject;
		for (var i = 0; i < this.controlItems.length; i++) {
			this[this.controlItems[i]] = $(this[this.controlItems[i]]);
		}
		this.listController = $(this.listController);
		this.initMarkup();
		this.initEvents();
	}

	,initMarkup: function() {
		
	}
	
	,initEvents: function() {
		if (this.listController != null) {
			this.listController.down('li').addClassName('active');
			this.sectionedContentObject.on('beforeShowSection', function(params) {
				var itemNum = params[1];
				var activeItem = this.listController.down('.active');
				if (activeItem != null) {
					activeItem.removeClassName('active');
				}
				this.listController.select('li')[itemNum].addClassName('active');
				
			}.bind(this));
			this.listController.select('li').each(function(el, index) {
				el.observe('click', function(index) {
					clearInterval(this.sectionedContentObject.slideshowInterval);
					this.sectionedContentObject.slideshowInterval = false;
					this.sectionedContentObject.showSection(index);
					this.sectionedContentObject.play();
				}.bind(this, index));
				
				el.observe('mouseenter', function() {
					this.addClassName('hover');
				});
				el.observe('mouseleave', function() {
					this.removeClassName('hover');
				});
				
			}.bind(this));
		}
		
		if (this.nextButton != null) {
			this.nextButton.observe('click', function() {
				this.sectionedContentObject.pause();
				this.sectionedContentObject.next();
			}.bind(this));
		}
		if (this.prevButton != null) {
			this.prevButton.observe('click', function() {
				this.sectionedContentObject.pause();
				this.sectionedContentObject.prev();
			}.bind(this));
		}
		if (this.pauseButton != null) {
			this.pauseButton.observe('click', function() {
				this.sectionedContentObject.pause();
			}.bind(this));
		}
		if (this.playButton != null) {
			this.playButton.observe('click', function() {
				this.sectionedContentObject.play();
			}.bind(this));
		}
		
		
		var controlItem = false;
		for (var i = 0; i < this.controlItems.length; i++) {
			controlItem = this.controlItems[i];
			if (this[controlItem] != null) {
				this[controlItem].observe('mouseenter', this.mouseEnterControl.bind(this, controlItem));
				this[controlItem].observe('mouseleave', this.mouseLeaveControl.bind(this, controlItem));
			}
		}
	}
	
	,mouseEnterControl: function(whichButton) {
		this[whichButton].addClassName('hover');
	}
	
	,mouseLeaveControl: function(whichButton) {
		this[whichButton].removeClassName('hover');
	}
	
});

Papercut.PreloadImages = function(images){
	var img = new Image();
	for(var i=0; i < images.length; i++){
		img.src = images[i];
	}
};

Papercut.CreateIEBar = function(){
	//Check for cookie, or create new one
	var barCookie = new Papercut.Cookie({ path: '/' });
	if(barCookie.get('IEBar') == null){
		barCookie.put('IEBar', false);
	}
	var disableBar = barCookie.get('IEBar');
	
	if(disableBar == "false"){
		var bar = document.createElement('div');
		bar.id = "IEBar";
		bar.style.display = 'none';
		bar.innerHTML = "<div class=\"inner\" onclick=\"Papercut.LaunchChromeFrameInstall();\"><img src=\"/images/ie/shield.gif\" align=\"absmiddle\" />&nbsp;&nbsp; Internet Explorer is missing updates required to view this site. Click here to update...</div><div class=\"close\"><a href=\"javascript:;\" onclick=\"Papercut.DisableIEBar();\"><img src=\"/images/ie/close.gif\" style=\"float:right\" align=\"absmiddle\" /></a></div>";
		document.body.appendChild(bar);
		Effect.toggle('IEBar', 'blind', {duration:0.5, delay:0.5});
	}
	
}

Papercut.DisableIEBar = function(){
	Effect.toggle('IEBar', 'blind', {duration:0.5});
	
	//create cookie to not show again
	var barCookie = new Papercut.Cookie({ path: '/' });
	if(barCookie.get('IEBar') != null){
		barCookie.put('IEBar', true);
	}
};

Papercut.LaunchChromeFrameInstall = function(){
	Effect.toggle('IEBar', 'blind', {duration:0.5});
	fb.start({
		href:'http://www.google.com/chromeframe/index.html'
		,rev:'doAnimations:false' 
	});
	
	//Create cookie to not show again
	/*
	var barCookie = new Papercut.Cookie({ path: '/' });
	if(barCookie.get('IEBar') != null){
		barCookie.put('IEBar', true);
	}
	*/
}
