/*
	Functions from prototype.js (http://prototype.conio.net/)
	Rewritten just a little bit.
*/

function $() {
	for ( var i = 0, elements = []; i < arguments.length; ++i )
		elements.push ( typeof arguments[i] == 'string' ? document.getElementById ( arguments[i] ) : arguments[i] );
	return elements.length == 1 ? elements[0] : elements;
}

function $A ( array ) {
	if ( !array ) return [];
	if ( array.toArray ) return array.toArray();
	for ( var i = 0, result = []; i < array.length; ++i )
		result.push ( array[i] );
	return result;
}

var Class = {
	create: function() { return function() { this.initialize.apply ( this, arguments ); } }
}

Object.extend = function ( dest, source ) {
	for ( p in source ) dest[p] = source[p];
	return dest;
}

Function.prototype.bind = function() {
	var __method = this, args = $A(arguments), obj = args.shift();
	return function() { return __method.apply ( obj, args.concat ( $A ( arguments ) ) ); }
}

Function.prototype.bindAsEventListener = function ( object ) {
	var __method = this;
	return function ( event ) { return __method.call ( object, event || window.event ); }
}

Object.prototype.shift = function() {
	var result = this[0];
	this.length--;
	for ( var i = 0; i < this.length; ++i )
		this[i] = this[i+1];
	return result;
}

var Try = {
	these: function() {
		for ( var i = 0, retval; i < arguments.length; ++i )
			try { retval = arguments[i](); break; } catch(error){}
		return retval;
	}
}


if (!window.Event) {
	var Event = new Object();
}

Object.extend(Event, {
	KEY_BACKSPACE: 8,
	KEY_TAB:	   9,
	KEY_RETURN:   13,
	KEY_ESC:	  27,
	KEY_LEFT:	 37,
	KEY_UP:	   38,
	KEY_RIGHT:	39,
	KEY_DOWN:	 40,
	KEY_DELETE:   46,

	element: function(event) {
		return event.target || event.srcElement;
	},

	isLeftClick: function(event) {
		return (((event.which) && (event.which == 1)) || ((event.button) && (event.button == 1)));
	},

	pointerX: function(event) {
		return event.pageX || (event.clientX + (document.documentElement.scrollLeft || document.body.scrollLeft));
	},

	pointerY: function(event) {
		return event.pageY || (event.clientY + (document.documentElement.scrollTop || document.body.scrollTop));
	},

	stop: function(event) {
		if (event.preventDefault) {
			event.preventDefault();
			event.stopPropagation();
		} else {
			event.returnValue = false;
			event.cancelBubble = true;
		}
	},

	observers: false,

	_observeAndCache: function(element, name, observer, useCapture) {
		if (!this.observers) this.observers = [];
		if (element.addEventListener) {
			this.observers.push([element, name, observer, useCapture]);
			element.addEventListener(name, observer, useCapture);
		} else if (element.attachEvent) {
			this.observers.push([element, name, observer, useCapture]);
			element.attachEvent('on' + name, observer);
		}
	},

	unloadCache: function() {
		if (!Event.observers) return;
		for (var i = 0; i < Event.observers.length; i++) {
			Event.stopObserving.apply(this, Event.observers[i]);
			Event.observers[i][0] = null;
		}
		Event.observers = false;
	},

	observe: function(element, name, observer, useCapture) {
		var element = $(element);
		useCapture = useCapture || false;
		if (name == 'keypress' && (navigator.appVersion.match(/Konqueror|Safari|KHTML/) || element.attachEvent))
			name = 'keydown';
		this._observeAndCache(element, name, observer, useCapture);
	},

	stopObserving: function(element, name, observer, useCapture) {
		var element = $(element);
		useCapture = useCapture || false;

		if (name == 'keypress' && (navigator.appVersion.match(/Konqueror|Safari|KHTML/) || element.detachEvent))
			name = 'keydown';

		if (element.removeEventListener) {
			element.removeEventListener(name, observer, useCapture);
		} else if (element.detachEvent) {
			element.detachEvent('on' + name, observer);
		}
	}
});


// Event stuffs, don't need these anymore, use prototype's observe instead
function addEvent( obj, type, fn, userCapture ) {
	Event.observe ( obj, type, fn, userCapture );
}

function removeEvent( obj, type, fn, userCapture ) {
	Event.stopObserving ( obj, type, fn, userCapture );
}

function addLoadEvent ( func ) {
	var oldonload = window.onload;
	if (typeof window.onload != 'function') window.onload = func;
	else { window.onload = function() { oldonload(); func(); } }
}

/*
	Others stuffs
*/

function getCookie( name ) {
	var start = document.cookie.indexOf( name + "=" );
	var len = start + name.length + 1;
	if ( ( !start ) && ( name != document.cookie.substring( 0, name.length ) ) ) return null;
	if ( start == -1 ) return null;
	var end = document.cookie.indexOf( ";", len );
	if ( end == -1 ) end = document.cookie.length;
	return unescape( document.cookie.substring( len, end ) );
}

function setCookie( name, value, expires, path, domain, secure ) {
	var today = new Date();
	today.setTime( today.getTime() );
	if ( expires )
		expires = expires * 1000 * 60 * 60 * 24;
	var expires_date = new Date( today.getTime() + (expires) );
	document.cookie = name+"="+escape( value ) +
		( ( expires ) ? ";expires="+expires_date.toGMTString() : "" ) + //expires.toGMTString()
		( ( path ) ? ";path=" + path : "" ) + ( ( domain ) ? ";domain=" + domain : "" ) + ( ( secure ) ? ";secure" : "" );
}

function deleteCookie( name, path, domain ) {
	if ( getCookie( name ) ) document.cookie = name + "=" + ( ( path ) ? ";path=" + path : "") +
	( ( domain ) ? ";domain=" + domain : "" ) + ";expires=Thu, 01-Jan-1970 00:00:01 GMT";
}


if ( typeof Array.prototype.push == 'undefined' ) {
	Array.prototype.push = function() {
		for ( var i = 0, l = this.length; i < arguments.length; ++i )
			this[l+i] = arguments[i];
		return this.length;
	}
}

Array.prototype.remove = function(v)
{
	for ( var i = 0; i < this.length; ++i )
		if ( this[i] == v )
			return this.splice(i, 1);
	return false;
}

Object.prototype.toQueryString = function() {
	var query = [];
	for ( var x in this ) {
		var type = typeof this[x];
		if ( type != 'function' && type != 'object' )
			query.push([x, encodeURIComponent(this[x])].join('='));
	}
	return query.join('&');
}

// from: http://jsfromhell.com/string/wordwrap
String.prototype.wordWrap = function(m, b, c){ //v1.0
	var i, j, l, s, r = this.split("\n");
	if(m > 0) for(i = -1, l = r.length; ++i < l;){
		for(s = r[i], r[i] = ""; s.length > m;
			j = c ? m : (j = s.substr(0, m).match(/\S*$/)).input.length - j[0].length
			|| j.input.length + (j = s.substr(m).match(/^\S*/)).input.length + j[0].length,
			r[i] += s.substr(0, j) + ((s = s.substr(j)).length ? b : "")
		);
		r[i] += s;
	}
	return r.join("\n");
};

Object.extend ( String.prototype, {
	escapeHTML: function() {
		var div = document.createElement('div');
		var text = document.createTextNode(this);
		div.appendChild(text);
		return div.innerHTML;
	},

	getTextWidth: function(){
		var span = document.createElement('span');
		Object.extend(span.style, {visibiliy:'hidden', position:'absolute', left:'0px', top:'0px'});
		span.innerHTML = this;
		document.body.appendChild(span);
		var width = span.offsetWidth;
		document.body.removeChild(span);
		return width;
	},

	// from: http://jsfromhell.com/string/wordwrap
	wordWrap: function(m, b, c) {
		var i, j, l, s, r = this.split("\n");
		if(m > 0) for(i = -1, l = r.length; ++i < l;){
			for(s = r[i], r[i] = ""; s.length > m;
				j = c ? m : (j = s.substr(0, m).match(/\S*$/)).input.length - j[0].length
				|| j.input.length + (j = s.substr(m).match(/^\S*/)).input.length + j[0].length,
				r[i] += s.substr(0, j) + ((s = s.substr(j)).length ? b : "")
			);
			r[i] += s;
		}
		return r.join("\n");
	}
});


/* Basic funcs & objs */
function Timer()
{
	this.t=false;
	this.intv=false;
	this.start=function(func,delay){this.stop();this.t=window.setTimeout(func,delay);};
	this.stop=function(){if(this.t)window.clearTimeout(this.t);}
	this.startInterval=function(func,delay){this.intv=window.setInterval(func,delay);};
	this.stopInterval=function(){if(this.intv)window.clearInterval(this.intv);}
}


var quickEditBase = Class.create();
quickEditBase.prototype =
{
	initialize : function() {
		this.options = {inputSize:50, maxInput:255, maxCharsVisible:100, textAlign:'left', maxWidth:100, style:{}};
	},

	setOptions : function(options) {
		Object.extend(this.options, options||{});
	},

	initContainer : function()
	{
		if(!this.container) return false;
		this.oldStyles = { backgroundColor: this.container.style.backgroundColor, color: this.container.style.color };

		this.observers = {
			mouseover: function(){Object.extend(this.container.style, {backgroundColor:'#FFFFE6',cursor:'text',color:'#000'});}.bind(this),
			mouseout: function() { Object.extend(this.container.style, this.oldStyles); }.bind(this),
			mouseclick: this.showEditable.bind(this)
		};

		Event.observe ( this.container, 'mouseover', this.observers.mouseover );
		Event.observe ( this.container, 'mouseout', this.observers.mouseout );
		Event.observe ( this.container, 'click', this.observers.mouseclick );
	},

	restoreContainer: function()
	{
		if(!this.container) return false;
		Object.extend(this.container.style, this.oldStyles);
		this.container.onmouseover = null;
		Event.stopObserving ( this.container, 'mouseover', this.observers.mouseover );
		Event.stopObserving ( this.container, 'mouseout', this.observers.mouseout );
		Event.stopObserving ( this.container, 'click', this.observers.mouseclick );
	},

	showEditable : function()
	{
		this.editBox = document.createElement('span');
		this.inputField = document.createElement('input');
		if(!this.editBox||!this.inputField)return false;
		this.inputField.value = this.value;
		this.inputField.size = this.options.inputSize;
		this.inputField.onkeypress = this.keyPressHandler.bindAsEventListener(this);
		this.inputField.onblur = this.doUpdate.bind(this);
		this.inputField.maxLength = this.options.maxInput;
		this.inputField.style.textAlign = this.options.textAlign;
		this.editBox.className = 'qinput';
		Object.extend(this.editBox.style, this.options.style||{});
		this.editBox.appendChild(this.inputField);
		this.container.parentNode.insertBefore(this.editBox, this.container);
		this.container.style.display = 'none';
		this.inputField.setAttribute('autocomplete', 'off');

		var width = this.inputField.value.getTextWidth();
		this.inputField.style.width = ( width > this.options.maxWidth ? this.options.maxWidth : ( width < 40 ? 40 : width ) ) + 'px';

		this.inputField.focus();
		this.inputField.select();
	},

	hideEditable : function(value)
	{
		var str = value || this.value;
		str = str_slice ( str.escapeHTML(), this.options.maxCharsVisible );
		this.container.innerHTML = str;
		this.container.style.display = '';
		if(this.editBox.parentNode) this.editBox.parentNode.removeChild(this.editBox);
	},

	keyPressHandler : function ( event )
	{
		event = event || window.event;
		var key = event.keyCode;
		if ( key == Event.KEY_RETURN ) {
			this.inputField.onblur = function(){};
			window.setTimeout(this.doUpdate.bind(this), 50);
			return false;
		}
		if ( key == Event.KEY_ESC ) {
			this.hideEditable();
			return;
		}
		var str = this.inputField.value + String.fromCharCode(key);
		var width = str.getTextWidth();
		this.inputField.style.width = ( width > this.options.maxWidth ? this.options.maxWidth : ( width < 40 ? 40 : width ) ) + 'px';
		return true;
	},

	doUpdate : function() {
		alert ( 'doUpdate() must be implemented' );
		this.hideEditable();
	}
};

// Ajax stuffs
var Ajax = {
	events: ['uninitialized', 'loading', 'loaded', 'interactive', 'complete'],
	enabled: ( typeof XMLHttpRequest != 'undefined' || typeof ActiveXObject != 'undefined' ),
	getXMLHttpObject: function ( ) {
		return Try.these (
			function() {return new ActiveXObject('Msxml2.XMLHTTP')},
			function() {return new ActiveXObject('Microsoft.XMLHTTP')},
			function() {return new XMLHttpRequest()}
		) || false;
	}
}

AjaxRequest = Class.create();
AjaxRequest.prototype = {
	initialize: function ( url, options ) {
		this.options	= Object.extend ( { method: 'post', async: true, postData: {}, response: 'json' }, options || {} );
		this.onloading  = null;
		this.onloaded   = null;
		this.oncomplete = null;
		this.response   = null;
		this.xmlhttp	= Ajax.getXMLHttpObject();
		this.url		= url;
	},

	setOptions: function ( options ) {
		this.options = Object.extend ( this.options, options || {} );
	},

	request: function ( url )
	{
		this.url = url || this.url;
		this.xmlhttp.open ( this.options.method, this.url + (this.url.indexOf('?')<0?'?':'&') + 'ajax=' + this.getRand(), this.options.async );
		if ( this.options.async )
			this.xmlhttp.onreadystatechange = this.stateChange.bind(this);
		if ( this.options.method == 'post' )
			this.xmlhttp.setRequestHeader ( 'Content-type','application/x-www-form-urlencoded; charset=utf-8' )
		this.xmlhttp.send ( this.options.method == 'post' ? this.options.postData.toQueryString() : null );
	},

	stateChange: function ( )
	{
		var state = Ajax.events[this.xmlhttp.readyState];
		if ( state == 'loading' && this.onloading != null )
			this.onloading.bind(this)();
		if ( state == 'loaded' && this.onloaded != null )
			this.onloaded.bind(this)();
		if ( state == 'complete' && this.xmlhttp.status == 200 )
		{
			this.response = this.getResponse();
			this.xmlhttp.onreadystatechange = function(){};
			if ( this.oncomplete != null )
				this.oncomplete.bind(this)();
		}
	},

	getResponse: function ( type ) {
		var type = type || this.options.response;
		if ( type == 'json' ) {
			try { eval ( 'var resp = ' + this.xmlhttp.responseText ); return resp; }
			catch (error) { return { result: 'failed', message: 'Returned result is not proper JSON format. Probably parse error.' }; }
		}
		return type == 'xml' ? this.xmlhttp.responseXML : this.xmlhttp.responseText;
	},

	getRand: function ( ) {
		for ( var i = 0, rand = ''; i < 5; ++i )
			rand += String(Math.floor(Math.random()*10000000000));
		return rand;
	}
};

Element = new Object();
Object.extend(Element,
{
	position: function ( element, type )
	{
		element = $(element);
		if ( !element ) return;
		if ( type == 'absolute' && !element._absolute )
		{
			var offset = Element.getOffset ( element, false );
			Object.extend ( element.style, { position: 'absolute', top: offset.offsetTop + 'px', left: offset.offsetLeft } );
			element._absolute = true;
		}
	},

	cumulativeOffset: function(element)
	{
		var valueT = 0, valueL = 0;
		do {
			valueT += element.offsetTop  || 0;
			valueL += element.offsetLeft || 0;
			element = element.offsetParent;
		} while (element);
		return [valueL, valueT];
	},

	setOpacity: function(element, opacity)
	{
		element = $(element);
		if(!element) return false;
		if(element.filters)
		{
			try { element.filters.alpha.opacity = opacity; }
			catch (error) { element.style.filter = "alpha(opacity=" + opacity + ")"; }
		}
		Object.extend ( element.style, { 'opacity': opacity / 100, MozOpacity: opacity / 100, visibility: opacity <= 0 ? 'hidden' : 'visible' } );
	},

	getOffset: function(element, absolute)
	{
		absolute = absolute || false;
		element = $(element);
		if(!element) return false;
		var offset = Object.extend ( Element.cumulativeOffset ( element ) );
		var retval = {offsetWidth: element.offsetWidth, offsetHeight:element.offsetHeight, offsetLeft: offset[0], offsetTop: offset[1]};
		retval.offsetRight = retval.offsetLeft + retval.offsetWidth;
		retval.offsetBottom = retval.offsetTop + retval.offsetHeight;
		return retval;
	},

	fade: function(element, from, to, step)
	{
		element = $(element);
		if(!element) return false;
		if(from == null) from = 100;
		step = step || 10;
		if ( from == to )
		{
			if(typeof element.onfadefinish == 'function')
				element.onfadefinish();
			return true;
		}
		from = Element.__getNextStepValue(from, to, step);
		Element.setOpacity(element, from);
		window.setTimeout ( Element.fade.bind ( this, element, from, to, step ), 10 );
	},

	__getNextStepValue: function(from, to, step)
	{
		if(from > to) from -= (from - to) > step ? step : from - to;
		else from += (to - from) > step ? step : to - from;
		return from;
	}
});

var Form = {
	disable: function(element, disabled) {
		element = $(element);
		if(!element) return false;
		var tagName = element.tagName.toLowerCase();

		if ( tagName == 'form' || tagName == 'input' || tagName == 'textarea' || (tagName == 'fieldset' && document.all) ) {
			element.disabled = disabled;
			return true;
		}
		if(typeof disabled == 'undefined') disabled = true;
		var inputs = element.getElementsByTagName('input');
		for(var i = 0; i < inputs.length; ++i)
			inputs[i].disabled = disabled;
		var inputs = element.getElementsByTagName('textarea');
		for(var i = 0; i < inputs.length; ++i)
			inputs[i].disabled = disabled;
	}
};


var Drag = Class.create();
Drag.prototype = {
	initialize: function(element, options) {
		this.dragObj = $(element);
		if ( !this.dragObj ) return false;
		this.options = { zIndex: 1, horizontal: true, vertical: true, leftBound: 0, rightBound: -1, topBound: 0, bottomBound: -1 };
		Object.extend ( this.dragObj.style, { position: 'absolute' } );
		this.setOptions ( options || this.options );
		this.lastMousePos = [0, 0]; // [x, y]
		this.observers = {
			mouseMove: this.mouseMoveListener.bindAsEventListener(this),
			mouseDown: this.startDrag.bindAsEventListener(this),
			mouseUp: this.stopDrag.bindAsEventListener(this)
		};
		this.ondrag = null;
		this.ondragstart = null;
		this.ondragend = null;
		Event.observe ( this.dragObj, 'mousedown', this.observers.mouseDown );
		Event.observe ( this.dragObj, 'mouseup', this.observers.mouseUp );

		this.dragObj.onmousedown = function(){ return false; }
	},

	setBoundary: function(top, right, bottom, left) {
		this.setOptions({leftBound:left, rightBound:right, topBound:top, bottomBound:bottom});
	},

	setBoundingObject: function(element)
	{
		element = $(element);
		if(!element) return false;
		var offset = Element.getOffset(element);
		this.setBoundary(offset.offsetTop, offset.offsetRight, offset.offsetBottom, offset.offsetLeft);
	},

	setOptions: function(options) {
		Object.extend(this.options, options || {});
	},

	startDrag: function(event)
	{

		if ( !Event.isLeftClick ( event ) ) return false;
		Object.extend ( this.dragObj.style, { zIndex: this.options.zIndex + 1 } );
		Event.observe ( document, 'mousemove', this.observers.mouseMove );
		Event.observe ( document, 'mouseup', this.observers.mouseUp );
		document.onselectstart = function(){return false;};
		document.ondragstart = function(){return false;}

		var offset = Element.getOffset ( this.dragObj, true );
		var mousePosX = ( event.pageX || event.pageY ) ? event.pageX : event.clientX + document.body.scrollLeft;
		var mousePosY = ( event.pageY || event.pageY ) ? event.pageY : event.clientY + document.body.scrollTop;
		this.relativeX = mousePosX - offset.offsetLeft;
		this.relativeY = mousePosY - offset.offsetTop;
		this.dragObjOffset = offset;
		if ( typeof this.ondragstart == 'function' ) this.ondragstart ( Element.getOffset ( this.dragObj, true ) );

		Element.position ( this.dragObj, 'absolute' );
		return false;
	},

	stopDrag: function(event)
	{
		Object.extend ( this.dragObj.style, { zIndex: this.options.zIndex } );
		Event.stopObserving ( document, 'mousemove', this.observers.mouseMove );
		Event.stopObserving ( document, 'mouseup', this.observers.mouseUp );
		document.onselectstart = function(){return true;};
		if ( typeof this.ondragend == 'function' ) this.ondragend ( Element.getOffset ( this.dragObj, true ) );
		Element.position ( this.dragObj, 'relative' );
	},

	mouseMoveListener: function(event)
	{
		var mousePosX = Event.pointerX(event);
		var mousePosY = Event.pointerY(event);
		if ( this.lastMousePos[0] == mousePosX && this.lastMousePos[1] == mousePosY ) return;
		var pos = {
			left: mousePosX - this.relativeX, right: (mousePosX - this.relativeX) + this.dragObjOffset.offsetWidth,
			top: mousePosY - this.relativeY, bottom: (mousePosY - this.relativeY) + this.dragObjOffset.offsetHeight
		};
		// boundary constraints
		if ( pos.left < this.options.leftBound ) pos.left = this.options.leftBound;
		else if ( this.options.rightBound > -1 && ( pos.right > this.options.rightBound ) ) pos.left = ( this.options.rightBound  - this.dragObjOffset.offsetWidth );
		if ( pos.top < this.options.topBound ) pos.top = this.options.topBound;
		else if ( this.options.bottomBound > -1 && ( pos.bottom > this.options.bottomBound ) ) pos.top = ( this.options.bottomBound - this.dragObjOffset.offsetHeight );
		// direction constraints
		if ( this.options.horizontal ) this.dragObj.style.left = pos.left + 'px';
		if ( this.options.vertical ) this.dragObj.style.top = pos.top + 'px';
		if ( typeof this.ondrag == 'function' ) this.ondrag ( Element.getOffset ( this.dragObj, true ) );
		this.lastMousePos = [mousePosX, mousePosY];
	}
};



