/**
 * Function.prototype.call and other IE 5.0 nonsense copy from base2.Legacy file or include one :)
 * http://base2.googlecode.com/svn/trunk/src/legacy.js
 */

function log() {
	if (window.console)
		window.console.log.apply(window.console, arguments);
	else {
		log.output.push(array(arguments).join(', '));
		clearTimeout(log.time);
		log.time = setTimeout(function(){
			var t = log.output.join('\n');
			log.output = [];
			alert(t);
		}, 100);
	}
	return arguments[0];
}
log.output = [];

/**
 * labai plachiai naudojamas praplesti objekto duomenims
 * target = extend(target, objectas, [ objectas, ... ]);
 * kinta tik target objektas
 * pvz: extend(Array.prototype, { method: function(){} });
 */
function extend(target) {
	for (var a = arguments, i, k = 0; ++k < a.length;)
		for (i in a[k]) target[i] = a[k][i];
	return target;
}

/**
 * greitas objekto kopijavimas
 * newObjectCopy = copy(object);
 * pvz:
extend(function classConstructor(settings){
	this.settings = extend(copy(this.settings), settings);
}, {prototype:{
	settings: { default: 0 }
}});
 */
function copy(object) {
	var fn = new Function;
	fn.prototype = object;
	return new fn;
};

/**
 * "masyviniu" objectu konvertavimas y masyva arba masyvo kopijavimas
 * dazhnai naudingas reikalas ir tikrai dazhnai naudojamas
 */
function array(a) {
	var r = [], i = 0, l = a.length;
	try {
		r = array.a.slice.call(a, 0);
		// safari kazhkodel nenukopijuoja kaikuriu objektu duomenu
		if (r[0] == undefined) throw "";
	} catch (e) {
		for (; i < l; i++) r[i] = a[i];
	}
	return r;
}
array.a = [];

/**
 * unikalaus id objektui priskirimo ir nuskaitymo metodas
 * galima butu shiektiek patobulinti norit naudoti crosswindow
 */
function assignID(object) {
	// assign a unique id
	if (!object.__guid) object.__guid = assignID.$id++;
	return object.__guid;
}
assignID.$id = 1;

/**
 * priskiria funkcijai kokionors objekto skoupa
 * dazhnai naudojamas eventu priskirimui
 * pvz:
extend(window.classConstructor = function(settings){
	document.onclick = bind(this.documentClick, this);
}, {prototype:{
	state: 0,
	documentClick: function(){
		alert("document clicked then state is "+ this.state);
	}
}});
 */
function bind(method, context) {
	var bound = function() {
		return method.apply(context, arguments);
	};
	bound.__cloneID = assignID(method);
	return bound;
}

/**
 * ishrenka dom elementus
 * dom() grazhina document, dom(element) grazhina element, dom("string") grazhina element.id == "string", dom("string" || element, "string") grazhina [ elment, element, ... ]
 * pvz:
dom(dom(), "html");
 */
function dom(elem, name) {
	return !elem ? document : name
		? array(dom(elem).getElementsByTagName(name))
		: elem.constructor == String
			? dom().getElementById(elem)
			: elem;
}
extend(dom, {
	/**
	 * suranda elemento teva
	 * pvz:
	var table = dom.parent(td, null, "table");
	 */
	parent: function(elem, klass, name) {
		klass = klass && new RegExp("(^|\\s)" + klass + "(\\s|$)");
		name = name && name.toUpperCase();

		while ((elem = elem.parentNode)
			&& (klass && !klass.test(elem.className)
				|| name && name != elem.nodeName));

		return elem;
	},


	/**
	 * suranda elemento vyresnijy broly
	 * reiks prideti klass
	 */
	prev: function(elem, klass, name) {
		klass = klass && new RegExp("(^|\\s)" + klass + "(\\s|$)");
		name = name && name.toUpperCase();

		while ((elem = elem.previousSibling)
			&& (elem.nodeType != 1
				|| klass && !klass.test(elem.className)
				|| name && name != elem.nodeName));

		return elem;
	},

	/**
	 * suranda elemento jaunesnijy broly
	 * reiks prideti klass
	 */
	next: function(elem, klass, name) {
		klass = klass && new RegExp("(^|\\s)" + klass + "(\\s|$)");
		name = name && name.toUpperCase();

		while ((elem = elem.nextSibling)
			&& (elem.nodeType != 1
				|| klass && !klass.test(elem.className)
				|| name && name != elem.nodeName));

		return elem;
	},

	/**
	 * suranda elementus su tam tikra klase
	 */
	getByClass:
	(dom().getElementsByClassName // FF 3, ... ?
	? function(klass, parent, tag) {
		var res = this(parent).getElementsByClassName(klass);
		return tag && (tag = tag.toUpperCase())
			? Array.filter(res, function(elem) { return elem.nodeName == tag })
			: array(res);
	}
	: dom().evaluate // FF <= 2, Opera, Safari
	? function(klass, parent, tag) {
		var xpath = this().evaluate("descendant::"+ (tag || '*') +"[contains(concat(' ',@class,' '),'"+ klass +"')]", this(parent), null, 7, null);
		for (var i = 0, res = []; (e = xpath.snapshotItem(i)); i++) res[res.length] = e;
		return res;
	}
	: function(klass, parent, tag) { // IE
		var regxp = new RegExp('(^|\\s)' + klass + '(\\s|$)'),
			elems = this(this(parent), tag || '*');
		for (var i = 0, res = [], e; (e = elems[i]); i++)
			if (regxp.test(e.className)) res[res.length] = e;
		e = elems = null;
		return res;
	}),

	/**
	 * greitasis elementu kurimo metodas innerHTML :)
	sukuriamas tik vienas elementas
	neveikia su <caption>, <thead>, <tbody>, <tr>, <th>, <td> kadangi jie reikalauja <table> lemento
	 */
	create: function(html) {
		var d = this().createElement('div'), res;
		d.innerHTML = html;
		res = d.firstChild; d = null;
		return this.remove(res);
	},

	remove: function(elem) {
		var p = this.parent(elem);
		return p && p.removeChild(elem);
	},

	before: function(elem, before) {
		var p = this.parent(before);
		return p && p.insertBefore(elem, before);
	},

	after: function(elem, after) {
		var p = this.parent(after);
		return p && p.insertBefore(elem, after.nextSibling);
	},

	hasClass: function(elem, klass) {
		return new RegExp("(^|\\s)" + klass + "(\\s|$)")
			.test(elem.className);
	},

	addClass: function(elem, klass) {
		if (!this.hasClass(elem, klass))
			elem.className += (elem.className ? " " : "") + klass;
		return elem;
	},

	removeClass: function(elem, klass) {
		elem.className = elem.className
			.replace(new RegExp("(^|\\s)" + klass + "(\\s|$)"), "$2");
		return elem;
	},

	toggleClass: function(elem, klass) {
		return this[(this.hasClass(elem, klass) ? 'remove' : 'add') +'Class'](elem, klass);
	}
});

/**
 * eventu modulis
 */

/**
 * priskiria eventus objectams
 * panaudojimo budai:
 * events(object || "id", type, handler),
 * events([object || "id", object || "id", ...], type, handler),
 * events(object || "id", { "type": handler, "type": handler, ... }),
 * events([object || "id", object || "id", ...], { "type": handler, "type": handler, ... })
 */
// shio metodo gali ir nebuti, bo jis mano sugalvotas grichiau testukams rashyti... :)
function events(elem, type, handl) {
	if (elem instanceof Array) {
		for (var i=0; i < elem.length; i++) events(elem[i], type, handl);
	} else if (!handl) {
		for (var i in type) events(elem, i, type[i])
	} else {
		events.add(dom(elem), type, handl);
	}
}
extend(events, {
	add: function(elem, type, handl) {
		if (!elem.__events) elem.__events = {};

		var handlers = elem.__events[type];

		if (!handlers) {
			handlers = elem.__events[type] = {};
			if (elem["on"+ type]) {
				var on = elem["on"+ type];
				elem["on"+ type] = null;
				this.add(elem, type, on);
			}
		}

		handlers[assignID(handl)] = handl;
		elem["on"+ type] = this.handle;
	},

	remove: function(elem, type, handl) {
		if (!type) for (type in elem.__events)
				this.remove(elem, type);

		if (elem.__events && elem.__events[type]) {
			if (!handl) for (handl in elem.__events[type])
				delete elem.__events[type][handl];
			else
				delete elem.__events[type][handl.__guid];
		}
	},

	handle: function(e) {
		e = e || window.event;

		if (!e.target && e.srcElement)
			e.target = e.srcElement;

		if (!e.relatedTarget && e.toElement)
			e.relatedTarget = e.toElement == e.target
				? e.fromElement : e.toElement;

		if (!e.preventDefault)
			e.preventDefault = function() {
				this.returnValue = false;
			};

		if (!e.stopPropagation)
			e.stopPropagation = function() {
				this.cancelBubble = true;
			};

		var handlers = this.__events[e.type];
		var result = true;
		for (var i in handlers) {
			if (handlers[i].call(this, e) === false) {
				e.preventDefault();
				e.stopPropagation();
				result = false;
			}
		}

		if (e.srcElement)
			e = e.target = e.relatedTarget = e.preventDefault = e.stopPropagation = null;
		return result;
	},

	dispatch: function(e) {
		var elem = e.target, type = e.type;
		while (elem) {
			if (elem['on'+ type])
				elem['on'+ type](e);
			elem = !e.cancelBubble && dom.parent(elem);
		}
		return !(e.returnValue === false);
	},

	/**
	 * grazhina surinktus ish evento duomenis apie mouso koordinates
	 * koordinaciu nuo target elmento skaichiavimas perdaug skiriasi visuose browseriuose kad galima butu jy lengvai implementuoti
	 * todel paliktos kordinates nuo relative elemento
	 */
	mouse: function(e) {
		/**
		 * opera e.x, e.y atiduoda gerai tik tada jei kursorius yra ant teksto,
		 * kitu atveju gaunasi kazhkox bullshitas
		 * idealus variantas operai tokiu atveju: <div style="position: relative; padding: 0; border: 0"><div onclick="...">...</div></div>
		 */
		var o = {
			relativeX: e.layerX != undefined ? e.layerX : e.x != undefined ? e.x : 0,
			relativeY: e.layerY != undefined ? e.layerY : e.y != undefined ? e.y : 0
		};

		if (e.pageX == null && e.clientX != null) {
			var d = dom().documentElement, b = dom().body;
			o.pageX = e.clientX + (d && d.scrollLeft || b.scrollLeft || 0);
			o.pageY = e.clientY + (d && d.scrollTop || b.scrollTop || 0);
		} else {
			o.pageX = e.pageX;
			o.pageY = e.pageY;
		}

		return o;
	},

	// events.hover(div, function(e){ ... mouse over element code ... }, function{ ... mouse out element code .... });
	hover: function(elem, over, out) {
		this.add(elem, 'mouseover', function(e){
			if (!this.__hovered) {
				this.__hovered = true;
				return over.call(this, e);
			}
		});

		this.add(elem, 'mouseout', function(e){
			var p = e.relatedTarget;
			while (p && p != this) p = dom.parent(p);
			if (p != this) {
				this.__hovered = false;
				return out.call(this, e);
			}
		});
	}
});

new function(){
	var drag;

	extend(drag = events.dragdrop = function(e) {
		if (e.target) drag.mousedown(e);
		else events.add(e, "mousedown", drag.mousedown);
		return drag.draggable;
	}, {
		draggable: {},
		mousedown: function(e) {
			var m = events.mouse(e);
			extend(drag.draggable, {
				target: e.target,
				relatedTarget: e.target,
				relativeX: m.relativeX,
				relativeY: m.relativeY
			});
			events.add(dom(), "mousemove",	drag.mousemove);
			events.add(dom(), "mouseup",	drag.cancel);
		},
		mousemove: function(e){
			events.remove(dom(), "mousemove",	drag.mousemove);
			events.remove(dom(), "mouseup",		drag.cancel);

			if (events.dispatch(extend(events.mouse(e), drag.draggable, {
				type: "mousedrag"
			}))) {
				events.add(dom(), "mouseup", drag.mouseup);
				e.preventDefault();
			}
			else
				drag.cancel();
		},
		mouseup: function(e){
			events.dispatch(extend(events.mouse(e), drag.draggable, {
				type: "mousedrop",
				target: e.target
			}));
			drag.cancel();
		},
		cancel: function(){
			events.remove(dom(), "mousemove",	drag.mousemove);
			events.remove(dom(), "mouseup",		drag.cancel);
			events.remove(dom(), "mouseup",		drag.mouseup);
			for (var i in drag.draggable)
				delete drag.draggable[i];
		}
	});

/**
 * dragdrop example
 *
var draggableObject = events.dragdrop(dom());
events.add(dom().body, "mousedrag", function(e){
	log("start drag bomb "+ e.target);
});
events.add(dom().body, "mousedrop", function(e){
	log("drop bomb on "+ e.target +" from base "+ e.relatedTarget);
});
 */

	// Additional string methods
	extend(String.prototype, {
		stripTags: function(){
			return this
				.replace(/\s+|\&nbsp;/g, ' ')
				.replace(/ *<br[^\/>]*\/?> */ig, '\n')
				.replace(/<!--[\s\S]*?-->|<[^>]+>/g, '')
				.trim();
		},

		rescape: function() {
			return this.replace(re, "\\$1");
		},

		trim: function() {
			return this.replace(/^\s+|\s+$/g, '');
		},

		format: function() {
			// replace %n with arguments[n]
			// e.g. format("%1 %2%3 %2a %1%3", "she", "se", "lls");
			// ==> "she sells sea shells"
			// only %1 - %9 supported
			var args = arguments;
			return this.replace(FORMAT, function(match, index) {
				return index <= args.length ? args[index - 1] : match;
			});
		},

		toNumber: function(){
			return parseFloat(this
				.replace(/\s+/g, '')
				.replace(/[\,\.](?=.*[\,\.])/g, '')
				.replace(/[\,\.]/, '.')
				.replace(/^(?=-?)0*(?=\d)\.?/, ''), 10);
		},

		// format: ymd, dmy, mdy
		toDate: function(format) {
			format = format || 'ymd';
			var s = this.replace(/\D+/g, ' '),
				a = format.split(''),
				m = s.match(dateFormat[format]),
				d = {}, i = 0;

			for (; m && s && i < a.length; i++)
				d[a[i]] = m[i + 1] || m[i + 4];

			d = Date.parse(d.y +'/'+ d.m +'/'+ d.d);

			return isNaN(d) ? null : new Date(d);
		},

		htmlencode: function() {
			var div = dom().createElement('div');
			div.appendChild(dom().createTextNode(this));
			return div.innerHTML;
		},

		parseRGB: function(){
		  var m = color.reg.exec(this), h = color.hex, n = color.num;
		  return m && (m[1] && [n(m[1]), n(m[2]), n(m[3])] || m[4] && [h(m[4]), h(m[5]), h(m[6])]);
		}
	});

	// Additional number methods
	extend(Number.prototype, {
		format: function(fix, dec, tou) {
			var parts = this.toFixed(arguments.length ? fix : 2).split('.');
			if (tou) parts[0] = parts[0].replace(/\B(?=(...)*$)/g, tou);
			return parts.join(dec || '.');
		}
	});

	var re = /([\/()[\]{}|*+-.,^$?\\])/g;
	var FORMAT = /%([1-9])/g;
	var dateFormat = {
		dmy: /(\d{1,2}) (\d{1,2}) (\d{4})|(\d{2})(\d{2})(\d{4})/,
		ymd: /(\d{4}) (\d{1,2}) (\d{1,2})|(\d{4})(\d{2})(\d{2})/
	};
	dateFormat.mdy = dateFormat.dmy;

	var color = {
		reg: new RegExp('^\\s*(?:rgb\\s*\\(%1,%1,%1\\)|#%2%2%2)\\s*$'
			.format('\\s*([0-9]{1,3}|[0-9\\.]+\\%)\\s*', '([a-f0-9]|[a-f0-9]{2})'), 'i'),
		hex: function(s) { return parseInt('0x'+ (s.length > 1 ? s : s+s)); },
		num: function(s) { return parseInt(/\%/.test(s) ? parseFloat(s) * 255 / 100 : s); }
	};
};

var cookie = {
	set: function(name, value, options) {
		var a = [encodeURIComponent(name) +'='+ encodeURIComponent(value)];

		for (name in options) {
			value = options[name];
			a.push(name +'='
				+ ( name == 'expires'
					? new Date((new Date).getTime() + value * 86400000).toUTCString()
					: name == 'secure' ? name : value ));
		}
		document.cookie = a.join('; ');
	},
	get: function(name) {
		var res = String(document.cookie)
			.match(new RegExp('\\b'+ encodeURIComponent(name).rescape() +'=([^;]*)'));
		return res && decodeURIComponent(res[1]) || null;
	},
	rem: function(name, options) {
		this.set(name, '', extend({ expires: -1 }, options));
	}
};

var ease = function(p) { return - Math.cos(p * Math.PI) / 2 + .5 };


function setTimeline(fn, time, inter) {
	var start = (new Date).getTime();
	time = time || 1000;
	return inter = window.setInterval(function(){
		var pos = ((new Date).getTime() - start) / time;
		if (pos >= 1) { pos = 1; clearTimeline(inter); }
		fn(pos);
	}, inter || 13);
}
function clearTimeline(id) { return window.clearInterval(id); }


