var Overlay = new Class({
	initialize: function (popup) {
		// create transparent div & inject
		var div = new Element('div', {id:'overlay'}).setStyles({
			position:'absolute',
			top:0, left:0,
			background:'#000',
			opacity:0
		}).injectInside(document.body);
		// create fade effects
		this.fadeIn = new Fx.Style(div, 'opacity', {
			duration:200,
			onStart: function () {
				$$('object, embed, select').each(function(el){el.style.visibility = 'hidden';});
			}
		});
		this.fadeOut = new Fx.Style(div, 'opacity', {
			duration:200,
			onStart: function () {
				$$('object, embed, select').each(function(el){el.style.visibility = 'visible';});
			}
		})
		this.windows = [popup];
		return $extend(div, this);
	},
	fitToWindow: function () {
		this.setStyles({
			height: window.getScrollHeight(),
			width:  window.getWidth()			// width: 100% doesn't work in IE6
		});
		return this;
	},
	dispose: function () {
		this.windows.remove();
		this.remove();
	}
});

var confirmExit = function (evt) {
	var message = 'Are you sure you want to leave?';
	if (typeof evt == 'undefined')	evt = window.event;
	if (evt) 						evt.returnValue = message;
	return message;
}

var Popup = new Class({
	initialize: function (options) {
		this.options = Object.extend({
			id: false,
			width:300,
			height:100,
			overlay:true,
			container:document.body,
			closable:true,
			draggable:false,
			resizable:false,
			content: false
		}, options || {});

//		window.addEvent('beforeunload', confirmExit);
		var div = new Element('div', {'class':'popup'}).setStyles({
			width:this.options.width, height:100, padding:10,
			background:'#fff',
			opacity:0
		}).injectInside(document.body);
			div.fade = new Fx.Style(div, 'opacity', {duration:200});
			div.content = new Element('div').injectInside(div);
		
		return $extend(div, this);
	},
	activate: function () {
		if (this.options.closable)		this.makeClosable();
		if (this.options.draggable)		this.makeDraggable();
		if (this.options.resizable)		this.makeResizable();
		if (this.options.overlay) {
			this.createOverlay();
			if (this.overlay.windows.length == 1) {
				this.overlay.fitToWindow().fadeIn.start(0, 0.7).setOptions({
					onComplete:function () {
						this.furtherActivateCode();
					}.bind(this)
				});
			} else {
				this.overlay.moveToTop();
				this.furtherActivateCode();
			}
		} else {
			this.furtherActivateCode();					
		}
		return this;
	},
	furtherActivateCode: function () {
		this.moveToTop().center().fade.start(1).setOptions({
			onComplete: function () {
				this.showLoader();						
			}.bind(this)
		});
	},
	deactivate: function () {
		if (tinyMCE)	{
			tinyMCE.triggerSave(true, true);
			if ($('abstract'))	tinyMCE.execCommand('mceRemoveControl', false, 'abstract');
			if ($('keyFacts'))	tinyMCE.execCommand('mceRemoveControl', false, 'keyFacts');
		}
		if (this.overlay && this.overlay.windows.length == 1) {
			this.setOpacity(0);
			this.overlay.fadeOut.setOptions({
				onComplete: function () {
					this.overlay.windows.pop();
					this.overlay.dispose();
					this.remove();
				}.bind(this)
			}).start(0);
		} else {
			this.setOpacity(0);
//			this.content.setHTML = '';
			this.overlay.windows.pop()
			this.overlay.windows.getLast().moveToTop();
			this.remove();
		}
//		window.removeEvents('beforeunload');
	},
	center: function() {
		var left = (window.getWidth()/2) - (this.getSize().size.x/2);
		var top = (window.getHeight()/2) - (this.getSize().size.y/2) + (window.getScrollTop());
		if (left < 0) left = 10;
		if (top < 0 || top < window.getScrollTop()) top = window.getScrollTop() + 10;
		
		this.setStyles({
			position:'absolute',
			left: left,
			top: top
		});
		if (this.overlay)		this.overlay.fitToWindow();
		return this;
	},
	createOverlay: function () {
		if ($('overlay')) {
			$('overlay').windows.push(this);
			this.overlay = $('overlay');
		} else {
			this.overlay = new Overlay(this);
		}
	},
	showLoader: function () {
		this.loader = this.clone().empty().setStyles({
			width: this.getStyle('width') || this.getSize().size.x,
			height: this.getStyle('height') || this.getSize().size.y,
			background:'#fff url(images/loader.gif) no-repeat center',
			opacity:1,
			zIndex: this.getStyle('zIndex') + 1,
			textAlign: 'center'
		}).injectAfter(this);
		if (this.getStyle('width').indexOf('%') != -1)	this.setStyles({position:'relative', left:'-10px'});
		
		(function () {if (this.options.onLoading) this.options.onLoading();}).delay(100, this);
		
		return this;
	},
	removeLoader: function () {
		if (this.loader)	this.loader.remove();
		return this;
	},
	makeClosable: function() {
		new Element('a', {'class':'close'})
			.setText('Close')
			.addEvent('click', function() {this.deactivate();}.bind(this))
			.injectTop(this);
		return this;
	},
	makeDraggable: function() {
		new Drag.Move(this, {
//					handle: this.titleBar,
			onStart: function(){  
				this.moveToTop();
			}.bind(this),
			onComplete: function(){

			}.bind(this)
		});
		return this;
	},
	makeResizable: function() {
		var div = new Element('div').injectInside(this.DOM);
		this.makeResizable({
			handle: new Element('a', {'class':'resize'}).injectInside(div),
			limit: {x: [200, 800], y: [40]}
		});		
		return this;
	},
	consume: function (content, options) {
		var options = Object.extend({
			
		}, options || {});
		// make real transparent and inject content, and get size
		this.setStyles({height:'auto', opacity:0}).content.adopt(content);
		this.center();
		var pos = this.getCoordinates();
		// grow clone to size of real
		this.loader.scale = new Fx.Styles(this.loader, {
			onComplete: function () {
				this.setOpacity(1);
				if (this.overlay)	this.overlay.fitToWindow();
				this.loader.remove();	
//				delete this.clone; // bug in IE
				if (options.onComplete)	options.onComplete();
			}.bind(this)
		}).start({width:pos.width, height:pos.height, top:pos.top, left:pos.left});
	}
});
		
var Table = new Class({
	initialize: function (el, options) {
		this.options = Object.extend({
			width:980,
			popup: true,
			overlay:true,
			container:document.body,
			striped:true,
			highlightable:true,
			sortable:true,
			filterable:true
		}, options || {});
		
		var table = $(el);
			table.header = table.getFirst();
			table.body = table.getFirst().getNext();
		
		return $extend(table, this);
	},
	activate: function () {
		if (this.options.striped)		this.stripe();
		if (this.options.sortable)		this.makeSortable();
		if (this.options.filterable)	this.makeFilterable();
		if (this.options.highlightable)	this.makeHighlightable();
	},
	stripe: function () {
		var counter = 0;
		var children = this.body.getElementsByTagName('tr');
		// use a for loop instead of $$('tr').each() because it's too costly in garbage collection
		for (var i=0, j=children.length; i<j; i++) {
			var tr = children[i];
			if (tr.style.display != 'none') {
				counter++;
			}
			tr.className = tr.className.replace( new RegExp('(^|\\s)alt(?:\\s|$)'), '$1').clean();
			if ( !(( counter % 2 ) != 0) )	tr.className += ' alt';
		}
	},
	sort: function (header_text) {
		var header = this.headers.get(header_text);
		var pos = header.cell.getPositionedOffset();
		var rowsNew = [];
		var rowsNow = $A(this.body.getElementsByTagName('tr'));
		while (row = rowsNow.shift()) {
//			row = { row: row.remove() };
			row = { row: row.parentNode.removeChild(row) };
			rowsNew.unshift( row );
		}
		if ( this.sort_column >= 0 && this.sort_column == header.column ) {
			// Reverse
			this.sortImage.setProperty('src', 'images/' + (this.sortImage.src.contains('down') ? 'up' : 'down') + '.png').setStyles({
				top: pos.y + (this.sortImage.src.contains('up') ? header.cell.getSize().size.y - this.sortImage.height : 0)
			});
		} else {
			// Sort
			this.sortImage.setProperty('src', 'images/down.png').setStyles({
				top: pos.y,
				left: pos.x + (header.cell.getSize().size.x/2)-10,
				display:'block'
			});
			// Get Type / Conversion Function
			this.sort_column = header.column;
			if (header.conversion_function) {
				this.conversion_function = header.conversion_function;
			} else {
				this.conversion_function = false;
				rowsNew.some(function(row){
					var to_match = $(row.row.getElementsByTagName('td')[this.sort_column]).getText();
					if (to_match == ''){ return false }
					this.conversions.some(function(conversion){
						if (conversion.matcher.test( to_match )){
							this.conversion_function = conversion.conversion_function;
							return true;
						}
						return false;
					}.bind( this ));
					if (this.conversion_function){ return true; }
					return false;
				}.bind( this ));
				if (this.conversion_function) {
					header.conversion_function = this.conversion_function.bind( this );
					this.headers.set( header_text, header );
				}
			}
			if (this.conversion_function) {
				rowsNew.each(function(row){
					row.compare_value = this.conversion_function(row);
					row.toString = function(){ return this.compare_value }	// the default Array.sort() method run toString on every value, so just overrule this with your custom function
				}, this);
				rowsNew.sort();
			}
		}
		var index = 0;
		while ( row = rowsNew.shift() ) {
			this.body.appendChild(row.row);
//			row.row.injectInside( this.body );
//			if (row.detail){ row.detail.injectInside( this.body ) };
			index++;
		}
		this.stripe();
		rowsNew = false;
	},
	makeSortable: function () {
		this.headers = new Hash;
		$A(this.header.getFirst().getElementsByTagName('th')).each(function(header, index) {
			this.headers.set($(header).getText(), {cell:header, column:index});
			header.addEvent('mousedown', function(evt) {
				var eventSrc = new Event(evt).target;
				if (header.getText() == eventSrc.getText())	this.sort(header.getText());
			}.bind(this));
		}, this);
		
		this.sortImage = new Element('img', {src:'images/down.png'}).setStyles({display:'none', position:'absolute', zIndex:999}).injectInside(this.getParent());

		if (!this.conversions)	this.loadConversions();
	},
	makeFilterable: function () {
		if (!this.conversions)	this.loadConversions();
		var headers = this.header.getFirst().getChildren();
		var uniqueValueKeys = ['id', 'supporting_only', 'title', 'date', 'author', 'priority', 'permission', 'reviewers', 'state', 'filename'];
		// could use this to add divs in one go here, before loop...
		
		// for each header
		headers.each(function (th, columnIndex) {
			if (th.btn = th.getFirst())	{
				var table = this;
				
				var pos = th.getPositionedOffset();
				var LIs = [];
/*				// Get the unique values from the column, then sort them
					var uniqueValues = [];
					this.body.getChildren().each(function (row) {
						var val = row.getChildren()[columnIndex].getText()
						if (!uniqueValues.contains(val) && val != '')		uniqueValues.push(val);
					});
*/
			
				// create buttons, position, and add event to generate list
				th.btn = th.getFirst();
				th.btn
					.setStyles({
						top:pos.y, 
						left:pos.x + th.getSize().size.x - 20
					})
					.addEvent('click', function () {
						var closing = (this.list && this.list.getStyle('display') == 'block');
						$$('.filterList').each(function (list) {list.setStyle('display', 'none')});
						// if list exists, show it, otherwise create it
						if (this.list) {
							if (!closing) {
								this.list.setStyles({
									top:th.getPositionedOffset().y+th.getSize().size.y,
									left:th.getPositionedOffset().x,
									display:'block'
								});
								if (this.list.getSize().size.x < th.getSize().size.x +20)	this.list.setStyle('width', th.getSize().size.x + 20);
//								this.list.moveToTop();
							}
						} else {
							var uniqueValues = table.options.filters[uniqueValueKeys[columnIndex]].remove('');
							// sort
							uniqueValues.some(function (val) {
								if (val.match(/^\d+$/)) {
									uniqueValues.sort(function (a, b){return (a-b);});	// altering uniqueValues toString() method didn't work
								} else {
									uniqueValues.sort();
								}
								return true;
							});
							// create HTML array
							uniqueValues.each(function (value, index) {
								LIs[index] = '<li' + (index%2 == 0 ? ' class="alt">' : '>') + value + '</li>';
							}, this);
							LIs.unshift('<li>All</li>');
							// create list
							this.list = new Element('ul', {'class':'filterList'})
								.addEvent('mousedown', function(e) {
									var li = (e.srcElement) ? e.srcElement : e.target;
									if ($(li).getTag() == 'li') {
										th.btn.list.setStyle('display', 'none');
										if (table.filteredRows) {
											table.filteredRows.each(function(row, index) {
												if (table.body.childNodes[index]) {
													if (row) $(row).injectBefore(table.body.childNodes[index]);
												} else {
													table.body.appendChild(row);
												}
											});
										}
									
										var val = li.getText();
										if (val == 'All') {
											table.body.getChildren().each(function (row) {
												row.setStyle('display', '');
											});
										} else {
											var colName = th.getText();
											table.filteredRows = [];
											if (colName.contains('Awaiting...')) {		// if Reviewer Column - should be outside the object, obviously
												$A(table.body.childNodes).each(function (row) {
//													row.style.display = ($(row.childNodes[columnIndex]).getText().contains(val) ? '' : 'none');
													table.filteredRows.push( ($(row.childNodes[columnIndex]).getText().contains(val)) ? null : $(row).remove() );
												});
//												table.body.getChildren().each(function (row) {
//													row.setStyle('display', (row.getChildren()[columnIndex].getText().contains(val) ? '' : 'none'));
//												});
											} else {
												$A(table.body.childNodes).each(function (row) {
//													row.style.display = ($(row.childNodes[columnIndex]).getText() == val ? '' : 'none');
													table.filteredRows.push( ($(row.childNodes[columnIndex]).getText() == val) ? null : $(row).remove() );

												});
//												table.body.getChildren().each(function (row) {
//													row.setStyle('display', (row.getChildren()[columnIndex].getText() == val ? '' : 'none'));
//												});
											}
										}
										table.stripe();
										headers.each(function (th) {th.removeClass('filtered');});
										if (val != 'All') th.addClass('filtered');
										$$('.filterBtn').each(function (button) {
											button.setStyle('left', button.getParent().getPositionedOffset().x + button.getParent().getSize().size.x - 20);
										});
										if ($('overlay'))	$('overlay').fitToWindow();
									}
								})
								.setStyles({
									top:th.getPositionedOffset().y+th.getSize().size.y,
									left:th.getPositionedOffset().x
								})
//								.injectInside(this.getParent())
								.injectInside(th.getParent().getParent().getParent().getParent())
								.setHTML(LIs.join(''));
							if (this.list.getSize().size.x < th.getSize().size.x + 20)	this.list.setStyle('width', th.getSize().size.x + 20);
						}
					});
			}
		}, this);
		
	},
	makeHighlightable: function () {
	/*
		this.tablerows = this.body.getChildren();
		$A(this.tablerows).each(function (row) {
			row.addEvent('mouseover', function () {row.addClass('on');});
			row.addEvent('mouseout', function () {row.removeClass('on');});
		});
	*/
	},
	loadConversions: function() {
		this.conversions = $A([
			// YYYY-MM-DD, YYYY-m-d
			{ matcher: /\d{4}-\d{1,2}-\d{1,2}/,
				conversion_function: function( row ) {
					var cell = $(row.row.getElementsByTagName('td')[this.sort_column]).getText();
					var re = /(\d{4})-(\d{1,2})-(\d{1,2})/;
					cell = re.exec( cell );
					return new Date(parseInt(cell[1]), parseInt(cell[2], 10) - 1, parseInt(cell[3], 10));
				}
			},
			// Numbers
			{ matcher: /^\d+$/,
				conversion_function: function( row ) {
					var cell = $(row.row.getElementsByTagName('td')[this.sort_column]).getText();
					return '00000000000000000000000000000000'.substr(0,32-cell.length).concat(cell);
				}
			},
			// Fallback 
			{ matcher: /.*/,
				conversion_function: function( row ) {
					return $(row.row.getElementsByTagName('td')[this.sort_column]).getText();
				}
			}
		]);
	}
});
		
var Form = new Class({
	initialize: function (id, options) {
		this.options = Object.extend({
			submitBtnText: 'Submit',
			reset:false
		}, options || {});
		
		var form = new Element('form', {id:id});
		this.fields = [];
		this.fieldsets = [];
		this.controls = new Element('div').adopt(new Element('input', {type:'submit', 'class':'button', value:this.options.submitBtnText})).injectInside(form);;
		
		if (this.options.reset) 	this.controls.adopt(new Element('input', {type:'reset', value:'Reset'}));
		if (this.options.onSubmit) 	form.addEvent('submit', this.options.onSubmit);
		
		return $extend(form, this);
	},
	appendFieldset: function (fieldset) {
		fieldset.injectBefore(this.controls);
		this.fieldsets.push(fieldset);
		return fieldset;
	}
});

var Fieldset = new Class({
	initialize: function (legend) {
		this.fields = [];
		this.legend = new Element('legend').setText(legend);
		this.fieldList = new Element('ul');
		var fieldset = new Element('fieldset', {id: legend.camelize()}).adopt(this.legend).adopt(this.fieldList);
		
		return $extend(fieldset, this);
	},
	appendFields: function(fields) {
		fields.each(function (field) {
			this.appendField(field);
		}.bind(this));
	},
	appendField: function (field) {
		this.fieldList.adopt(field);
		this.fields.push(field);
		var text = (field.ref? field.ref : (field.label? field.label.getText() : field.control.id));
//		var text = (field.label) ? field.label.getText() : field.control.id;
//				if (field.label) {
//					text = field.label.getText();
			this.form.fields[(text.charAt(text.length-1) == ':') ? text.substring(0, text.length-1) : text] = field.control;
//				}
	}
});

var Field =  new Class({
	initialize: function (options) {
		var li = new Element('li');
		this.options = (typeof(options) == 'string') ? {label:options} : options;
		this.options.id = (this.options.id) ? this.options.id : (this.options.label) ? this.options.label.camelize() : null;
		if (this.options.label == 'Password') this.options.type = 'password';
		this.options.value = String(this.options.value).nonNull();
		
		if (this.options.label)		this.label = new Element('label', {'for': this.id}).setText(this.options.label + ':').injectInside(li);
		if (this.options.ref)		this.ref = this.options.ref;
		
		switch (String(this.options.type)) {
			case 'textarea':
				this.control = new Element('textarea').setText(this.options.value);
				break;
			case 'select':
				this.control = new Element('select', {name:this.options.id});
				if (options.multiple) this.control.multiple = true;
				break;
			case 'checkbox':
				this.control = new Element('input', {type:this.options.type, name:this.options.id});
				break;
			case 'file':
				this.control = new Element('input', {type:this.options.type, value:this.options.value});
				break;
			case 'password':
			case 'hidden':
				this.control = new Element('input', {type:this.options.type, value:this.options.value});
				break;
			case 'button':
				this.control = new Element('button', {id:this.options.text, type:this.options.type, 'class':'button', value:this.options.text});
				break;
			case 'submit':
			case 'reset':
				this.control  = new Element('input', {type:this.options.type, 'class':'button', value:this.options.value});
				break;
			default:
				this.control = new Element('input', {type:'text', value:this.options.value});
		}
		if (this.options.id)			this.control.id = this.options.id;
		if (this.options.maxLength)		this.control.maxLength = this.options.maxLength;
		
		li.adopt(this.control);
		this.control.setValue = function (value) {
			switch (this.getParent().options.type) {
				case 'textarea': this.setText(String(value).nonNull()); break;
				default: 
					var div = new Element('div').setText(String(value).nonNull());
					this.value = div.getText();
					break;
			}
		}
		
		return $extend(li, this);
	},
	pushOptions: function (options) {
		options.each(function (option) {
			this.control.adopt(new Element('option', {value:option.value}).setText(option.text));
		}.bind(this));
		
		return this;
	}
});

Element.extend({
	moveToTop: function () {
		var zIndex = 0;
		var children = document.body.getElementsByTagName('*');
		for (var i=0, j=children.length; i<j; i++) {
			var element = children[i];
				if (element.id == 'overlay') {
					var t = 't';
				}
				var tag = String(element.nodeName).toLowerCase();
				if (tag != 'tr' || tag != 'td') {
					var z = element.style.zIndex;
					if (parseInt(z) > parseInt(zIndex))		zIndex = z;
				}
			
		}
		this.setStyle('zIndex', zIndex-0+1);
		
		return this;
	},
	getPositionedOffset: function () {
		var left = 0, top = 0, element = this;
		do {
			left += element.offsetLeft || 0;
			top += element.offsetTop  || 0;

			element = $(element.offsetParent);
			if (element) {
				if (element.tagName == 'BODY') break;
				var p = element.getStyle('position');
				if (p == 'relative' || p == 'absolute') break;
			}
		} while (element);
		return {'x':left, 'y':top};
	},
	center: function () {
		var left = (window.getWidth()/2) - (this.getSize().size.x/2);
		var top = (window.getHeight()/2) - (this.getSize().size.y/2) + (window.getScrollTop());
		if (left < 0) left = 10;
		if (top < 0 || top < window.getScrollTop()) top = window.getScrollTop() + 10;
		
		this.setStyles({
			position:'absolute',
			left: left,
			top: top
		});
		if (this.overlay)		this.overlay.fitToWindow();
		return this;
	},
	fitToWindow: function () {
		this.setStyles({
			height: window.getScrollHeight(),
			width:  window.getWidth()			// width: 100% doesn't work in IE6
		});
		return this;
	}
});
String.extend({
	nonNull: function () {
		return (this == null || this == 'null' || this == 'undefined') ? '' : this;
	},
	camelize: function () {
		return this.toLowerCase().replace(/ /g, '-').camelCase();
	},
	sqlDate: function () {
		var date = this.substring(3, 5) + '/' + this.substring(0,2) + '/' + this.substring(6,10);
		return (date == '//') ? '' : date;
	}
});
Date.prototype.prettify = function () {
	if (!this.getDate()) return '';
	
	var d = (this.getDate() < 10) ? '0' + this.getDate() : this.getDate();
	var m = ((this.getMonth() + 1) < 10) ? '0' + (this.getMonth() + 1) : this.getMonth() + 1;
	var y = this.getFullYear();
	
	return d + '/' + m + '/' + y;
}
