//contains commonly used classes and utility functions
//requires mootools
//--------------------------------
//BgLoader
//PageSelect
//Submiter
//Table
//File Uploader
//Array class extension
//String functions
//Other functions
//--------------------------------

//reports errors - will need a php class to go with it
function ErrorReport(err) {
	var error = new Error(err);
	if ($type(err) == 'hash') error.message = err.message;
	error.stack = error.stack.slice(error.stack.indexOf('\n',error.stack.indexOf('@http://')) + 1);
	error.lineNumber = error.stack.slice(error.stack.lastIndexOf(':') + 1);
	if (Browser.Engine.gecko) console.warn(error);
	new Request({'url':'/quote/error_mail/' + escape(JSON.encode(error).replaceAll('/','__')).replaceAll('%','qqz')}).send();
}

//Makes a popup inside the target (needs to be called in an onclick function)
//lots of css and class and content options
//will one day be expanded to replace confirm boxes

window.onerror = function(err,file,line){
//	alert(err + ' ' + file + ' ' + line);
}

function Flasher(el,a,b,options) {
	this.el = el;
	this.a = a;
	this.b = b;
	this.speed = '1000';
	this.continueCycle = true;
	this.topStrip = false;
	if ($chk(options.speed)) this.speed = options.speed;
	if ($chk(options.corner)) {
		if (options.corner == 'topStripColor') this.topStrip = this.el.getSibling('div[class=x-corner]');
	}	
	this.start = function(type) {
		if (type != 'delay') {
			this.cycle(this.el);
			if (this.topStrip) this.topStrip.getElements('div').each(function(div){this.cycle(div)}.bind(this));
		}
		// else {
		// 	this.fade(this.el);
		// 	if (this.topStrip) this.topStrip.getElements('div').each(function(div){this.fade(div)}.bind(this));
		// 	
		// }
	};
	// this.fade = function(element) {
	// 	this.el.setStyle('background',this.b);
	// 	var first = function(element) {
	// 		element.setProperty('background',this.a);
	// 		if (this.continue) this.fade.delay(this.speed,this,element);
	// 	};
	// 	first.delay(this.speed,this,element);
	// };
	this.cycle = function(element) {
		new Fx.Morph(element,{duration:this.speed,transition:Fx.Transitions.Sine.easeOut,onComplete:function(){
			new Fx.Morph(element,{duration:this.speed,transition:Fx.Transitions.Sine.easeOut,onComplete:function(){
				if (this.continueCycle) this.cycle(element);
				else this.el.setStyle('background-color',this.a);			
			}.bind(this)}).start({'background-color':[this.b,this.a]});
		}.bind(this)}).start({'background-color':[this.a,this.b]});
	};
	this.stop = function() {this.continueCycle = false}
}

function PopUp(e,data,options) {
	if ($type(e) != 'event') throw('e');
	this.event = e;
	this.event.stop();
	if (e.target.getTag() == 'img') this.target = e.target.getParent();
	else this.target = e.getTarget();
	this.request = false;
	this.extraCss = new Object();
	this.isMenu = false;
	this.level = 0;
	this.open = true;
	if (!$chk(options) && $type(data) == 'object') {
		options = data;
		data = undefined;
	}
	else if (!$chk(options)) options = new Object();	
	if ($chk(options.mouseClose)) this.clientY = this.event.event.clientY;
	else this.clientY = false;	

	if ($chk(options.level)) this.level = options.level;
	if ($type(options) != 'object') throw('options');
	if (options.isMenu) {
		options.allowClick = true;
		this.isMenu = true;
		this.extraCss.padding = 0;
	}
	if ($chk(options.addScript)) this.addScript = options.addScript;
	else this.addScript = false;
	if ($chk(options.useClass)) {
		this.cssClass = 'poppermcpoppy ' + options.useClass;
		this.css = "position:absolute";
	}
	else {
		this.cssClass = 'poppermcpoppy';
		this.css = "position:absolute;width:200px;height:200px;background-color:#CEEAE1;color:#000;border:1px solid #000;z-index:100;padding:15px;font-weight:normal;font-size:16px;line-height:normal;text-decoration:none;text-align:left;";
	}
	this.close = function() {
		if (this.level > 0 && this.save) return;
		this.target.removeClass('highlight');
		if ($('background-fade') !== null) {with ($('background-fade')) {
			getSibling('span[class^=poppermcpoppy]').dispose();
			dispose();
		}}
		else {
			if (this.target.getSibling('span[class^=poppermcpoppy]') !== null) this.target.getSibling('span[class^=poppermcpoppy]').dispose();
			if ($('background-fade') !== null) $('background-fade').dispose();
		}
		this.open = false;
	};
	this.addStyle = function(style) {
		var styleTokens = new StringTokenizer(style,';');
		while(styleTokens.hasNext()) {
			var token = styleTokens.nextToken().trim();
			this.extraCss[token.getToken(':')] = token.getToken(':',1);
		}	
	};
	this.getStyle = function(style) {
		if ($chk(this.extraCss[style])) return this.extraCss[style] + '';
		else if (this.css && this.css.indexOf(style > -1)) {
			var slice = this.css.slice(this.css.indexOf(style) + style.length + 1);
			return slice.substring(0,slice.indexOf(';'));
		}
		else return '0';
	};
	if ($chk(options.addStyle)) {
		if (options.addStyle.contains('style')) throw('style');
		this.addStyle(options.addStyle);
	}
	if ($chk(options.ieStyle && Browser.Engine.trident)) {
		if (options.ieStyle.contains('style')) throw('style');
		this.addStyle(options.ieStyle);
	}
	if ($chk(options.ajax)) this.addStyle('background-color:#ffffff;overflow-y:scroll');
	this.backgroundFade = defaultFor(options.backgroundFade,false);
	this.corner = defaultFor(options.corner,false);
	if ($type(data) != 'string' && $type(data) != 'element' && !$chk(options.ajax)) throw('data');
	if ($type(data) == 'element') {
		this.html = null;
		this.grabee = data;
	}
	else if ($(data) != null) {
		this.html = null;
		this.grabee = $(data);
	}
	else {
		if ($chk(options.ajax)) {
			this.request = options.ajax;
			this.html = '';
		}
		else this.html = data;
		this.grabee = new Element('span');
	}		
	if (options.allowClick) this.target.setProperty('href','#noclick');
	this.removeAll = function() {
		$(document.body).getElements('span[class^=poppermcpoppy]').each(function(span){span.dispose()})
		$(document.body).getElements('a').each(function(a){if (a.hasClass('highlight')) a.removeClass('highlight')});
	};		
	this.unique = function() {
		if ($chk(options.ajax)) {
			this.removeAll();
			return true;
		}
		if (this.target.getSibling('span') != null && this.target.getSibling('span').hasClass('poppermcpoppy')) {
			if (this.target.getProperty('href') != '#noclick' || this.isMenu) this.close()
			return false;
		}
		else if (this.target.getParent('span') != null && this.target.getParent('span').hasClass('poppermcpoppy') && this.level == 0) {
			if (this.target.getProperty('href') != '#noclick' || this.isMenu) this.close()
			return false;
		}			
		return true;
	};
	if (!this.unique()) return false;
	if (this.level > 0) {
		var parent = this.target.getParent('span[class^=poppermcpoppy]');
		this.extraCss.left = parent.getStyle('width').replace('px','').toInt() + parent.getStyle('padding').replace('px','').replace('pt','').toInt() * 2 - 2 + 'px';
	}
	if (this.target.getParent('a') !== null) this.target.getParent('a').setStyle('text-decoration','none');
	var left = 25;
	if (Browser.Engine.trident) left = 40;
	var closeW = parseInt(this.getStyle('width').replace('px','').toInt() / 2) - left + this.getStyle('padding').replace('px','').replace('pt','').toInt();
	if (this.backgroundFade) {
		new Element('div',{'id':'background-fade','style':'height:' + document.body.scrollHeight + ';width:' + '100%' + ';z-index:5;background-color:#000;position:absolute;top:0px;left:0px','events':{'click':function(e){e.stop();this.close()}.bind(this)}}).inject($(document.body),'top');
		$('background-fade').grayOut('now');
		var w = parseInt(this.getStyle('width').replace('px','').toInt() / -2) + document.getScroll().x;
		var h = parseInt(this.getStyle('height').replace('px','').toInt() / -2) + document.getScroll().y;
		this.css += 'left:50%;top:50%;margin:' + h + 'px auto auto ' + w + 'px;';
		this.pop = new Element('span',{'style':this.css,'class':this.cssClass,'html':this.html}).inject($('background-fade'),'after').adopt([this.grabee, new Element('div',{'style':'bottom:2px;left:' + closeW + 'px;position:absolute;','class':'poppermcpoppy-close'}).grab(new Element('a',{'href':'#noclick','html':'(close)','style':'color:#2A6BC8;font-weight:normal;line-height:2em','events':{'click':function(e){
			e.stop();
			this.close();
		}.bind(this)}}))]);
	}
	else if (options.isMenu) this.pop = new Element('span',{'style':this.css,'class':this.cssClass,'html':this.html}).inject(this.target,'after').adopt([this.grabee]);
	else this.pop = new Element('span',{'style':this.css,'class':this.cssClass,'html':this.html}).inject(this.target,'after').adopt([this.grabee, new Element('div',{'style':'bottom:2px;left:' + closeW + 'px;position:absolute;','class':'poppermcpoppy-close'}).grab(new Element('a',{'href':'#noclick','html':'(close)','style':'color:#2A6BC8;font-weight:normal;line-height:2em','events':{'click':function(e){this.close()}.bind(this)}}))]);
	this.pop.setStyles(this.extraCss);
	if (this.corner) this.pop.corner(this.corner);
	if (this.request) {
		this.pop.empty();
		var y = parseInt(this.pop.getStyle('height').replace('px','').toInt() / 3) + 'px';
		var x = parseInt(this.pop.getStyle('width').replace('px','').toInt() / 2) + 'px';
		new Element('img',{'src':'/media/images/spinner.gif','style':'margin:' + y + ' ' + x + ';'}).inject(this.pop,'top');
		new Request({url:this.request,onComplete:function(res){
			this.pop.set('html',res);
			new Element('a',{'html':'(close)','href':'#','style':'position:absolute;right:10px;color:#2A6BC8;font-weight:normal;','class':'poppermcpoppy-close','events':{'click':function(e){e.stop();this.close()}.bind(this)}}).inject(this.pop,'top');
			if (this.addScript) this.addScript.attempt();
		}.bind(this)}).send();
	}
	var div = this.pop.getElement('div[class^=poppermcpoppy-close]');
	var a = this.pop.getElement('a[class^=poppermcpoppy-close]');
	if ($chk(div)) div.addEvent('mouseover',function(){div.getElement('a').setStyle('background-color',this.getStyle('background-color'))}.bind(this));
	if ($chk(a)) a.addEvent('mouseover',function(){a.setStyle('background-color',this.getStyle('background-color'))}.bind(this));
	if ($chk(options.noClose)) {
		if ($chk(div)) div.dispose();
		if ($chk(a)) a.dispose();
	}
	if ($chk(options.mouseClose)) {
		this.target.addEvent('mouseleave',function(f){if (f.event.clientY < this.clientY) this.close()}.bind(this));
		this.pop.addEvent('mouseleave',function(){this.close()}.bind(this));
	}
	if (this.level == 0) document.onclick = function() {if (this.target.getProperty('href') != '#noclick' || this.isMenu) {
		document.onclick = null;
		this.close();
	}}.bind(this);	
	if (this.isMenu) {this.pop.getElements('a').each(function(el){
		if (!el.hasClass('poppersubmenu')) el.addEvent('mouseover',function(){
			el.getParent('span[class^=poppermcpoppy]').getElements('a').each(function(wsub) {
				if (wsub.hasClass('poppersubmenu')) {
					wsub.removeClass('highlight');
					if ($chk(wsub.getSibling('span'))) wsub.getSibling('span').dispose();
				}
			})}
		)});
		if (this.level == 0) this.target.addClass('highlight');
	}
	if (this.level > 0) {
		this.pop.addEvent('mouseenter',function(){
			this.getSiblings().each(function(s,i,list) {
				if (s.hasClass('poppermcpoppy')) {
					list[i - 1].addClass('highlight');
				}
			})
		},this.pop)
	}
}
//addScript example

// if ($chk($('storage-quote-yes'))) $('storage-quote-yes').addEvent('click',function(e){
// 	var sq = new PopUp(e,{ajax:'/quote/get_storage_quotes/',addScript:function(){
// 		<?if ($this->session->userdata('storage_temp')):?>
// 			var temp = 'yes';
// 		<?else:?>
// 			var temp = 'no';
// 		<?endif?>
// 		$$('tbody').each(function(tb){var starred = false;tb.getElements('td[id$=temperature]').each(function(td){if (td.get('text') == temp && !starred) {
// 			var feet = td.getParent('tr').getElement('td[id$=size]');
// 			feet.set('html','<strong>* ' + feet.get('text') + '</strong>');
// 			starred = true;
// 		}})});
// 		
// 		$('storage-submit').addEvent('click',function(e){
// 			var companies = new Array();
// 			$$('tbody').each(function(tb){tb.getElements('input[type=checkbox]').each(function(inp){if (inp.checked) {companies.push(inp.getParent('tr').id.getToken('-',3))}})});
// 			new Element('img',{'src':'/media/images/spinner.gif'}).replaces(e.getTarget());
// 			new Request({'url':'/quote/submit_storage_quote/' + ciEncode(companies),onComplete:function(){
// 				sq.close();
// 				$('you-need-storage').set('html','<br><strong>Storage Quote Submitted!</strong>');
// 			}}).send();
// 		});
// 	},addStyle:'height:500px;width:700px;background-color:' + "<?=$insetColor?>" + ';padding:40px 0 0 0;',ieStyle:'height:525px;width:725px;',allowClick:true,backgroundFade:true})
// });

//ignores leading and trailing delimiters
//3rd param is how many tokens to skip initially 
//nextToken can return multiple tokens at once, including delimiters
//new StringTokenizer("1-2-3-4",'-',2).nextToken(2) == '3-4'
//native js
function StringTokenizer(str,del,start) {	
	try {
		if (str === undefined) throw('constructor');
		if (del === undefined) del = '-';
		if (str.indexOf(del) == 0) str = str.slice(del.length);
		this.array = str.split(del);
		this.i = 0;
		this.del = del;
		if (start !== undefined) this.array = this.array.slice(start);
		this.nextToken = function(num) {
			if (num === undefined) num = 1;
			var str = '';
			do {
				str += this.array[this.i];
				if (num > 1) str += this.del;
				this.i++;
				num--;
			} while (num > 0);
			return str;
		}
		this.hasNext = function() {return this.i < this.array.length};
		this.lastToken = function(){return this.array[this.array.length - 1]}	
	}
	catch (err) {if (err == 'constructor') console.error('class StringTokenizer: constructor format is: string,delimiter,[number of skipped tokens]')}
}String.prototype.getToken = function(del,start,tokens) {if (del === undefined) del = '-';return new StringTokenizer(this,del,start).nextToken(tokens)};
String.prototype.getLastToken = function(del) {if (del === undefined) del = '-';return new StringTokenizer(this,del).lastToken()};


//Working with dropdowns a pain in the ass.  Select remedies that.
//fixes the problem with IE not implementing the disable attribute
//MooTools 1.2
function Select(id) {
	try {
		if ($(id).getTag() != 'select') throw (id);
		this.id = id;
		this.options = $A($(id).options);
		this.length = this.options.length;		
		this.removeIndex = function(i) {
			if (i == $(this.id).selectedIndex) this.setSelectedIndex(0);
			this.options.erase(this.options[i]);
			$(this.id).remove(i);
			this.length--;
		};
		this.removeValue = function(value) {this.options.each(function(option,i){if (option.value == value) this.removeIndex(i)},this);this.length--};
		this.insert = function(values,pos) {$(this.id).grab(new Element('option',values));if (pos !== undefined) this.moveOption(values.value,pos);this.length++};
		this.getSelectedIndex = function() {return $(this.id).selectedIndex};
		this.getSelectedValue = function() {return $(this.id).value};
		this.getSelectedText = function() {return $(this.options[$(this.id).selectedIndex]).get('text')};
		this.setSelectedIndex = function(i) {this.options[i].selected = true};
		this.setSelectedValue = function(value) {this.options.each(function(option,i){if (option.value == value) this.options[i].selected = true},this)};
		this.getValue = function(i) {return this.options[i].value};
		this.getIndex = function(value) {var i = -1;this.options.each(function(option,j){if (option.value == value) i = j});return i};
		this.moveOption = function(value,loc) {}; //one day
		this.disableOption = function(i,property) {
			if (i === undefined) {
				property = 'index'
				i = this.getSelectedIndex();
			}
			if (Browser.Engine.trident) {
				if (property == 'index') new Element('optgroup',{'label':this.options[i].value,'id':this.options[i].value,'style':"color:graytext;font-style:normal;padding-left:3px;background-color:none;"}).replaces(this.options[i]);
				else if (property == 'text') this.options.each(function(option){if (option.value == i) new Element('optgroup',{'label':option.value,'id':option.value,'style':"color:graytext;font-style:normal;padding-left:3px;background-color:none;"}).replaces(option)});
				else if (property == 'value') this.options.each(function(option){if (option.value == i) new Element('optgroup',{'label':option.value,'id':option.value,'style':"color:graytext;font-style:normal;padding-left:3px;background-color:none;"}).replaces(option)});			
			}
			else {
				if (property == 'index') this.options[i].setProperty('disabled','true');
				else if (property == 'text') this.options.each(function(option){if (option.value == i) option.setProperty('disabled','true')});
				else if (property == 'value') this.options.each(function(option){if (option.value == i) option.setProperty('disabled','true')});
			}
		};
		this.enableOption = function(i,property) {
			if (i === undefined) {
				property = 'index';
				i = this.getSelectedIndex();
			}
			if (Browser.Engine.trident) {
				try {
					if (property == 'index')
						$(this.id).getChildren().each(function(el,j){if (i == j && el.getTag() == 'optgroup') new Element('option',{'value':el.id,'html':el.label}).replaces($(el))});
					else if (property == 'text') {
						var optgroup = $(this.id).getElement('optgroup[label=' + i + ']');
						if (optgroup !== null) new Element('option',{'value':optgroup.id,'html':optgroup.label}).replaces($(optgroup));
					}
					else if (property == 'value') {
						var optgroup = $(i);
						if (optgroup !== null) new Element('option',{'value':optgroup.id,'html':optgroup.label}).replaces($(optgroup));
					}
				}
				catch (err) {console.error('class Select function enableOption(ie): ' + err)}
			}
			else {
				if (property == 'index') this.options[i].removeProperty('disabled');
				else if (property == 'text') this.options.each(function(option){if (option.value == i) option.removeProperty('disabled')});
				else if (property == 'value') this.options.each(function(option){if (option.value == i) option.removeProperty('disabled')});
			}
		}
	}
	catch (id) {console.error('class Select: "' + id + '" is not a SELECT object')}
}
Element.implement({
	removeIndex: function(i) {new Select(this.id).removeIndex(i)},
	removeValue: function(i) {new Select(this.id).removeValue(i)},
	insert: function(value,pos) {new Select(this.id).insert(value,pos)},
	getSelectedIndex: function() {return new Select(this.id).getSelectedIndex()},
	getSelectedText: function() {return new Select(this.id).getSelectedText()},
	getSelectedValue: function() {return new Select(this.id).getSelectedValue()},
	setSelectedIndex: function(value) {new Select(this.id).setSelectedIndex(value)},
	setSelectedValue: function(value) {new Select(this.id).setSelectedValue(value)},
	getValue: function(i) {return new Select(this.id).getValue(i)},
	getIndex: function(i) {return new Select(this.id).getIndex(i)},
	disableOption: function(i,property) {new Select(this.id).disableOption(i,property)},
	enableOption: function(i,property) {new Select(this.id).enableOption(i,property)}
});

//creates an upload form to send any kind of file to a php upload script
//php function, name of the object, the id of the div you want the uploader in, max files uploaded at once
function FileUploader(formAction,uName,div,max) {
	this.formAction = formAction;
	if (max === undefined || max == false)
		max = 5;
	this.uName = uName;
	this.max = max;
	this.div = div;
	this.num = 0;
	
	this.writeStart = writeStart;
	this.addToQueue = addToQueue;
	this.removeFromQueue = removeFromQueue;
	this.submit = submit;
	this.cancel = cancel;
	
	this.writeStart();
}

function cancel() {
	$$('.queued').each(function(e){
		this.removeFromQueue(e);
	});
}

function submit() {
	if (this.num == 0) {
		alert('Select a file to upload first!');
		return false;
	}
	return true;
}

function writeStart() {
	var content = "<form action=\"" + this.formAction + "\" method=\"post\" enctype=\"multipart/form-data\" class=\"form\" id=\"" + this.uName + "-form\">\n";
	content = content + "<input type=\"file\" size=\"20\" class=\"file\" onchange=\"" + this.uName + ".addToQueue(this);return false;\">\n";
	content = content + "<div id=\"file_queue\"></div>\n<p>";
	content = content + "<div id=\"submit-cancel\"></div></p></form>\n";
	
	$(this.div).setHTML(content);
}

function addToQueue(el) {
	if (this.num == this.max) {
		alert('Only ' + this.max + ' files can be uploaded at a time!');
		return false;
	}
	var oip = el.value;
	var tmpFilePath = $('file_queue').appendChild(document.createElement('div'));
	tmpFilePath.className = 'queued clearfix';
	tmpFilePath.id = 'file-wrap-' + this.num;
	var pathName = tmpFilePath.appendChild(document.createElement('p'));
	pathName.className = 'file-path';
	pathName.id = 'file-' + this.num;
	if(this.num > '0'){
		var doubleFile = false;
		$$('.file-path').each(function(f){
			if(f.firstChild && f.firstChild.nodeValue == oip){
				alert('This file has already been added to the queue.');
				$('userfile').value = "";
				$('file_queue').removeChild(tmpFilePath);
				doubleFile = true;
			}
		});
	}
	if (doubleFile)
		return false;
	var fileSpan = el.cloneNode();					//new inputs must be cloned apparently, not created
	fileSpan.name = 'userfile_' + this.num;
	tmpFilePath.appendChild(fileSpan);
	fileSpan.style.position = 'absolute';
	fileSpan.style.left = '-1500px';
	var titleSpan = tmpFilePath.appendChild(document.createElement('span'));
	titleSpan.className = 'field_name';
	titleSpan.id = "title-"+ this.num;
	$(titleSpan.id).setText('Title');
	var pathTitle = tmpFilePath.appendChild(document.createElement('input'));
	pathTitle.setProperties({'name':'title_userfile_'+this.num});
	var desSpan = tmpFilePath.appendChild(document.createElement('span'));
	desSpan.className = 'field_name';
	desSpan.id = "des-" + this.num;
	$(desSpan.id).setText('Description');	
	var pathDesc = tmpFilePath.appendChild(document.createElement('textarea'));
	pathDesc.setProperties({'name':'desc_userfile_'+this.num});
	$(pathName.id).setText(oip);
	var close = tmpFilePath.appendChild(document.createElement('img'));
	close.setProperties({'src':'/media/images/ui/cross.png','class':'close','onclick':this.uName + '.removeFromQueue(this.parentNode);return false;'});		
	
	this.num++;
	
	$('submit-cancel').setHTML("<input type=\"submit\" value=\"Upload File(s)\" class=\"submit\" onclick=\"return " + this.uName + ".submit();\">\n<input type=\"button\" value=\"Cancel\" class=\"submit\" onclick=\"" + this.uName + ".cancel();return false;\">\n");
	$('userfile').value = " ";
}

function removeFromQueue(el) {
	el.parentNode.removeChild(el);
	this.num--;
	if (this.num == 0)
		$('submit-cancel').setHTML("");
}


//---------------------------------------


//table num, does it need its ids cleaned
//supply clean functions somewhere else
//be sure and call updateRow() before adding cells()
function Table(num,ids) {
	this.table = document.getElementsByTagName("table")[num];
	this.rows = this.table.getElementsByTagName("tr");
	this.row = null;
	this.rowNum = 0;
	this.cellNum = 0;
	this.ids = ids;
	
	this.updateRow = updateRow;
	this.newCell = newCell;
	this.clean = clean;
	this.getCell = getCell;
	this.updateCell = updateCell;
}

function updateCell(num,content,row) {
	if (row === undefined)
		row = this.row;
	num--;
	var cells = row.getElementsByTagName("td");
	if (num > cells.length - 1)
		return -1;
	cells[num].innerHTML = content;
}

function getCell(num,row) {
	if (row === undefined)
		row = this.row;
	num--;
	var cells = row.getElementsByTagName("td");
	if (num > cells.length - 1)
		return -1;
	return cells[num].innerHTML;
}

function updateRow(num) {
	this.rowNum = num;
	this.table.deleteRow(num);
	this.row = table.insertRow(num);
	this.cellNum = 0;
}

function newCell(data) {
	if (data === undefinded)
		data = "";
	var cell = this.row.insertCell(this.cellNum);
	cell.innerHTML = data;
	this.cellNum++;
}

function clean() {
	cleanRowClasses();
	if (this.ids)
		cleanRowIds();
	this.row = null;
	this.rowNum = 0;
	this.cellNum = 0;
}


//----------------------


//submit data class
//uses ajax and json to submit large amounts of data in blocks
//if you specify onFinish as 'y', create a completeTransfer(type) function in the originating js
// function Submiter (url,uData,div,onFinish,uType,blockSize) {
// 	if (blockSize === undefined)
// 		this.blockSize = 100;
// 	else
// 		this.blockSize = blockSize;
// 	if (onFinish != 'y')
// 		onFinish = 'n';
// 		
// 	this.uploadType = uType;
// 	this.onFinish = onFinish;
// 	this.url = url;
// 	this.uploadData = uData;
// 	this.numBlocks = Math.ceil(this.uploadData.length / this.blockSize);
// 	this.div = div;
// 
// 	this.submitData = submitData;
// }
// 
// function submitData(i) {
// 	if (i === undefined)
// 		i = 0;
// 	var block = new Array();
// 	
// 	for (var s = i * this.blockSize; s < i * this.blockSize + this.blockSize; s++) {
// 		if (s >= this.uploadData.length)
// 			break;
// 		block.push(this.uploadData[s]);
// 	}
// 	
// 	var o = this;
// 	new Ajax(this.url + Json.toString(block), {
// 		method:'get',
// 		async: 'false',
// 		evalScripts: 'true',
// 		onComplete: function(){
// 			var p = Math.round(100 * (i / o.numBlocks));
// 			
// 			$(o.div).innerHTML = p + "% complete"; 
// 						
// 			if (i * o.blockSize + o.blockSize > o.uploadData.length) {
// 				if (o.onFinish == 'y') {
// 					$(o.div).innerHTML = "100% complete!";
// 					completeTransfer(o.uploadType);
// 				}
// 				else
// 					return;
// 			}
// 			else
// 				o.submitData(i + 1);
// 		}
// 	}).request();
// }

//mootools 1.2 version
//can do arrays, objects, and strings, cleaner and faster and more robust
//takes an object as a parameter
function Submitter (options){try {
	if ($chk(options.blockSize)) this.blockSize = options.blockSize;
	else this.blockSize == 100;
	if ($chk(options.onComplete)) this.onComplete = options.onComplete;
	else this.onComplete = false;
	if ($chk(options.url)) this.url = options.url;
	else throw ("class Submitter: constructor: 'url' must be provided.");
	if (this.url.charAt(this.url.length - 1) != '/') this.url += '/';
	if ($chk(options.type)) this.uploadType = options.type;
	else this.uploadType = false;
	if ($chk(options.data)) this.uploadData = options.data;
	else throw ("class Submitter: constructor: 'data' must be provided.");
	if (options.encoding == 'uri' || options.encoding == 'extra') this.encoding = options.encoding;
	else this.encoding = 'none';
	if ($chk(options.progress)) {
		if (options.progress.loading == 'percentage') this.loading = 'percentage';
		else this.loading = 'spinner';
		if (!$chk(options.progress.where)) throw('class Submitter: constructor: "progress.where" must be provided.');
		else this.div = options.progress.where;
		if ($chk(options.progress.message)) {
			this.message = options.progress.message;
			if ($type(this.message) == 'string') this.messageType = 'string';
			else if ($type(this.message) == 'element') this.messageType = 'element';
		}
		else {
			this.message = "Loading Complete!";
			this.messageType = 'string';
		}
	}
	else {
		this.loading = false;
		this.div = false;
		this.message = false;
	}
	this.blocks = new Array();		
	if ($type(this.uploadData) == 'array') this.blocks = this.uploadData;
	else if ($type(this.uploadData) == 'string') {
		var j = 0;
		while (j < this.uploadData.length) {
			block.push(this.uploadData.substr(j,this.blockSize));
			j += this.blockSize;
		}
	}
	else if ($type(this.uploadData == 'object')) for (var key in this.uploadData) {
		var obj = new Object();
		obj[key] = this.uploadData[key]
		this.blocks.push(obj);
	}
	this.send = function(i) {
		if (!$chk(i)) i = 0;
		if (this.loading == 'spinner' && i == 0) $(this.div).grab(new Element('img',{'src':'/media/images/spinner.gif'}));
		var data = JSON.encode(this.blocks[i]);
		data = data.replaceAll('(','').replaceAll(') ','-').replaceAll('*',''); //MOVINGWORLD ONLY UGH
		if (this.encoding == 'uri') data = encodeURIComponent(data);
		if (this.encoding == 'extra') data = encodeURIComponent(data).replaceAll('%','qqz');
		new Request({url:this.url + data, onComplete: function(){
			var p = Math.round(100 * (i / this.numBlocks));
			if (this.loading == 'percentage') $(this.div).set('html',p + "% complete"); 
			if (i == this.blocks.length - 1) {
				if (this.message) {
					if (this.messageType == 'string') $(this.div).set('html',this.message);
					else if (this.messageType == 'element') {
						$(this.div).empty();
						$(this.div).grab(this.message);
					}
				} 
				if (this.onComplete) this.onComplete.attempt(this.uploadType);				
			}
			else this.send(i + 1);
		}.bind(this)}).send();
	};
} catch(err) {
	if($type(err) != 'string') new ErrorReport(new Hash({message:'class Submitter: ' + err.message}));
	else new ErrorReport(err);
}}

//------------------------------------

//page select class
//works in conjunction with BgLoader objects
//doesnt handle any actual data, just links and page numbers
function PageSelect (size,maxPages,pages,type,div){
	if (size == 'huge') {
		this.big = true;
		this.huge = true;
	}
	else if (size == 'big') {
		this.big = true;
		this.huge = false;
	}
	else {
		this.big = false;
		this.huge = false;
	}
	this.maxPages = maxPages;
	this.pages = pages;
	this.type = type;
	this.div = div;
	this.currentPage = 1;
	
	//following 3 used with a BgLoader
	this.isComplete = 'unset';
	this.temp = 0;
	this.staticVal = 1;

	this.writePage = writePage;
	this.selectPage = selectPage;
}

function selectPage(pageNum,iC) {
	if (iC !== undefined) {
		this.isComplete = iC;
	}
	
	if (!this.isComplete) {
		this.temp = pageNum;
		pageNum = this.staticVal;
	}
	
	if (pageNum >= this.pages && iC)
		pageNum = this.staticVal;
		
	this.currentPage = pageNum;
	
	var pre = new Array();
	var suf = new Array();
	
	if (this.pages > 1) {
		if (this.pages >= this.maxPages) 
			var step = Math.floor(this.pages / (this.maxPages - 4));
		if (this.pages > 114) {
			var f = 0;
			var r = 7;
			var t = 0;
		}
	
		var po = Math.floor(this.maxPages * (pageNum / this.pages));
		if (pageNum <= 4)
			po = pageNum;
		else if (pageNum >= this.pages - 4)
			po = this.pages - pageNum + 1;
		else 
			po = 9;
		
		if (po >= pageNum)
			po = pageNum;
		else if (this.maxPages - po >= this.pages - pageNum)
			po = this.maxPages - (this.pages - pageNum);

		p = po;
		var i = pageNum + 1;
		while (p < this.maxPages - 1 && i < this.pages) {
			this.writePage(i,pageNum,this.pages,suf);
			if (this.big && i >= pageNum + 3) {
				if (this.huge) {
					if (t == 0)
						var s = r * Math.pow(2,f);
					if (t == 0 && (this.pages - i) / (this.maxPages - p - 1) < s)
						t = Math.floor((this.pages - i) / (this.maxPages - p - 1));
					if (t != 0)
						i += t;
					else {
						if (f == 0)
							i -= 3;
						i += s;
					}
					f++;
				}
				else
					i += step;
			}
			else
				i++;
			p++;
		}
		this.writePage(this.pages,pageNum,this.pages,suf);

		if (this.pages > 114) {
			var f = 0;
			var t = 0;
		}

		p = po - 1;
		i = pageNum - 1;
		while (p > 1 && i > 1) {
			this.writePage(i,pageNum,this.pages,pre);
			if (this.big && i <= pageNum - 3) {
				if (this.huge) {
					if (t == 0)
						var s = r * Math.pow(2,f);
					if (t == 0 && i / (p - 1) < s)
						t = Math.ceil(i / (p - 1));
					if (t != 0)
						i -= t;
					else {
						if (f == 0)
							i += 3;
						i -= s;
					}
					f++;
				}
				else
					i -= step;
			}
			else
				i--;
			p--;
		}
		this.writePage(1,pageNum,this.pages,pre);
	
		pre.reverse();
		if (pageNum != 1 && pageNum != this.pages) {
			pre.push("&nbsp;");
			pre.push(pageNum);
		}

		content = "";
		for (var i = 0; i < pre.length; i++)
			content = content + pre[i] + '\n';
		for (var i = 0; i < suf.length; i++)
			content = content + suf[i] + '\n';

		$(this.div).innerHTML = content;
	}
}

//pageNum is being pushed
//current is the current page or last loaded
function writePage(pageNum,current,pages,array) {
	var c = this.type.charAt(0);
	
	if (!this.isComplete) {
		current = this.temp;
		if (current == pages) {
			array.push("Last");
			return;
		}
		else if (pageNum > current) {
			array.push(pageNum);
			return;
		}
	}
	if (pageNum == 1) {
		if (current == 1) {
			array.push("First");
			return;
		}
		var id = "id=\"" + this.type + "-first\"";
		var name = "First";
	}
	else if (pageNum == pages) {
		if (current == pages) {
			array.push("Last");
			return;
		}
		var id = "id=\"" + this.type + "-last\"";
		var name = "Last";
	}
	else {
		var id = "id=\"" + c + "p-" + pageNum + "\"";
		var name = pageNum;
	}
	
	var content = "&nbsp;<a " + id + " href=\"#\" onclick=\"" + this.type + "Page(" + pageNum + "," + pages + ");return false\">" + name + "</a>";
	array.push(content);
}

//-------------------------------------------------------

//background loader class
//pulls data in blocks from the db in the background
//creates an array of arrays of db pulls
//works with PageSelect object
//create BgLoader objects in the order you want them to run in (if they run at the same time its 3 times slower)
//use the completed(flag) function
//can use the optionalFunction(flag) function after each block is loaded
//on the last block, the optionFunction then the Completed function run
//requires mootools

//size of the data,size of each block, php to pull it from, function flag, div to put % loaded in, type of data,pageSelect obj name, pages per block, whether to call an intermediate function, extra param passed to the php (name or id probably)
function BgLoader(size,blockSize,phpForm,flag,loadDiv,type,pageSelect,ppb,optFunc,extra) {	
	if (pageSelect === undefined) {
		this.pageSelect = false;
		this.ppb = false;
	}
	else {
		this.pageSelect = pageSelect;
		this.ppb = ppb;
	}
	if (loadDiv === undefined)
		this.loadDiv = false;
	if (optFunc === undefined)
		this.optFunc = false;
	if (extra === undefined)
		this.extra = false;
	this.extra = extra;
	this.optFunc = optFunc;
	this.flag = flag;
	this.size = size;
	this.type = type;
	this.blockSize = blockSize;
	this.numBlocks = Math.ceil(size / blockSize);
	this.loadedBlocks = 0;
	this.phpForm = phpForm;
	this.loadDiv = loadDiv;
	this.isComplete = false;
	this.blocks = new Array();
	this.processed = new Array();
	this.start = 1;
	this.desist = false;

	this.pullFirst = pullFirst;
	this.pullBlocks = pullBlocks;
	this.jsObjToArray = jsObjToArray;
	this.begin = begin;
	this.hold = hold;
	this.pullNext = pullNext;
}

function pullNext() {
	if (this.numBlocks == 0) {
		this.isComplete = true;
		if (this.pageSelect)
			this.pageSelect.selectPage(this.loadedBlocks * this.ppb,this.isComplete);
		completed(this.flag);
		return;
	}
	var o = this;
	this.loadedBlocks++;
	this.start++;
	this.desist = true;
	var page = this.start - 1;
	new Ajax(this.phpForm + page + "/" + this.blockSize + "/" + this.extra, {
		method:'get',
		async: 'true',
		evalScripts: 'true',
		onSuccess: function(res){	
			o.blocks.push(jsObjToArray(Json.evaluate(res)));
		},
		onComplete: function() {
			if (o.numBlocks == 1 || o.loadedBlocks == o.numBlocks) {
				o.isComplete = true;
				if (o.pageSelect)
					o.pageSelect.selectPage(o.loadedBlocks * o.ppb,o.isComplete);
			}
			if (o.optFunc)
				optionalFunction(o.flag);
			if (o.isComplete)
				completed(o.flag);
		}
	}).request();

	this.loadedBlocks = o.loadedBlocks;
	this.blocks = o.blocks;
	this.isComplete = o.isComplete;
	// i dont think this stuff is necessary ^^^ test after rollcall crap is done
}

function pullFirst() {
	if (this.numBlocks == 0) {
		this.isComplete = true;
		if (this.pageSelect)
			this.pageSelect.selectPage(this.loadedBlocks * this.ppb,this.isComplete);
		completed(this.flag);
		return;
	}
	var o = this;
	this.loadedBlocks++;
	this.start++;
	this.desist = true;
	new Ajax(this.phpForm + "1/" + this.blockSize + "/" + this.extra, {
		method:'get',
		async: 'true',
		evalScripts: 'true',
		onSuccess: function(res){	
			o.blocks.push(jsObjToArray(Json.evaluate(res)));
		},
		onComplete: function() {
			if (o.numBlocks == 1) {
				o.isComplete = true;
				if (o.pageSelect)
					o.pageSelect.selectPage(o.loadedBlocks * o.ppb,o.isComplete);
			}
			if (o.optFunc)
				optionalFunction(o.flag);
			if (o.isComplete)
				completed(o.flag);
		}
	}).request();

	this.loadedBlocks = o.loadedBlocks;
	this.blocks = o.blocks;
}
			
function pullBlocks(blockNum) {
	if (this.numBlocks == 1)
		this.pullFirst();
	if (this.desist || this.numBlocks == 0) {
		if (this.numBlocks == 0) {
			this.isComplete = true;
			completed(this.flag);
		}
		if (this.pageSelect)
			this.pageSelect.selectPage(this.loadedBlocks * this.ppb,this.isComplete);
		return;
	}
	if (!this.isComplete) {
		var o = this;
		this.loadedBlocks++;
		new Ajax(this.phpForm + blockNum + "/" + this.blockSize + "/" + this.extra, {
			method:'get',
			async: 'false',
			evalScripts: 'true',
			onSuccess: function(res){	
				o.blocks.push(jsObjToArray(Json.evaluate(res)));
			},
			onComplete: function() {
				if (o.loadDiv) {
					var loaded = Math.round(100 * ((blockNum + 1) / o.numBlocks));
					var current = $(o.loadDiv).innerHTML;
					if (current.indexOf("%") != -1) {
						var load = parseInt(current.substring(current.indexOf(":") + 2,current.indexOf("%")));
						if (loaded > load && loaded <= 100)
							$(o.loadDiv).innerHTML = "Loading " + o.type + " table: " + loaded + "% complete";
						if (loaded >= 100)
							$(o.loadDiv).innerHTML = "Loading complete!";
					}
					else if (current.indexOf("!") == -1 || blockNum == o.start)
						$(o.loadDiv).innerHTML = "Loading " + o.type + " table: 0% complete";
				}
				if (o.optFunc)
					optionalFunction(o.flag);
						
				if (o.loadedBlocks < o.numBlocks) 
					o.pullBlocks(o.loadedBlocks + 1);
				else
					o.isComplete = true;
				
				if (o.pageSelect)
					o.pageSelect.selectPage(o.loadedBlocks * o.ppb,o.isComplete);
				
				if (o.isComplete)
					completed(o.flag);
			}
		}).request();

		this.loadedBlocks = o.loadedBlocks;
		this.blocks = o.blocks;
		if (o.pageSelect)
			o.pageSelect.selectPage(o.loadedBlocks * o.ppb,true);
	}
	else {
		if (this.pageSelect)
			this.pageSelect.selectPage(this.loadedBlocks * this.ppb,true);
		if (this.loadDiv) 
			$(this.loadDiv).innerHTML = "Loading complete!";
		completed(this.flag);
	}
}

function hold() {
	this.desist = true;
}

//CANNOT have commas as part of the string
function jsObjToArray(obj) {
	var str = obj.toString();
	var data = new Array();
	var a = 0;
	var b = str.indexOf(",");
	while (a < str.length) {
		data.push(str.substring(a,b));
		a = b + 1;
		b = str.indexOf(",",a);	
		if (b == -1)
			b = str.length;
	}
	return data;
}

function begin() {
	this.desist = false;
	this.pullBlocks(this.start);
}

//------------------------------------
//Utility functions below...

function clone(myObj){
	if(typeof(myObj) != 'object') return myObj;
	if(myObj == null) return myObj;
	var myNewObj = new Object();
	for(var i in myObj)
		myNewObj[i] = clone(myObj[i]);
	return myNewObj;
}

function roundTo(num,r,dir) {
	var val = parseInt(num / r) * r;
	if (dir == 'up' && val < num) return val + r; 
	else if (dir == 'down' && val > num) return val - r;
	return val;
}

function givePercentageRange(num,percent,symbol) {
	if (!$chk(num)) return '';
	if (symbol === undefined) symbol = "";
	var lower = Math.floor(num - num * percent);
	var higher = Math.floor(num + num * percent);
	if (lower == 0 && higher == 0) return 'Less than $50';
	lower = roundTo(lower,5,'up');
	higher = roundTo(higher,5,'up');
	if (lower == higher) higher += 5;
	return symbol + lower + " - " + symbol + higher;
}

//returns an array with the low and high bound from the string that 'givePercentageRange()' generates
function lowHigh(id) {
	if ($(id).get('html') == 'No Cost' || $(id).get('html') == 'Less than $50') return [0,0];
	var tokens = new StringTokenizer($(id).get('html'),' - ');
	return [tokens.nextToken().slice(1),tokens.nextToken().slice(1)];
}

function rangeToRequest(str,hasSymbol) {
	if (hasSymbol) while(str.indexOf('$') > -1) str = str.replace('$','');
	return str.replace(' - ','/');
}

function add(a,b) {
	if (b !== undefined) return parseInt(a) + parseInt(b);
	var sum = 0;
	a.each(function(b) {sum = parseInt(sum) + parseInt(b)});
	return sum;
}

function popUp(url,name,height,width) {
	name = (name === undefined?'name':name);
	height = (height === undefined?500:height);
	width = (width === undefined?200:width);
	var newwindow = window.open(url,name,'height=' + height + ',width=' + width);
	if (window.focus) {newwindow.focus()}
	return false;
}

function decimalBinary(num,max) {
    var b = new Array();
    for (var n = max - 1; n >= 0; n--) {
        if (pow(2,n) <= num) {
            b.push(1);
            num -= pow(2,n);
        }
        else b.push(0);
    }
    return b.reverse();
}

//---------------------------------

Hash.implement({
	//property,numeric/alpha,asc/desc
	sort: function(sortBy,type,dir) {
		type = defaultFor(type,'num');
		dir = defaultFor(dir,'asc');
		var newList = new Hash();
		var toSort = new Array();
		var propId = new Object();
		this.each(function(item){
			toSort.push(item[sortBy]);
			if (!$chk(propId[item[sortBy]])) propId[item[sortBy]] = new Array();
			propId[item[sortBy]].push(item.id);
		});
		if (type.contains('num')) toSort = toSort.sort(sortNumber);
		else toSort = toSort.sort();
		if (dir == 'asc' || dir == 'ascending') toSort = toSort.reverse();
		toSort.each(function(prop){propId[prop].each(function(id){newList.set(id,this.get(id))},this)},this);
		return newList;
	}
});

Event.implement({
	getTarget: function() {return this.target}
});

Element.implement({
	//general
	removeStyle: function(style) {this.setStyle(style,null)},
	removeStyles: function(styles) {$splat(styles).each(function(style){this.removeStyle(style)}.bind(this))},
	hasChildren: function() {return (this.getChildren().length > 0)},
	getSibling: function(type) {return this.getParent().getElement(type)},
	getSiblings: function() {return this.getParent().getChildren()},
	isEqual: function(el) {
		var objects = new Array('object','element','arguments','collection');
		if (typeof el == 'string') el = $(el);
		for (var property in this) if (el[property] != this[property]) return false;
		return true;
	},
	getTag: function() {return this.tagName.toLowerCase()},
	hasTag: function(tag) {return (this.tagName.toLowerCase() == tag.toLowerCase())},
	//display
	hide: function(option) {
		if ($chk(option) && option == 'keep') this.setStyle('opacity',0);
		else this.addClass('hidden');
	},
	show: function() {
		this.removeClass('off-left');
		this.removeClass('hidden');
		this.removeClass('off-left2');
		this.removeClass('hide');
		if (this.getStyle('display') == 'none') this.removeStyle('display');
		if (this.style.opacity < 1 || (this.style.opacity > 1 && this.style.opacity < 10)) this.removeStyle('opacity');
	},
	toggle: function(speed,func) {
		if (this.visible()) this.fadeOut(speed,func);
		else this.fadeIn(speed,func);
	},
	displayToggle: function() {
		if (this.getStyle('display') == 'none') this.setStyle('display','block');
		else this.setStyle('display','none');
	},
	visible: function(cssClass) {
		if ($chk(cssClass)) return this.hasClass(cssClass);
		else if (this.hasClass('off-left') || this.hasClass('off-left2') || this.hasClass('hide') || this.hasClass('hidden') || this.getStyle('display') == 'none' || this.getStyle('opacity') == 0) return false;
	//	return this.faded();
		return true;
	},
	fadeIn: function(speed,onComplete) {
		var skip = false;
		if (this.visible()) skip = true;
		this.show();
		if (speed === undefined) speed = 'short';
		if (speed == 'now' || skip) {
			if ($chk(onComplete)) onComplete.attempt();
		}
		else new Fx.Morph(this,{duration:speed,transition:Fx.Transitions.Sine.easeOut,onComplete:function(){if ($chk(onComplete)) onComplete.attempt()}}).start({'opacity':[0,1]});
	},
	fadeOut: function(speed,func) {
		var skip = false;
		if (!this.visible()) skip = true;
		if (speed === undefined) speed = 'short';
		if (speed == 'now' || skip) {
			this.hide();
			if ($chk(onComplete)) onComplete.attempt();
		}
		else new Fx.Morph(this,{duration:speed,transition:Fx.Transitions.Sine.easeOut,onComplete:function(){	
			this.hide();
			if ($chk(func)) func.attempt();
		}.bind(this)}).start({'opacity':[1,0]});
	},
	fadeInOut: function(speed,pause,func) {
		if (speed === undefined) speed = 'long';
		if (pause === undefined) pause = 750;
		if (!this.visible()) {
			this.show();
			new Fx.Morph(this,{duration:speed,transition:Fx.Transitions.Sine.easeOut,onComplete:function(){this.fadeOut.delay(pause,this,[speed,func])}.bind(this)}).start({'opacity':[0,1]});
		}
		else this.fadeOut.delay(pause,this,[speed,func]);
	},
	faded: function(percent) {
		if (percent === undefined) percent = 50;
		return (this.style.opacity != percent && this.style.opacity != percent / 100);
	},
	grayIn: function(speed,onComplete) {
		if (speed === undefined) speed = 'normal';
		if (speed == 'now') new Fx.Morph(this).set({'opacity':1});
		if (this.style.opacity < 1 || (this.style.opacity > 1 && this.style.opacity < 10) || !this.visible()) {
			this.show();
			new Fx.Morph(this,{duration:speed,transition:Fx.Transitions.Sine.easeOut,onComplete:function(){if (onComplete !== undefined) onComplete.attempt()}}).start({'opacity':[.5,1]});
		}
		else if (onComplete !== undefined) onComplete.attempt();
		this.getClickables().each(function(el){el.allowEvents()});
		this.allowEvents();
	},
	grayOut: function(speed,onComplete) {
		if (speed === undefined) speed = 'normal';
		if (speed == 'now') new Fx.Morph(this).set({'opacity':0.5});
		if (this.style.opacity == 0.5 || this.style.opacity == 5) {if (onComplete !== undefined) onComplete.attempt()}
		else new Fx.Morph(this,{duration:speed,transition:Fx.Transitions.Sine.easeOut,onComplete:function(){if ($chk(onComplete)) onComplete.attempt()}}).start({'opacity':[1,.5]});
		this.getClickables().each(function(el){el.suppressEvents()});
		this.suppressEvents();
	},
	flashBackground: function(start,finish,speed) {
		if (!$chk(speed)) speed = 'normal';
		new Fx.Morph(this,{duration:speed,transition:Fx.Transitions.Sine.easeOut,onComplete:function(){this.setStyle('background-color',null)}.bind(this)}).start({'background-color':[start,finish]});
	},
	//forms
	addShadow: function() {
		var coordinates = this.getCoordinates();
		if (this.getStyle('margin-left') == 'auto') var l = 0;
		else var l = this.getStyle('margin-left').removeAll('px').toInt();
		if (this.getStyle('margin-right') == 'auto') var r = 0;
		else var r = this.getStyle('margin-right').removeAll('px').toInt();
		if (this.getStyle('margin-top') == 'auto') var u = 0;
		else var u = this.getStyle('margin-top').removeAll('px').toInt();
		if (this.getStyle('margin-bottom') == 'auto') var d = 0;
		else var d = this.getStyle('margin-bottom').removeAll('px').toInt();
		// u-=2;
		// r+=2;
		// d+=2;
		// l-=2;	
		var w = this.clientWidth + 4;
		var h = this.clientHeight + 4;
		// var y = coordinates.top + 2;
		// var x = coordinates.left + 2;
		// if ($chk(options)) {
		// 	if (!Browser.Engine.trident) {
		// 		if ($chk(options.coords)) {
		// 			y = options.coords.top;
		// 			x = options.coords.left;
		// 		}
		// 		if ($chk(options.dims)) {
		// 			if ($chk(options.dims.width)) w = options.dims.width;
		// 			if ($chk(options.dims.height)) h = options.dims.height;
		// 		}
		// 	}
		// 	else {
		// 		if ($chk(options.iecoords)) {
		// 			y = options.iecoords.top;
		// 			x = options.iecoords.left;
		// 		}
		// 		if ($chk(options.iedims)) {
		// 			if ($chk(options.iedims.width)) w = options.iedims.width;
		// 			if ($chk(options.iedims.height)) h = options.iedims.height;
		// 		}
		// 	}
		// }
		//this.setStyles({'position':'relative','z-index':'300'});
	//	new Element('div',{'rel':'shadow','style':'background:#888;width:' + w + ';height:' + h + ';position:absolute;top:' + y + ';left:' + x + ';z-index:200'}).inject(this,'after');
		
		// var shadow = new Element('div',{'style':'width:' + w + ';height:' + h + ';background:#888;margin:auto;display:block'}).inject(this,'after').grab(this);
		// shadow.setStyle('margin','margin:2px -2px -2px 2px');
		// this.setStyle('margin',u + 'px ' + r + 'px ' + d + 'px ' + l + 'px');
		
		this.addEvent('mouseenter',function(e){this.setStyle('margin',parseInt(u + 1) + 'px ' + parseInt(r - 1) + 'px ' + parseInt(d - 1) + 'px ' + parseInt(l + 1) + 'px')});			
		this.addEvent('mouseleave',function(e){this.setStyle('margin',u + 'px ' + r + 'px ' + d + 'px ' + l + 'px')});
		this.addEvent('mousedown',function(e){this.setStyle('margin',parseInt(u + 2) + 'px ' + parseInt(r - 2) + 'px ' + parseInt(d - 2) + 'px ' + parseInt(l + 2) + 'px')});
		this.addEvent('mouseup',function(e){this.setStyle('margin',parseInt(u + 1) + 'px ' + parseInt(r - 1) + 'px ' + parseInt(d - 1) + 'px ' + parseInt(l + 1) + 'px')});
	},
	removeShadow: function() {
		if (!$chk(this.getSibling('div[rel=shadow]'))) return false;
		this.getSibling('div[rel=shadow]').dispose();
		this.removeStyles(['position','z-index']);
	},
	emptyField: function() {return (this.value === null || this.value.trim().length == 0)},
	suppressEvents: function() {this.setProperty('disabled','true')},
	allowEvents: function() {this.erase('disabled')},
	getClickables: function() {
		return recurse(new Array(),this);
		function recurse(array,e) {
			if ((e.hasTag('input') && e.getProperty('type') != 'hidden') || e.hasTag('select') || e.hasTag('textarea') || e.hasTag('a')) array.push(e);
			else if (e.hasChildren() && e.visible()) e.getChildren().each(function(el){recurse(array,el)});
			return array;
		}
	},
	getFormElements: function(hidden) {
		if (!$chk(hidden)) hidden = false;
		return recurse(new Array(),this);
		function recurse(array,e) {
			if (hidden) {
				if (e.isFormElement()) array.push(e);
				else if (e.hasChildren()) e.getChildren().each(function(el){recurse(array,el)});
			}
			else {
				if ((e.hasTag('input') && e.getProperty('type') != 'hidden') || e.hasTag('select') || e.hasTag('textarea')) array.push(e);
				else if (e.hasChildren() && e.visible()) e.getChildren().each(function(el){recurse(array,el)});
			}
			return array;
		}
	},
	setTabbing: function() { //tabindex skips selects for some reason-- they are being assinged by this function correctly
		this.getFormElements().each(function(el,i){el.setProperty('tabindex',i + 1)});
	},
	isFormElement: function() {
		return (this.hasTag('input') || this.hasTag('select') || this.hasTag('textarea'))
	},
	setFormValidation: function() {
		this.getFormElements(true).each(function(el){el.addEvents({'blur':input.pass(el),'change':input.pass(el)})});
		function input(el) {
			el = $(el);
			var id = el.id;
			if (id.contains('email') && !validateEmail(el.value)) return false;
			//if (id.isADate() && !validateDate(el.value)) return false;
			if (id.contains('zip') && !el.value.validateZipCode()) return false;
			if (el.emptyField()) return false;	
			var adjacent = el;
			if (el.getParent('span') !== null && el.getParent('span').hasClass('required')) adjacent = el.getParent('span');	
			adjacent.getSiblings().each(function(el,i,list){
				var div = false;
				if (el == adjacent) 
					for (var j = i - 1; j > -1 && !list[j].isFormElement(); j--) 
						if (list[j].hasClass('error') && list[j].getProperty('style') == null) 
							div = list[j];				
				if (div && div.visible()) new Fx.Morph(div,{duration:'short',transition:Fx.Transitions.Sine.easeOut}).start({'opacity':[1,0]})				
			});
			//special cases
			if (el.getParent('div').hasClass('storage-option')) el.getParent().getSibling('div').getChildren().each(function(el,i){
				if (id.contains('feet') && i == 0 && el.getProperty('style') == null) new Fx.Morph(el,{duration:'short',transition:Fx.Transitions.Sine.easeOut}).start({'opacity':[1,0]})
				if (id.contains('full') && i == 1 && el.getProperty('style') == null) new Fx.Morph(el,{duration:'short',transition:Fx.Transitions.Sine.easeOut}).start({'opacity':[1,0]})
			});
		}
	},
	cleanse: function(ceiling,zero,extra) {
		extra = defaultFor(extra,'');
		var i = this.value.length;
		while (i--) if (!this.value.charAt(i).numeric() && !extra.contains(this.value.charAt(i))) this.value = this.value.substr(0,i) + this.value.substr(i + 1);
		if ($chk(ceiling) && ceiling.toInt() > 0 && this.value.toInt() > ceiling.toInt()) this.value = ceiling;
		if ($chk(zero) && this.value == '') this.value = 0;
		//if (this.value != '') this.value = this.value.toInt();
	},
	setCleanse: function(ceiling,zero,extra) {this.addEvent('keyup',function(){this.cleanse(ceiling,zero,extra)},this);this.addEvent('blur',function(){this.cleanse(ceiling,zero,extra)},this)},
	toggleCheckbox: function() {
		if (this.checked) this.checked = false;
		else this.checked = true;
	},
	validateZipCode: function() {
		if (this.value.length != 5 && this.value.length != 9 || !this.value.numeric()) return false;
		var re = /^((0-9)-)/
		return !re.test(this.value);
	},
	//tables
	checkAll: function() {this.getElements('input[type=checkbox]').each(function(i){i.checked = true})},
	checkNone: function() {this.getElements('input[type=checkbox]').each(function(i){i.checked = false})},
	checkOpposite: function() {this.getElements('input[type=checkbox]').each(function(i){
		if (i.checked) i.checked = false;
		else i.checked = true
	})},
	alternateRowStyles: function(a,b) {
		var c = this;
		if ($chk(this.getElement('tbody'))) c = this.getElement('tbody');
		c.getElements('tr').each(function(tr,i) {
			if (i % 2 == 0) tr.setStyle('background-color',a);
			else tr.setStyle('background-color',b);
		});
	},
	alternateRowClasses: function(a,b) {
		var c = this;
		if ($chk(this.getElement('tbody'))) c = this.getElement('tbody');
		c.getElements('tr').each(function(tr,i) {
			if (i % 2 == 0) tr.addClass(a);
			else tr.addClass(b);
		});
	}
});

String.implement({
	reverse: function() {
		var i = this.length;
		var str = '';
		while (--i) str += this.charAt(i);
		return str;
	},
	endsWith: function(str) {
		return this.reverse().startsWith(str.reverse());
	},
	removeLast: function(str) {
		if (this.endsWith(str)) {
			s = this.trim();
			return s.substring(0,s.length - str.trim().length);
		}
		else return this;
	},
	startsWith: function(str) {
		if (this.trim().indexOf(str.trim()) == 0) return true;
		return false;
	},
	removeFirst: function(str) {
		if (this.startsWith(str)) return this.trim().replace(str.trim(),'');
		else return this;
	},
	underscore: function() {
		var str = this;
		while(str.indexOf(' ') > -1) str = str.replace(' ','_');
		return str.toLowerCase(); 
	},
	humanize: function() {
		var caps = "";
		var tokens = new StringTokenizer(this.underscore(),'_');
		while (tokens.hasNext()) {
			var word = tokens.nextToken();
			caps = caps + word.charAt(0).toUpperCase() + word.slice(1) + " ";
		}
		return caps.substring(0,caps.length - 1);
	},
	isADate: function() {
		if (this.contains('date-spec') || this.contains('date-specific')) return true;
		return false;
	},
	replaceAll: function(old,newC) {
		var str = this;
		while (str.contains(old)) str = str.replace(old,newC);
		return str;
	},
	removeAll: function(s) {return this.replaceAll(s,'')},
	parseString: function(del,step) {
		var str = '';
		for (var i = 1; i <= this.length; i++) {
			str += this.charAt(i - 1);
			if (i % step == 0 && i != this.length) str += del;
		}
		return str;
	},
	//validation
	validateEmail: function() {
		var emailRe = /^\w+([\.-]?\w+)*@\w+([\.-]?\w+)*\.(\w{2}|(com|net|org|edu|int|mil|gov|arpa|biz|aero|name|coop|info|pro|museum))$/
		//phoneRe = /^((\+\d{1,3}(-| )?\(?\d\)?(-| )?\d{1,5})|(\(?\d{2,6}\)?))(-| )?(\d{3,4})(-| )?(\d{4})(( x| ext)\d{1,5}){0,1}$/
		 return emailRe.test(this);
	},
	alpha: function() {return !(/[^a-zA-Z]/.test(this))},
	alphaNumeric: function() {
		var re = /[^0-9a-zA-Z-]/
		return !re.test(this);
	},
	numeric: function() {return /[0-9]/.test(this)},
	validateZipCode: function() {
		if (this.length != 5 && this.length != 9) return false;
		var re = /^((0-9)-)/
		return !re.test(this);
	},
	completeEnconde:function() {
		return escape(this.replaceAll('=',' ').replaceAll('+',' ').replaceAll('*',' ').replaceAll('-',' ').replaceAll('.',' ').replaceAll('/',' ').replaceAll('\\',' ').replaceAll(';',' ')).trim()
	},
	ciStringEncode:function() {return encodeURIComponent(this.trim()).replaceAll('%','qqz')}
});

function ciEncode(d) {return encodeURIComponent(JSON.encode(d)).replaceAll('%','qqz')}

function defaultFor(p,d) {
	if (!$chk(p)) p = d;
	return p;
}

var toggleCheckbox = function(id) {
	if ($(id).checked) $(id).checked = false;
	else $(id).checked = true;
};

function an(string) {
	var vowels = 'aeiou';
	var l = 'a';
	if (vowels.contains(string[0])) l = 'an';
	return l + ' ' + string;
}

function IFrame(parentElement,atr) {  
	var iframe = new Element("iframe");  
	if(parentElement == null) parentElement = document.body;  
	parentElement.grab(iframe);   
	iframe.doc = null;   
	if(iframe.contentDocument) iframe.doc = iframe.contentDocument;  
	else if(iframe.contentWindow) iframe.doc = iframe.contentWindow.document;  
	else if(iframe.document) iframe.doc = iframe.document;  
	iframe.doc.open(); 
	if ($chk(atr.classes)) {
		var path = atr.classes[0];
		if (path.charAt(0) != '/') path = '/' + path;
		if (path.charAt(path.length - 1) != '/') path += '/';
		atr.classes.each(function(v,i) {if (i > 0) iframe.doc.write('<link href=".' + path + v + '.css" rel="stylesheet" media="screen" type="text/css">')});			
	}
	if ($chk(atr.styles)) {
		var path = atr.styles[0];
		if (path.charAt(0) != '/') path = '/' + path;
		if (path.charAt(path.length - 1) != '/') path += '/';
		atr.styles.each(function(v,i) {if (i > 0) iframe.doc.write('<link href="' + path + v + '.js" type="text/javascript">')});
	}
	iframe.doc.close();  
	if ($chk(atr.properties)) for (var key in atr.properties) iframe[key] = atr.properties[key];		
	return iframe;  // Return the iframe, now with an extra property iframe.doc 
}

function getFunctionName(theFunction) {if (theFunction.name) return theFunction.name; return 'function'}
function getSignature(theFunction) { 
	var signature = getFunctionName(theFunction) + "("; 
	for(var x = 0; x < theFunction.arguments.length; x++) { 
		var nextArgument = theFunction.arguments[x]; 
		if ($type(nextArgument) == 'event') nextArgument = '"on' + nextArgument.type + '" event';
		signature += "'" + nextArgument + "'"; 
		if (x < theFunction.arguments.length - 1) signature += ", "; 
	} 
	return signature + ")";
} 
function stackTrace(startingPoint,toString) { 
	var stackTraceMessage = "Stack trace: \n"; 
	var nextCaller = startingPoint; 
	while(nextCaller) { 
		stackTraceMessage += getSignature(nextCaller) + "\n"; 
		nextCaller = nextCaller.caller; 
	}
	if ($chk(toString)) return stackTraceMessage; 
	else if (Browser.Engine.gecko) console.warn(stackTraceMessage); 
	else alert(stackTraceMessage);
}

function multiCorner(array,options){try {
	if ($type(options) != 'object' && $chk(options)) throw('function multiCorner: argument must be single parameter or an array, then an optional options object.');
	$splat(array).each(function(div) {
		if ($(div) == null) throw('function multiCorner: "' + div + '" is not in the dom.');
		else $(div).corner(options);
	});
}catch(err){new ErrorReport(err)}}

function requestString(a){try {
	if ($type(a) !== 'array') throw('function requestString: parameter must be an array.');
	var s = '';
	a.each(function(p){s = s + p + '/'});
	return s;
}catch(err){new ErrorReport(err)}}

function curry(method) {
   var curried = [];
   for (var i = 1; i < arguments.length; i++) curried.push(arguments[i]);
   return function() {
       var args = [];
       for (var i = 0; i < curried.length; i++) args.push(curried[i]);
       for (var i = 0; i < arguments.length; i++) args.push(arguments[i]);
       return method.apply(null, args);
   }
}

function getDimensions(volume,height,width) {
	height = 5;
	
	volume = volume.toInt();
	height = height.toInt();
	if (!$chk(width)) width = roundTo(Math.sqrt(volume / height),5,'up');
	var length = roundTo(Math.floor(volume / height / width),5,'up');
	if (width == 0) width = 5;
	if (length == 0) length = 5;
	return width + " x " + length + ' square feet';
}



//Extension of Array class
//functions are duplicated below... rollcall uses old versions


Array.implement({
	//converts array to string and searches that-- actually works and seems really really fast
	//returns -1 or an index that isnt useful - will need update to return meaningful index - with tokens
	sexySearch: function(value) {
		var t = this.slice();
		u = new Array();
		t.push("xkv");
		u.push("xkv");
		var v = u.concat(t);
		value = " " + value + " ";
		var str = v.join(" ");
		return str.search(value);
	},
	lastIndexOf: function(n){
	    var i=this.length;
	    while(i--) if(this[i]===n) return i;
	    return -1;
	},
	firstIndexOf: function(needle) {
		for (var i = 0; i < this.length; i ++) if (this[i] === needle) return i;
		return -1;
	},
	removeAll: function() {this.length = 0},
	contains: function(needle) {
		for (var i = 0; i < this.length; i++) if (this[i] === needle) return true;
		return false;
	},
	removeElement: function(needle) {
		var a = new Array();
		var i = this.firstIndexOf(needle);
		if (i < 0) return this;
		a = this.slice(0,i);
		a = a.concat(this.slice(i + 1,this.length));
		return a;
	},
	shuffle: function(){
	     var i=this.length,j,t;
	     while(i--) {
	         j=Math.floor((i+1)*Math.random());
	         t=arr[i];
	         arr[i]=arr[j];
	         arr[j]=t;
	     }
	 },
	unique: function(sortBy) {
		var a=[],i;
		if (sortBy !== undefined) this.sort(undefined);
		else this.sort();
		for (i=0;i<this.length;i++) if(this[i]!==this[i+1]) a[a.length]=this[i];
		return a;
	}
});

//Rollcall uses these versions sometimes somewhere

//for array.sort()
function sortNumber(a,b) {
	return a - b;
}

function removeFromArray(needle,haystack) {
	var a = new Array();
	var i = arrayIndexOf(needle,haystack);
	if (i < 0)
		return haystack;
	a = haystack.slice(0,i);
	a = a.concat(haystack.slice(i + 1,haystack.length));
	return a;
}

function isFound(needle,haystack) {
	for (var i = 0; i < haystack.length; i ++)
		if (haystack[i] === needle)
			return true;
	return false;
}

function arrayIndexOf(needle,haystack) {
	for (var i = 0; i < haystack.length; i ++)
		if (haystack[i] === needle)
			return i;
	return -1;
}

//binary search
function binarySearch(A, value){
	var low = 0;
	var high = A.length - 1;
	while (low <= high) {
	    var mid = Math.floor((low + high) / 2);
	    if (A[mid] > value)
	        high = mid - 1;
	    else if (A[mid] < value)
	        low = mid + 1;
	    else
	        return mid; // found
	}
	return null; // not found
}

//todo with strings....

function trim(stringValue) {
	return stringValue.replace(/(^\s*|\s*$)/, "");
}

//comes up a lot for alternating table classes
function cleanRowClasses(t) {
	if (t === undefined)
		var t = 1;	
	var table = document.getElementsByTagName("table")[t];
	var rows = table.getElementsByTagName("tr");
	
	for (var i = 1; i < rows.length; i++) 
		if (i % 2 == 0) {
			rows[i].setAttribute('class','odd');  		//safari, ff, and every other not idiotic browser
			rows[i].setAttribute('className','odd');	//idiotic browsers (ie)
		}
		else {
			rows[i].setAttribute('class','even');
			rows[i].setAttribute('className','even');	
		}	
}

function cleanRowStyles(t) {
	$(t).getElement('tbody').getElements('tr').each(function(tr,i){
		if (i % 2 == 0) tr.setProperties({'class':'even','style':'background-color:#FBFBF9'});
		else tr.setProperties({'class':'odd','style':'background-color:#EFEFE7'});
	});
}

function validateEmail(email) {
	var emailRe = /^\w+([\.-]?\w+)*@\w+([\.-]?\w+)*\.(\w{2}|(com|net|org|edu|int|mil|gov|arpa|biz|aero|name|coop|info|pro|museum))$/
	//phoneRe = /^((\+\d{1,3}(-| )?\(?\d\)?(-| )?\d{1,5})|(\(?\d{2,6}\)?))(-| )?(\d{3,4})(-| )?(\d{4})(( x| ext)\d{1,5}){0,1}$/
	 return emailRe.test(email);
}

function removeSpaces(string) {
	while(string.indexOf(' ') > -1) string = string.substring(0,string.indexOf(' ')) + string.substring(string.indexOf(" ") + 1);
	return string;
}

function alphaNumeric(string) {
	var re = /[^0-9a-zA-Z]/
	return !re.test(string);
}

function numeric(string) {return /^[0-9]+$/.test(string)}

function validateZipCode(string) {
	if (string.length != 5 && string.length != 9) return false;
	var re = /^((0-9)-)/
	return !re.test(string);
}

function validateName(name) {
	if (name == "enter name here")
		return false;
	if (name.indexOf("Guest ") == 0 && (name.length == 7 || name.length == 8))
		return false;
	return true;
}

//matches some invalid dates
function validateDate(date) {
	var re = /(0[1-9]|1[012])[- /.](0[1-9]|[12][0-9]|3[01])[- /.](19|20)[0-9]{2}/
	if (!re.test(date)) return false;
	date = new StringTokenizer(date,'/');
	var month = date.nextToken();
	var day = date.nextToken();
	var year = date.nextToken();
	if (new Date().setFullYear(year,month,day) < new Date() || new Date().setFullYear(year,month,day) > new Date().getDate() + 600) return false;
	return true;
}

//make your own cleanRules(content)
function clean(el) {
	if ($(el).getChildren().length > 0) $(el).getChildren().each(function(i){clean(i)});
	else $(el).setText(cleanRules($(el).getText()));
}

//table input functions
function checkAll(id) {$(id).getElements('input[type=checkbox]').each(function(i){i.checked = true})}

function checkNone(id) {$(id).getElements('input[type=checkbox]').each(function(i){i.checked = false})}

function checkOpposite(id) {$(id).getElements('input[type=checkbox]').each(function(i){
	if (i.checked) i.checked = false;
	else i.checked = true;
})}

function checkFolders(id) {
	$(id).getElements('input[type=checkbox]').each(function(i){		
		if (i.id.indexOf('fo') > -1)
			i.checked = true;
		else
			i.checked = false;
	});
}

function checkFiles(id) {
	$(id).getElements('input[type=checkbox]').each(function(i){
		if (i.id.indexOf('fi') > -1)
			i.checked = true;
		else
			i.checked = false;
	});
}

