// Copyright © 2009 by Kai Tamkun
// and licensed under the GNU AGPL

HTMLCanvasElement.prototype.c2d = function() {
	return this.getContext('2d');
};

String.prototype.add = function(text) {
	console.log(this);
	if (this == '') {
		return text;
	} else {
		return this+' '+text;
	};
};

Object.prototype.each = Array.prototype.each = function(code) {
	for (var i in this) {
		if (this.hasOwnProperty(i)) {
			code(this[i], i);
		};
	};
};

HTMLElement.prototype.center = function() {
	this.setStyle({display:'block',marginLeft:'auto',marginRight:'auto'});
};

HTMLElement.prototype.clear = function() {
	this.innerHTML = '';
};

HTMLTextAreaElement.prototype.clear = HTMLInputElement.prototype.clear = function() {
	this.value = '';
};

HTMLElement.prototype.showhide = function() {
	if (this.getStyle('display') != 'none') {
		this.setStyle({display:'none'});
	} else {
		this.setStyle({display:''});
	};
};

HTMLElement.prototype.show = function() {
	this.style.display = '';
};

HTMLElement.prototype.hide = function() {
	this.style.display = 'none';
};

HTMLElement.prototype.fade = function(opt) {
	if(!opt){varopt={};};if(!opt['from']){opt['from']=1.0;};if(!opt['to']){opt['to']=0.0;};if(!opt['res']){opt['res']=0.01;};if(!opt['duration']){opt['duration']=1.0;};if(!opt['onStart']){opt['onStart']=function(){};};if(!opt['onFinish']){opt['onFinish']=function(){};};
	var element = this;
	opt['onStart']();
	var interval = setInterval(function() {
		opt['from'] -= opt['res'];
		element.style.opacity = opt['from'];
		if (opt['from'] <= opt['to']) {
			opt['onFinish']();
			clearInterval(interval);
		};
	}, opt['duration']/(1/opt['res'])*1000);
};

HTMLElement.prototype.appear = function(opt) {
	if(!opt){var opt={};};if(!opt['from']){opt['from']=0.0;};if(!opt['to']){opt['to']=1.0;};if(!opt['res']){opt['res']=0.01;};if(!opt['duration']){opt['duration']=1.0;};if(!opt['onStart']){opt['onStart']=function(){};};if(!opt['onFinish']){opt['onFinish']=function(){};};
	var element = this;
	opt['onStart']();
	var interval = setInterval(function() {
		opt['from'] += opt['res'];
		element.style.opacity = opt['from'];
		if (opt['from'] >= opt['to']) {
			opt['onFinish']();
			clearInterval(interval);
		};
	}, opt['duration']/(1/opt['res'])*1000);
};

HTMLElement.prototype.getStyle = function(attr) {
	if (typeof this.currentStyle != 'undefined') {
		return this.currentStyle[attr];
	} else {
		return document.defaultView.getComputedStyle(this, null)[attr];
	};
};

HTMLElement.prototype.ghost = function() {
	this.setStyle({userSelect:'none',webkitUserSelect:'none'});
	this.addEventListener('mousedown', function(event) {
		event.preventDefault();
	}, 1);
};

HTMLElement.prototype.insert = function(text) {
	this.update($v(this)+text);
};

HTMLElement.prototype.insertLine = function(toinsert, prefix) {
	var prefix = prefix||'<br />\n';
	if (this.innerHTML == '') {
		this.insert(toinsert);
	} else {
		this.insert(prefix+toinsert);
	};
};

HTMLElement.prototype.setStyle = function(styles) {
	var element = this;
	styles.each(function(style, index) {
		element.style[index] = style;
	});
	return this;
};

HTMLElement.prototype.setAttributes = function(attributes) {
	for (i in attributes) {
		this.setAttribute(i, attributes[i]);
	};
	return this;
};

HTMLElement.prototype.update = function(content) {
	this.innerHTML = content;
	return this;
};

HTMLTextAreaElement.prototype.update = HTMLInputElement.prototype.update = function(content) {
	this.value = content;
	return this;
};

HTMLTextAreaElement.prototype.textHolder = HTMLInputElement.prototype.textHolder = function(text, blurColor, focusColor) {
	var t=$v(this), b='#777777', f='#000000', opt;
	if (typeof text == 'string') { // Backwards compatibility
		t = text;
		if (blurColor) {
			b = blurColor;
		};
		if (focusColor) {
			f = focusColor;
		};
	} else {
		opt = text;
		if (opt['text']) {
			t = opt['text'];
		};
		if (opt['blur']) {
			b = opt['blur'];
		};
		if (opt['focus']) {
			f = opt['focus'];
		};
	};
	this.addEventListener('focus', function() {
		if ($v(this) == t) {
			this.clear();
			this.style.color = f;
		};
	}, 1);
	this.addEventListener('blur', function() {
		if ($v(this) == '') {
			this.style.color = b;
			this.update(t);
		};
	}, 1);
	this.update(t);
	this.style.color = b;
};

String.prototype.e = function() {
	return Base64.encode(this);
};

String.prototype.xe = String.prototype.xmlEntities = function() {
	return this.replace(/\</g, '&lt;').replace(/\>/g, '&gt;');
};

function $b(dec, base) { // Attribution: http://ur1.ca/ju03
	var hex = '';
	var q = Math.floor(Math.abs(dec));
	var r;
	while (true) {
		r = q % base;
		hex = '0123456789abcdefghijklmnopqrstuvwxyz'.charAt(r)+hex;
		q = (q-r)/base;
		if (q == 0) {
			break;
		};
	};
	return ((dec<0) ? '-'+hex:hex);
};

function $c(tag, attributes, style) {
	return document.createElement(tag).setAttributes(attributes).setStyle(style);
};

function $posX(size) {
	return Math.round($sizeX()/2-size/2);
};

function $posY(size) {
	return Math.round($sizeY()/2-size/2);
};

function $v(id) {
	if ($(id).value != undefined) {
		return $(id).value;
	} else if ($(id).innerHTML != undefined) {
		return $(id).innerHTML;
	};
};

Parse = {
	XML:function(text) {
		try {
			var parser = new DOMParser();
			return parser.parseFromString(text, 'text/xml');
		} catch(e) {
			try {
				var parser = new ActiveXObject('Microsoft.XMLDOM');
				parser.async = 'false';
				parser.loadXML(text);
				return parser;
			} catch(f) {
				return false;
			};
		};
	}
};

function $(id) {
	this.browser = { // From Prototype
		IE:!!((window.attachEvent)&&(navigator.userAgent.indexOf('Opera')===-1)),
		Opera:(navigator.userAgent.indexOf('Opera')>-1),
		WebKit:(navigator.userAgent.indexOf('AppleWebKit/')>-1),
		Gecko:((navigator.userAgent.indexOf('Gecko') > -1)&&(navigator.userAgent.indexOf('KHTML')===-1)),
		MobileSafari:!!navigator.userAgent.match(/Apple.*Mobile.*Safari/)
	};
	this.tags = function(tag) { // tags
		return document.getElementsByTagName(tag);
	};
	this.bezier = function(t, p0, c1, c2, p3) {
		return {'x':(p0[0]*(t*t*t))+(c1[0]*(3*t*t*(1-t)))+(c2[0]*(3*t*(1-t)*(1-t)))+(p3[0]*((1-t)*(1-t)*(1-t))), 'y':(p0[1]*(t*t*t))+(c1[1]*(3*t*t*(1-t)))+(c2[1]*(3*t*(1-t)*(1-t)))+(p3[1]*((1-t)*(1-t)*(1-t)))};
	};
	this.body = function() {
		return $$.tags('body')[0];
	};
	this.create = function(what) {
		if (what == 'debug') {
			$$.body().appendChild($$.element('div').setStyle({position:'fixed',left:'0px',bottom:'0px',borderTopRightRadius:'3px',font:'13pt sans-serif',background:'rgba(150, 150, 150, 0.5)',color:'#000'}).setAttributes({id:'debug'}));
		};
	};
	this.d = function(text) {
		if ($('debug')) {
			$('debug').update(text);
		};
	};
	this.kailib = function() {
		return true;
	};
	this.lp = function(str, total, padwith) {
		padwith = (padwith)?padwith:'0';
		while (str.length < total) {
			str = padwith+str;
		};
		return str;
	};
	this.morph = function(opt) { // Not working right now... ^_^;
		if (!opt) {
			opt = {};
		};
		if (!opt['node']) {
			return 2;
		};
		if (!opt['duration']) {
			opt['duration'] = 1.0;
		};
		if (!opt['type']) {
			return 3;
		};
		if ((!opt['from'])||(!opt['to'])) {
			return 4;
		};
		if ((opt['type']!='opacity')&&(opt['type']!='background')&&(opt['type']!='color')&&(opt['type']!='left')&&(opt['type']!='top')) {
			return 5;
		};
		var bezier = [];
		var times = [0];
		var duration = parseInt(opt['duration']*1000);
		for (var i = 0; i <= 100; i++) {
			var b = $$.bezier(i/100, [0, 0], [100, 0], [20, 10], [80, 10]);
			console.log(i+': '+b['x']);
			bezier[i] = parseInt(b['y']*10);
		};
		for (var i = 1; i <= duration; i++) {
			times[i] = bezier[i+1]-bezier[i];
		};
		console.log(bezier);
	};
	this.sizeX = function() {
		return document.documentElement.clientWidth;
	};
	this.sizeY = function() {
		return document.documentElement.clientHeight;
	};
	this.time = function() {
		var time = new Date;
		var time = time.getTime();
		return parseInt(time/1000);
	};
	this.element = function(tag) {
		return document.createElement(tag);
	};
	this.mouse = function(hash) {
		event = window.event;
		try {
			$$.mx = event.pageX;
			$$.my = event.pageY;
		} catch(_) {
			$$.mx = event.clientX+document.body.scrollLeft;
			$$.my = event.clientY+document.body.scrollTop;
		};
		if (hash) {
			return {'x':$$.mx, 'y':$$.my};
		} else {
			return [$$.mx, $$.my];
		};
	};
	// Attribution: http://ur1.ca/ju04
	// Ran through Closure Compiler
	this.selector = function(j){if(!document.getElementsByTagName)return[];j=j.split(" ");for(var a=new Array(document),m=0;m<j.length;m++){token=j[m].replace(/^\s+/,"").replace(/\s+$/,"");if(token.indexOf("#")>-1){var b=token.split("#"),c=b[0];a=document.getElementById(b[1]);if(c&&a.nodeName.toLowerCase()!=c)return[];a=new Array(a)}else if(token.indexOf(".")>-1){b=token.split(".");c=b[0];var g=b[1];c||(c="*");b=[];for(var d=0,f=0;f<a.length;f++){var i;i=c=="*"?getAllChildren(a[f]):a[f].getElementsByTagName(c);for(var h=0;h<i.length;h++)b[d++]=i[h]}a=[];for(d=c=0;d<b.length;d++)if(b[d].className&&b[d].className.match(new RegExp("\\b"+g+"\\b")))a[c++]=b[d]}else if(token.match(/^(\w*)\[(\w+)([=~\|\^\$\*]?)=?"?([^\]"]*)"?\]$/)){c=RegExp.$1;var k=RegExp.$2;g=RegExp.$3;var l=RegExp.$4;c||(c="*");b=[];for(f=d=0;f<a.length;f++){i=c=="*"?getAllChildren(a[f]):a[f].getElementsByTagName(c);for(h=0;h<i.length;h++)b[d++]=i[h]}a=[];c=0;switch(g){case "=":g=function(e){return e.getAttribute(k)==l};break;case "~":g=function(e){return e.getAttribute(k).match(new RegExp("\\b"+l+"\\b"))};break;case "|":g=function(e){return e.getAttribute(k).match(new RegExp("^"+l+"-?"))};break;case "^":g=function(e){return e.getAttribute(k).indexOf(l)==0};break;case "$":g=function(e){return e.getAttribute(k).lastIndexOf(l)==e.getAttribute(k).length-l.length};break;case "*":g=function(e){return e.getAttribute(k).indexOf(l)>-1};break;default:g=function(e){return e.getAttribute(k)}}a=[];for(d=c=0;d<b.length;d++)if(g(b[d]))a[c++]=b[d]}else{c=token;b=[];for(f=d=0;f<a.length;f++){i=a[f].getElementsByTagName(c);for(h=0;h<i.length;h++)b[d++]=i[h]}a=b}}return a};
	this.cookies = function() {
		function set(name, value, time, path) {
			var date = new Date();
			if (time) {
				date.setTime(time);
			};
			if (!path) {
				path = '/';
			};
			var utc = date.toUTCString();
			document.cookie = name+'='+testcookie+'; expires='+utc+'; path='+path;
		};
	};
	this.st = function(opt) { // Simple Transition
		if(!opt){opt={};};if(!opt['vars']){opt['vars']={};};if(!opt['from']){opt['from']=0;};if(!opt['onFinish']){opt['onFinish']=function(){};};if(!opt['duration']){opt['duration']=1;};if(!opt['res']){opt['res']=1;};if(typeof opt['to'] == 'undefined'){opt['to']=100;};if(!opt['code']){opt['code']=function(){};};if(opt['from']<opt['to']){var up=1;}else{var up=0;};var from=opt['from'];var to=opt['to'];var delay=opt['duration']/(Math.abs(opt['from']-opt['to'])/opt['res'])*1000;
		var interval = setInterval(function() {
			if (up) {
				from += opt['res'];
				opt['code'](from, opt['vars']);
				if (from >= opt['to']) {
					clearInterval(interval);
					opt['onFinish']();
				};
			} else {
				from -= opt['res'];
				opt['code'](from, opt['vars']);
				if (from <= opt['to']) {
					clearInterval(interval);
					opt['onFinish']();
				};
			};
		}, delay);
	};
	this.xhr = function(opt) {
		var xhr;
		try {
			try {
				xhr = new XMLHttpRequest();
			} catch(e0) {
				try {
					xhr = new ActiveXObject('Msxml2.XMLHTTP');
				} catch(e1) {
					try {
						xhr = new ActiveXObject('Microsoft.XMLHTTP');
					} catch(e2) {
						try {
							xhr = newActiveXObject('Msxml2.XMLHTTP.6.0');
						} catch(e3) {
							try {
								xhr = newActiveXObject('Msxml2.XMLHTTP.3.0');
							} catch(e4) {
								return 0;
							};
						};
					};
				};
			};
		} catch(e) {
			alert(e);
		};
		if(!opt['method']){opt['method']='get';};
		if (!((opt['url'])&&((opt['method']=='get')||((opt['method']=='post')&&(opt['data'])))&&(opt['onSuccess']))) {
			return false;
		};
		xhr.onreadystatechange = function() {
			try {
				if (xhr.readyState == 4) {
					opt['onSuccess'](xhr.responseText, xhr);
				} else if(((xhr.readyState == 18)||(xhr.readyState == 19)||(xhr.readyState == 20))&&(opt['onFailure'])) {
					opt['onFailure'](xhr.readyState);
				};
			} catch(f) {
				alert(f);
			};
		};
		if (opt['method'] == 'get') {
			xhr.open('GET', opt['url'], true);
			xhr.send(null);
		} else {
			xhr.open('POST', opt['url'], true);
			xhr.setRequestHeader('Content-Type', 'text/plain;charset=UTF-8');
			xhr.send(opt['data']);
		};
	};
	if (id) {
		if (typeof id == 'string') {
			return document.getElementById(id);
		} else {
			return id;
		};
	};
};

$$ = new $();

function getAllChildren(j){return j.all?j.all:j.getElementsByTagName('*')};
function $rgbToHex(text) {
	text = text.replace(/^[^0-9]*|[^0-9]*$/, '');
	text = text.split(/[^0-9]+/);
	return '#'+$b(text[0], 16)+$b(text[1], 10)+$b(text[2], 10); // Can't use .each() in case of an alpha channel.
};

// Attribution: http://ur1.ca/ju1b
Base64={_keyStr:"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=",encode:function(b){var d="",c,a,f,g,h,e,i=0;for(b=Base64._utf8_encode(b);i<b.length;){c=b.charCodeAt(i++);a=b.charCodeAt(i++);f=b.charCodeAt(i++);g=c>>2;c=(c&3)<<4|a>>4;h=(a&15)<<2|f>>6;e=f&63;if(isNaN(a))h=e=64;else if(isNaN(f))e=64;d=d+this._keyStr.charAt(g)+this._keyStr.charAt(c)+this._keyStr.charAt(h)+this._keyStr.charAt(e)}return d},decode:function(b){var d="",c,a,f,g,h,e=0;for(b=b.replace(/[^A-Za-z0-9\+\/\=]/g,"");e<b.length;){c=this._keyStr.indexOf(b.charAt(e++));a=this._keyStr.indexOf(b.charAt(e++));g=this._keyStr.indexOf(b.charAt(e++));h=this._keyStr.indexOf(b.charAt(e++));c=c<<2|a>>4;a=(a&15)<<4|g>>2;f=(g&3)<<6|h;d+=String.fromCharCode(c);if(g!=64)d+=String.fromCharCode(a);if(h!=64)d+=String.fromCharCode(f)}return d=Base64._utf8_decode(d)},_utf8_encode:function(b){b=b.replace(/\r\n/g,"\n");for(var d="",c=0;c<b.length;c++){var a=b.charCodeAt(c);if(a<128)d+=String.fromCharCode(a);else{if(a>127&&a<2048)d+=String.fromCharCode(a>>6|192);else{d+=String.fromCharCode(a>>12|224);d+=String.fromCharCode(a>>6&63|128)}d+=String.fromCharCode(a&63|128)}}return d},_utf8_decode:function(b){for(var d="",c=0,a=c1=c2=0;c<b.length;){a=b.charCodeAt(c);if(a<128){d+=String.fromCharCode(a);c++}else if(a>191&&a<224){c2=b.charCodeAt(c+1);d+=String.fromCharCode((a&31)<<6|c2&63);c+=2}else{c2=b.charCodeAt(c+1);c3=b.charCodeAt(c+2);d+=String.fromCharCode((a&15)<<12|(c2&63)<<6|c3&63);c+=3}}return d}};

function $buffer(start) {
	this.text = start||'';
	this.history = [this.text];
	this.historyindex = 1;
	this.pos = this.text.length;
	function setAbsolutePosition(newpos) {
		this.pos = newpos;
	};
	function setRelativePosition(newpos) {
		this.pos += newpos;
	};
	function undo(amounts, force) {
		var a = amounts||1;
		this.historyIndex -= a;
		if (this.historyIndex < 1) {
			this.historyIndex = 1;
			if (!force) {
				return;
			};
		};
		this.text = history[this.historyIndex];
		return this.text;
	};
	function redo(amounts, force) {
		var a = amounts||1;
		this.historyIndex += a;
		if (this.historyIndex < 1) {
			this.historyIndex = 1;
			if (!force) {
				return;
			};
		};
		this.text = history[this.historyIndex];
		return this.text;
	};
	function insert(toinsert) {
		var before = this.text.substr(0, this.pos);
		var after = this.text.substr(this.pos);
		this.text = before+toinsert+after;
		this.pos += toinsert.length;
		this.history.insert(this.historyIndex, this.text);
		this.historyIndex++;
		return this.text;
	};
	function insertLine(toinsert, prefix) {
		var prefix = prefix||'\n';
		if (!this.text) {
			this.insert(toinsert);
		} else {
			this.insert(toinsert, prefix);
		};
	};
	function clear() {
		this.text = '';
		this.history.insert(this.historyIndex, '');
		this.historyIndex++;
	};
	function getText() {
		return this.text;
	};
};

Array.prototype.insertAt = function(pos, val, force) {
	if ((pos > this.length)&&(!force)) {
		return false;
	};
	for (var i = this.length-1; i >= pos; i--) {
		this[i+1] = this[i];
	};
	this[pos] = val;
	return val;
};