K = {
	
	require: function(fileName) {
		switch (fileName.substr(fileName.lastIndexOf('.') + 1).toLowerCase()) {
			case 'js':
				document.write('<script type="text/javascript" src="/js/' + fileName + '?' + REVISION_NUMBER + '"></script>');	
				break;
			case 'css':
				document.write('<link type="text/css" rel="stylesheet" href="/css/' + fileName + '?' + REVISION_NUMBER + '" />');
				break;
		}
	}
	
};

K.Page = {};





K.Utility = {
	
	timestampToDate: function(timestamp) {
		
		var match = timestamp.match(/^(\d{4})\D(\d{2})\D(\d{2})\D(\d{2})\D(\d{2})\D(\d{2})$/);
		
		if (match) {
			
			var date = new Date(
				parseInt(match[1], 10),
				parseInt(match[2], 10) - 1,
				parseInt(match[3], 10),
				parseInt(match[4], 10),
				parseInt(match[5], 10),
				parseInt(match[6], 10)
			);
						
			return date;
			
		}
		
	}
	
};





K.Element = function(tagName, attributes, content) {

	var element;

	if (Prototype.Browser.IE) {
		var html = '<' + tagName;
		if (attributes != undefined && attributes && (attributes.type || attributes.name)) {
			html += (attributes.type ? ' type="' + attributes.type + '"' : '') + (attributes.name ? ' name="' + attributes.name + '"' : '');
			delete attributes.type, attributes.name;
		}
		html += '>';
		element = document.createElement(html);
	}
	else {
		element = document.createElement(tagName);
	}

	if (attributes) {
		for (var key in attributes) {
			var attribute = document.createAttribute(key);
			attribute.value = attributes[key];
			element.setAttributeNode(attribute);
		}
	}

	if (content) {
		if (!(content instanceof Array)) {
			content = [content];
		}
		for (var i = 0; i < content.length; i++) {
			if (content[i]) {
				if (content[i].nodeType) {
					element.appendChild(content[i]);
				}
				else {
					var divElement = document.createElement('div');
					divElement.innerHTML = content[i].toString().replace(/\</g, '&lt;').replace(/\>/g, '&gt;');
					while (divElement.firstChild) {
						element.appendChild(divElement.firstChild);
					}
					delete divElement;
				}
			}
		}
	}

	return element;

};





K.Effect = {
	
	toggle: function(element) {
		
		if (Element.visible(element)) {
			
			new Effect.Appear(
				element, 
				{
					from: 1,
					to: 0,
					duration: 0.25, 
					queue: 'end', 
					afterFinish: function() {
						
						element.style.visibility = 'hidden';
						
						new Effect.SlideUp(element, {duration: 0.5, queue: 'end'});
				
					}
				}
			);
			
		}
		else {
			
			element.style.visibility = 'hidden';
		
			new Effect.SlideDown(
				element, 
				{
					duration: 0.5, 
					queue: 'end', 
					afterFinish: function() {
						
						Element.setOpacity(element, 0);
						
						element.style.visibility = 'visible';
						
						new Effect.Appear(element, {duration: 0.25, queue: 'end'});
				
					}
				}
			);
			
		}
		
	}	
	
};





K.Form = {

	blurrize: function(element) {

		if (element.tagName == 'FORM') {
			for (var i = 0; i < element.elements.length; i++) {
				this.blurrize(element.elements[i]);
			}
		}
		else if ((element.type == 'text' || element.type == 'textarea') && element.alt) {
			this.blur(element);
			Event.observe(element, 'focus', this.focus.bind(this, element));
			Event.observe(element, 'blur', this.blur.bind(this, element));
		}

	},

	focus: function(element) {

		if (element.tagName == 'FORM') {
			for (var i = 0; i < element.elements.length; i++) {
				this.focus(element.elements[i]);
			}
		}
		else if ((element.type == 'text' || element.type == 'textarea') && element.alt && element.value == element.alt) {
			element.value = '';
			Element.removeClassName(element, 'blur');
		}

	},

	blur: function(element) {

		if (element.tagName == 'FORM') {
			for (var i = 0; i < element.elements.length; i++) {
				this.blur(element.elements[i]);
			}
		}
		else if ((element.type == 'text' || element.type == 'textarea') && element.alt && (element.value == element.alt || !element.value)) {
			Element.addClassName(element, 'blur');
			element.value = element.alt;
		}

	}

};





K.Idle = {
	
	count: 0, 
	
	iterations: 18, 
	
	initialize: function() {
		this.iterate.bind(this).delay(600);
	},
	
	iterate: function() {
		new Ajax.Request('/session/idle', {method: 'post'});
		this.count++;
		if (this.count < this.iterations) {
			this.initialize();
		}
	}	
	
};





K.Login = {

	initialize: function(object) {
		
		Object.extend(this, object);
		
		var focusElement;
		
		for (var i = 0; i < this.focusElements.length; i++) {
			
			var element = this.focusElements[i];
			
			if (element.type == 'text') {
				focusElement = element;
			}
			else if (element.type == 'password') {
				if (focusElement) {
					if (focusElement.value) {
						focusElement = element;
					}
				}
				else {
					focusElement = element;
				}
			}
			
		}
		
		if (focusElement) {
			focusElement.focus();	
		}
		
	}
	
};





K.QuickSearch = {
	
	initialize: function(object) {
		
		Object.extend(this, object);
		
		Event.observe(this.openElement, 'click', this.open.bindAsEventListener(this));
		
	}, 
	
	open: function(event) {
		
		Event.stop(event);
		
		if (!this.box) {
			
			this.box = new K.Window({tip: true});
			
			this.formElement = K.Element('form', {'action': '/search/quick', 'method': 'get', 'id': 'search_quick'},
				[
					this.inputElement = K.Element('input', {'type': 'text', 'name': 'username', 'size': 10, 'maxlength': 40, 'class': 'text'}),
					K.Buttonizer.create({text: 'Hae', type: 'submit', className: 'button_small'})
				]
			);
			
			this.box.setContent(this.formElement);
			this.box.setFocusElement(this.inputElement);
			
		}
		
		this.box.positionByEvent(event, -15, 15).enable();
		
	}
	
};





K.Messenger = {
	
	enabled: true, 
	
	initialize: function(object) {
		
		Object.extend(this, object);
		
		
		if (this.contentElement) {
			
			this.filter(this.contentElement);
			
			Element.show(this.contentElement);
			
		}
		
		
		if (this.navigationElements.length > 0) {
			for (var i = 0; i < this.navigationElements.length; i++) {
				Event.observe(this.navigationElements[i], 'click', this.navigate.bindAsEventListener(this, i));	
			}
		}
		
		this.removeElements = Element.select(this.element, 'a.remove_message');
		
		for (var i = 0; i < this.removeElements.length; i++) {
			Event.observe(this.removeElements[i], 'click', this.removeMessage.bindAsEventListener(this, i));
		}
		
		Event.observe(this.formElement, 'submit', this.onSubmit.bindAsEventListener(this));
		
		if (this.removeElement) {
			Event.observe(this.removeElement, 'click', this.removeMessagesConfirm.bindAsEventListener(this));
		}
		
		K.Smiliezer.initialize({
			textElement: this.messageElement,
			smiliesElement: this.smiliesElement,
			onUpdate: this.update.bind(this)	
		});
		
		this.textareaExtender = new K.Form.TextareaExtender({
			element: this.messageElement,
			containerElement: this.messageElement.parentNode,
			onSubmit: this.submit.bind(this)
		});
		
		this.textareaCounter = new K.TextareaCounter({
			limit: 2500,
			hideBefore: 1000,
			element: this.messageElement,
			counterElement: this.counterElement
		});
		
	}, 
	
	update: function() {
		
		this.textareaExtender.update();
		this.textareaCounter.update();
		
	},
	
	navigate: function(event, index) {
		
		Event.stop(event);
		
		location.replace(this.navigationElements[index].href);
		
	},
	
	isSubmittable: function() {
		
		if (this.textareaCounter.isError()) {
			
			(new K.ConfirmWindow(
				'Liikaa tekstiä',
				{
					description: 'Viestin maksimipituus on 2500 merkkiä. Poista tekstiä, kunnes punainen reunus katoaa.',
					onClose: (function() {
						this.messageElement.focus();
					}).bind(this)
				}
			)).enable();
			
		}
		
		return this.enabled && this.messageElement.value.length > 0 && !this.textareaCounter.isError();
		
	},
	
	onSubmit: function(event) {
		
		if (!this.isSubmittable()) {
			Event.stop(event);
			return;
		}
			
		this.submitElement.disabled = true;
		this.enabled = false;
		
	}, 
	
	submit: function() {
		
		if (this.isSubmittable()) {
			this.submitElement.disabled = true;
			this.enabled = false;
			this.formElement.submit();
		}
		
	},
	
	filter: function(element) {
		
		var elements = Element.select(element, 'td.message');
		
		for (var i = 0; i < elements.length; i++) {
			K.Filter.filter(elements[i], 'smiley', 'profanity', 'hyperlink', 'wrap');
		}
		
	},
	
	removeMessage: function(event, index) {
	
		Event.stop(event);
		
		var element = this.removeElements[index];
		
		new Ajax.Request(element.href, {method: 'get'});
		
		Element.hide(Element.up(element, 'table'));
			
	},
	
	removeMessagesConfirm: function(event) {
	
		Event.stop(event);
		
		(new K.ConfirmWindow(
			'Viestin poisto',
			{
				description: 'Haluatko varmasti poistaa kaikki viestit?',
				accept: 'Kyllä',
				decline: 'En',
				onAccept: this.removeMessages.bind(this)
			}
		)).enable();
			
	},
	
	removeMessages: function() {
		
		location = this.removeElement.href;
		
	}
	
};





K.Filterizer = {
	
	map: {
		censorize: 'profanity',
		linkify: 'hyperlink'
	},

	filter: function() {
		
		for (var i = 0; i < arguments.length; i++) {
			
			var 
				className = arguments[i],
				elements = Element.select(document.body, '.' + className);
			
			for (var j = 0; j < elements.length; j++) {
				K.Filter.filter(elements[j], this.map[className], 'wrap');
				Element.removeClassName(elements[j], className);
			}
			
		}
		
	}
	
};





K.Filter = {
	
	filter: function(element) {
		
		for (var i = 1; i < arguments.length; i++) {
		
			var filter, parameters;
			
			if (arguments[i] instanceof Array) {
				filter = arguments[i][0];
				parameters = arguments[i][1];
			}
			else {
				filter = arguments[i];
				parameters = {};
			}
			
			var fragment = document.createDocumentFragment();
					
			while (element.firstChild) {
				fragment.appendChild(element.firstChild);	
			}
				
			while (fragment.firstChild) {
				
				if (fragment.firstChild.nodeType == 3) {
					
					var textFragment = this[filter](fragment.firstChild, parameters);
					
					if (textFragment.nodeType == 11) {
						while (textFragment.firstChild) {
							element.appendChild(textFragment.firstChild);
						}
						fragment.removeChild(fragment.firstChild);
					}
					else {
						element.appendChild(textFragment);
					}
					
				}	
				else {
					element.appendChild(fragment.firstChild);
				}
				
			}
			
		}
		
	},
	
	hyperlink: function(element, parameters) {
		
		parameters.embed = parameters.embed == undefined || parameters.embed;
		parameters.length = parameters.length || 50;
		parameters.size = parameters.size || 1;
		
		var 
			text = element.nodeValue,
			matches = text.match(/http:\/\/[^ ]*/gi),
			sizes = [[320, 265], [425, 344]];
			
		if (matches && matches.length > 0) {
			
			var 
				fragment = document.createDocumentFragment(),
				lastIndex = 0;
			
			for (var i = 0; i < matches.length; i++) {
				
				var match = matches[i];
				
				if (['.', ',', '!', '?'].indexOf(match.substr(-1)) >= 0) {
					match = match.substr(0, match.length - 1);	
				}
				
				var 
					index = text.indexOf(match, lastIndex),
					length = match.length;
				
				fragment.appendChild(document.createTextNode(text.substr(lastIndex, index - lastIndex)));

				if (parameters.embed && match.search(/youtube\.com\/watch\?v\=/i) >= 0) { // Youtube
				
					var 
						source = match.replace(/^.*watch\?v\=([\w\-]*).*?$/i, 'http://www.youtube.com/v/$1'),
						divElement = document.createElement('div'),
						width = sizes[parameters.size - 1][0],
						height = sizes[parameters.size - 1][1];
					
					divElement.innerHTML = 
						'<object width="' + width + '" height="' + height + '">' + 
							'<param name="movie" value="' + source + '"></param>' + 
							'<param name="allowFullScreen" value="true"></param>' + 
							'<param name="allowscriptaccess" value="always"></param>' +
							'<embed src="' + source + '" type="application/x-shockwave-flash" allowscriptaccess="always" allowfullscreen="true" width="' + width + '" height="' + height + '"></embed>' +
						'</object>';
						
					fragment.appendChild(divElement.firstChild);
					
				}
				else {			
					var hyperlinkElement = document.createElement('a');
					hyperlinkElement.href = match;
					hyperlinkElement.appendChild(document.createTextNode(match.length > parameters.length ? match.substr(0, parameters.length) + '...' : match));
					fragment.appendChild(hyperlinkElement);
				}
				
				lastIndex = index + length;
					
			}
			
			fragment.appendChild(document.createTextNode(text.substr(lastIndex, text.length - lastIndex)));
			
			return fragment;
				
		}
		else {
		
			return element;
			
		}
			
	},
	
	smiley: function(element, parameters) {
		
		var 
			text = element.nodeValue,
			matches = text.match(new RegExp('\\x5B(' + K.Smiliezer.smilies.join('|') + ')\\x5D', 'g'));
			
		if (matches && matches.length > 0) {
			
			var 
				fragment = document.createDocumentFragment(),
				lastIndex = 0;
			
			for (var i = 0; i < matches.length; i++) {
				
				var 
					match = matches[i],
					index = text.indexOf(match, lastIndex),
					length = match.length;
				
				fragment.appendChild(document.createTextNode(text.substr(lastIndex, index - lastIndex)));

				var smileyElement = document.createElement('img');
				smileyElement.src = '/images/smilies/icon_' + match.substr(1, match.length - 2) + '.gif';
				smileyElement.className = 'smiley';
				fragment.appendChild(smileyElement);
				
				lastIndex = index + length;
					
			}
			
			fragment.appendChild(document.createTextNode(text.substr(lastIndex, text.length - lastIndex)));
			
			return fragment;
				
		}
		else {
		
			return element;
			
		}
		
	},
	
	profanity: function(element, parameters) {
		
		var 
			text = element.nodeValue,
			matches = text.match(/\b(hintti|hinti|huora|jumalauta|kulli|kyrpä|lutka|mulkku|mulku|neekeri|nekru|nussi|pillu|pimppi|runkata|runkku|runku|runkkari|ryssi|ryssä|saatana|tussu|vittu|vitu).*?\b/gi);
			
		if (matches && matches.length > 0) {
			
			var 
				fragment = document.createDocumentFragment(),
				lastIndex = 0;
			
			for (var i = 0; i < matches.length; i++) {
				
				var 
					match = matches[i],
					index = text.indexOf(match, lastIndex),
					length = match.length;
				
				fragment.appendChild(document.createTextNode(text.substr(lastIndex, index - lastIndex) + '*****'));

				lastIndex = index + length;
					
			}
			
			fragment.appendChild(document.createTextNode(text.substr(lastIndex, text.length - lastIndex)));
			
			return fragment;
				
		}
		else {
		
			return element;
			
		}
		
	},
	
	wrap: function(element, parameters) {
		
		parameters.length = parameters.length || 50;
		
		var 
			text = element.nodeValue,
			matches = text.match(/\S{50,}/g);
			
		if (matches && matches.length > 0) {
			
			var 
				fragment = document.createDocumentFragment(),
				lastIndex = 0;
			
			for (var i = 0; i < matches.length; i++) {
				
				var 
					match = matches[i],
					index = text.indexOf(match, lastIndex),
					length = match.length;
				
				fragment.appendChild(document.createTextNode(text.substr(lastIndex, index - lastIndex)));
				fragment.appendChild(document.createTextNode(text.substr(index, length).wrap(parameters.length, ' ')));

				lastIndex = index + length;
					
			}
			
			fragment.appendChild(document.createTextNode(text.substr(lastIndex, text.length - lastIndex)));
			
			return fragment;
				
		}
		else {
		
			return element;
			
		}
		
	},
	
	image: function(element, parameters) {
		
		var 
			text = element.nodeValue,
			matches = text.match(/\x5Bimage:(\d+)\x5D/gi);
			
		if (matches && matches.length > 0) {
			
			var 
				fragment = document.createDocumentFragment(),
				lastIndex = 0;
			
			for (var i = 0; i < matches.length; i++) {
				
				var 
					match = matches[i],
					index = text.indexOf(match, lastIndex),
					length = match.length;
				
				fragment.appendChild(document.createTextNode(text.substr(lastIndex, index - lastIndex)));
				
				var 
					imageElement = document.createElement('img'),
					imageId = match.replace(/\D/g, '');
					
				imageElement.src = '/i/1/' + Math.floor(imageId / 1000) + '/' + imageId + '.jpg';
				
				fragment.appendChild(imageElement);

				lastIndex = index + length;
					
			}
			
			fragment.appendChild(document.createTextNode(text.substr(lastIndex, text.length - lastIndex)));
			
			return fragment;
				
		}
		else {
		
			return element;
			
		}		
		
	}
	
};





K.Smiliezer = {

	smilies: [
		'biggrin', 
		'confused', 
		'cool', 
		'cry', 
		'eek', 
		'evil', 
		'lol', 
		'mad', 
		'mrgreen', 
		'neutral', 
		'razz', 
		'redface', 
		'rolleyes', 
		'sad', 
		'smile', 
		'surprised', 
		'twisted', 
		'wink', 
		'heart'
	],
	
	initialize: function(object) {
		
		Object.extend(this, object);
		
		for (var i = 0; i < this.smilies.length; i++) {
			var smileyElement = K.Element('img', {'src': '/images/smilies/icon_' + this.smilies[i] + '.gif', 'class': 'smiley'});
			this.smiliesElement.appendChild(smileyElement);
			Event.observe(smileyElement, 'click', this.addSmiley.bindAsEventListener(this, i));
		}
		
	},
	
	addSmiley: function(event, index) {
	
		Event.stop(event);	

		if (this.textElement.type == 'textarea') {
			var 
				scrollTop = this.textElement.scrollTop,
				scrollLeft = this.textElement.scrollLeft;
		}
		
		if (Prototype.Browser.IE) {
			this.textElement.focus();
			document.selection.createRange().text = '[' + this.smilies[index] + ']';
			this.textElement.caretPos = document.selection.createRange().duplicate();
		}
		else {
			var i = this.textElement.selectionStart;
			this.textElement.value = this.textElement.value.substr(0, i) + '[' + this.smilies[index] + ']' + this.textElement.value.substr(i);
			this.textElement.focus();
			this.textElement.selectionStart = this.textElement.selectionEnd = i + this.smilies[index].length + 2;
		}
		
		if (this.textElement.type == 'textarea') {
			if (scrollTop) {
				this.textElement.scrollTop = scrollTop;
			}
			if (scrollLeft) {
				this.textElement.scrollLeft = scrollLeft;
			}
		}
		
		if (this.onUpdate) {
			this.onUpdate();	
		}
		
	}

};





K.Form.TextareaExtender = Class.create({
	
	initialize: function(object) {

		Object.extend(this, object);
		
		Event.observe(this.element, 'keydown', this.onKeyDown.bindAsEventListener(this));
		Event.observe(this.element, 'keyup', this.onKeyUp.bindAsEventListener(this));
		
		this.dummyElement = K.Element('textarea', {'cols': this.element.cols, 'rows': this.element.rows, 'value': ' '});
		Element.setStyle(this.dummyElement, {position: 'absolute', left: '-10000px'});
	  
		this.element.parentNode.appendChild(this.dummyElement);
		
		this.initialHeight = this.dummyElement.scrollHeight;
		this.lineHeight = parseInt(Element.getStyle(this.element, 'line-height'));
		
	},
	
	onKeyDown: function(event) {
		
		if (event.keyCode == Event.KEY_RETURN) {
			Event.stop(event);
			if (this.onSubmit) {
				this.onSubmit();
			}
			return;
		}
		
	 	this.update();   
		
	},
	
	onKeyUp: function(event) {
	  
		this.update();
		  
	},
	
	update: function() {
		
		this.dummyElement.value = this.element.value || ' ';
		
		// Must do something here in between before getting the scrollHeight to give IE some time to render the text
		if (this.element.scrollTop > 0) {
			this.element.scrollTop = 0; // IE fix
		}
		
		var height = Math.round(this.dummyElement.scrollHeight / this.lineHeight) * this.lineHeight;
		
		if (this.height != height) {
			
			this.height = height;
			
			new Effect.Morph(this.element, {style: {height: this.height + 'px'}, duration: 0.05, queue: 'end'});
			
			if (this.height > this.initialHeight) {
				Element.addClassName(this.containerElement, 'textarea_extended');
			}
			else {
				Element.removeClassName(this.containerElement, 'textarea_extended');
			}
			
		}
		
	}
	
});





K.TextareaCounter = Class.create({

	error: false, 
	
	initialize: function(object) {
		
		Object.extend(this, object);
		
		this.update();
		
		Event.observe(this.element, 'keydown', this.update.bindAsEventListener(this));
		Event.observe(this.element, 'keyup', this.update.bindAsEventListener(this));
		
	},
	
	update: function() {
		
		if (this.count != this.element.value.length) {
		
			this.count = this.element.value.length;
			
			this.counterElement.firstChild.nodeValue = (this.limit - this.count) + ' / ' + this.limit;
			
			if (this.count > this.limit) {
				if (!this.error) {
					this.error = true;
					Element.addClassName(this.element, 'errorized');
				}
			}
			else {
				if (this.error) {
					this.error = false;
					Element.removeClassName(this.element, 'errorized');
				}
			}
			
			if (this.hideBefore) {
				if (this.count >= this.hideBefore) {
					Element.show(this.counterElement);
				}
				else {
					Element.hide(this.counterElement);
				}
			}
			
		}
		
	},
	
	isError: function() {
		return this.error;	
	}
	
});





K.ClassCycler = {
	
	cycle: function(elements, element_name, class_names) {
		
		$A(elements).each(function(element) {
			var class_index = 0;
			$A(Element.select(element, element_name)).each(function(child_element) {
				Element.addClassName(child_element, class_names[class_index]);
				class_index = class_index < class_names.length - 1 ? class_index + 1 : 0;
			});
		});
		
	}	
	
};





K.Buttonizer = {
	
	create: function(object) {
	
		var textElement = document.createTextNode(object.text || ''); 
		
		var innestElement = document.createElement('span');
		innestElement.className = 'button_innest';
		innestElement.appendChild(textElement)
		
		var innerElement = document.createElement('span');
		innerElement.className = 'button_inner';
		innerElement.appendChild(innestElement)
		
		var element = document.createElement('span');
		element.className = 'button';
		element.appendChild(innerElement);
		
		var buttonElement;
		
		if (object.type) {
			buttonElement = K.Element('button', {type: object.type}); 
		}
		else {
			buttonElement = K.Element('a');	
			if (object.href) {
				buttonElement.href = object.href;	
			}
		}
		
		buttonElement.className = 'button' + (object.className ? ' ' + object.className : '');
		
		buttonElement.appendChild(element);
		
		return buttonElement;
		
	}
	
};





K.Modalizer = {
	
	observing: false,
	
	tickets: [],

	add: function(ticket) {
		if (!this.observing) {
			this.observing = true;
			Event.observe(document.body, 'keydown', this.onKeyDown.bindAsEventListener(this));
		}
		if (this.tickets.indexOf(ticket) < 0) {
			this.tickets.push(ticket);
		}
	}, 
	
	remove: function(ticket) {
		var index = this.tickets.indexOf(ticket);
		if (index >= 0) {
			this.tickets.splice(index, 1);	
		}
	},
	
	onKeyDown: function(event) {
		if (this.tickets.length > 0) {
			Event.stop(event);	
		}
	}
	
};





K.Window = Class.create({
	
	enabled: false,
	
	modal: false, 
	
	centerized: false, 
	
	closable: true, 
	
	tip: false,
	
	position: [0, 0],
	
	size: [0, 0],
	
	initialize: function(object) {
		
		Object.extend(this, object);
		
		this.create();
		
	}, 
	
	create: function() {
		
		if (this.modal) {
			this.blockerElement = K.Element('div', {'class': 'window_blocker'});
		}
		
		this.centerizerElement = K.Element('div', null, 
			this.boxElement = K.Element('div', {'class': 'window_box'}, 
				this.padderElement = K.Element('div', {'class': 'window_padder'}, 
					this.contentElement = K.Element('div', {'class': 'window_content'})				
				)
			)
		)
						
		if (this.centerized) {
			this.centerizerElement.className = 'window_centerizer';
		}
		
		if (this.closable) {
			this.closeElement = K.Element('div', {'class': 'window_close'});
			this.boxElement.appendChild(this.closeElement);
			Event.observe(this.closeElement, 'click', this.close.bindAsEventListener(this));	
		}
		
		if (this.tip) {
			this.tipElement = K.Element('div', {'class': 'window_tip'});
			this.boxElement.appendChild(this.tipElement);
		}
		
		this.setSize(this.size);
		
	},
	
	enable: function() {
		if (!this.enabled) {
			this.enabled = true;
			if (this.modal) {
				document.body.appendChild(this.blockerElement);
				if (this.modal == 2) {
					K.Modalizer.add(this);
				}
			}
			document.body.appendChild(this.centerizerElement);
		}
		this.update();
		return this;
	},
	
	disable: function() {
		if (this.enabled) {
			this.enabled = false;
			document.body.removeChild(this.centerizerElement);
			if (this.modal) {
				document.body.removeChild(this.blockerElement);
				if (this.modal == 2) {
					K.Modalizer.remove(this);
				}
			}
		}
		this.update();
		return this;
	},
	
	update: function() {
		if (this.enabled) {
			this.repositionize();
			if (this.focusElement) {
				this.focus.bind(this).defer();
			}
		}
	},
	
	repositionize: function() {
		
		this.boxElement.style.visibility = 'hidden';
		
		this.boxElement.style.left = 0;
		this.boxElement.style.top = 0;
		
		var 
			width = this.boxElement.offsetWidth,
			height = this.boxElement.offsetHeight;
		
		if (this.centerized) {
			this.boxElement.style.left = Math.round(width / -2) + 'px';
			this.boxElement.style.top = Math.round(height / -2) + 'px';
		}
		else {
			var limits = this.getLimits();
			this.boxElement.style.left = (this.position[0] + width + 10 > limits[1][0] ? Math.max(limits[0][0], limits[1][0] - width - 10) : this.position[0]) + 'px';
			this.boxElement.style.top = (this.position[1] + height + 10 > limits[1][1] ? Math.max(limits[0][1], limits[1][1] - height - 10) : this.position[1]) + 'px';
		}
		
		this.boxElement.style.visibility = '';
		
	},
	
	getLimits: function() {
		return [
			[0, 0],
			[document.body.clientWidth, document.body.clientHeight]
		];
	},
	
	open: function() {
		this.enable();
		if (this.onOpen) {
			this.onOpen();
		}
		return this;
	},
	
	close: function() {
		this.disable();
		if (this.onClose) {
			this.onClose();
		}
		return this;
	},
	
	positionByPosition: function(position, x, y) {
		if (!this.centerized) {
			this.position = [
				position[0] + x || 0,
				position[1] + y || 0
			];
		}
		this.update();
		return this;
	},
	
	positionByEvent: function(event, x, y) {
		this.positionByPosition([Event.pointerX(event), Event.pointerY(event)], x || 0, y || 0);
		return this;
	},
	
	positionByElement: function(element, x, y) {
		this.positionByPosition(Element.cumulativeOffset(element), x || 0, y || 0);
		return this;
	}, 
	
	setSize: function(size) {
		this.size = size;
		this.contentElement.style.width = this.size[0] > 0 ? this.size[0] + 'px' : '';
		this.contentElement.style.height = this.size[1] > 0 ? this.size[1] + 'px' : '';
		this.update();
		return this;
	}, 
	
	setContent: function(content) {
		while (this.contentElement.firstChild) {
			this.contentElement.removeChild(this.contentElement.firstChild);	
		}
		this.appendContent(content);
		return this;
	},
	
	appendContent: function(content) {
		if (content instanceof Array) {
			for (var i = 0; i < content.length; i++) {
				this.contentElement.appendChild(content[i]);	
			}
		}
		else {
			this.contentElement.appendChild(content);
		}
		this.update();
		return this;
	},
	
	setFocusElement: function(element, selectText) {
		this.focusElement = element;
		this.selectText = selectText;
	},
	
	focus: function() {
		if (this.focusElement) {
			this.focusElement.focus();
			if (this.focusElement.value && this.focusElement.value.length > 0) {
				var length = this.focusElement.value.length;
				if (this.focusElement.setSelectionRange) {
					this.focusElement.setSelectionRange(this.selectText ? 0 : length, length);
				}
				else if (this.focusElement.createTextRange) { // IE poopoo
					var textRange = this.focusElement.createTextRange(); 
					if (this.selectText) {
						textRange.moveStart('character', 0); 
						textRange.moveEnd('character', length);
					}
					else {
				    	textRange.move('character', length); 
			    	}
				    textRange.select();
				}
			}
		}
	}
	
});





K.ConfirmWindow = Class.create(K.Window, {
	
	centerized: true, 
	
	modal: 2, // Block keys
	
	size: [300, 0],
	
	accept: 'OK', 
	
	initialize: function(title, object) {
		
		this.title = title;
		
		if (object) {
			Object.extend(this, object);	
		}
		
		if (this.decline) {
			this.closable = false;	
		}
		
		this.create();
		
		var titleElement = document.createElement('h2');
		titleElement.appendChild(document.createTextNode(this.title));
		this.contentElement.appendChild(titleElement);
		
		if (this.description) {
			var descriptionElement = document.createElement('p');
			descriptionElement.appendChild(document.createTextNode(this.description));
			this.contentElement.appendChild(descriptionElement);
		}
		
		var buttonsElement = document.createElement('div');
		buttonsElement.className = 'buttons';
		this.contentElement.appendChild(buttonsElement);
		
		var acceptElement = K.Buttonizer.create({text: this.accept, className: 'button_standard'});
		buttonsElement.appendChild(acceptElement);
		Event.observe(acceptElement, 'click', this.onAcceptElementClick.bindAsEventListener(this));

		if (this.decline) {		
			var declineElement = K.Buttonizer.create({text: this.decline, className: 'button_standard'});
			buttonsElement.appendChild(declineElement);
			Event.observe(declineElement, 'click', this.onDeclineElementClick.bindAsEventListener(this));
		}
		
		this.focusElement = acceptElement;
		
	}, 
	
	onAcceptElementClick: function(event) {
		Event.stop(event);
		if (this.onAccept) {
			this.onAccept();
		}
		this.close();
	},
	
	onDeclineElementClick: function(event) {
		Event.stop(event);
		if (this.onDecline) {
			this.onDecline();
		}
		this.close();
	}
	
});





K.XBrowserSupport = {

	initialize: function() {
		
		var tests = {
			ie: /MSIE/,
			ie7: /MSIE 7/,
			ie8: /MSIE 8/,
			ff: /Firefox/,
			ff3: /Firefox\/3\.0/,
			ff35: /Firefox\/3\.5/,
			o: /Opera/,
			chr: /Chrome/,
			chr3: /Chrome\/3/
		};
		
		for (var browser in tests) {
			if (tests[browser].test(navigator.userAgent)) {
				Element.addClassName(document.body, browser);
			}
		}
		
	}
	
};





K.ClickTracker = {
	
	tracking: [
		'/'
	],

	initialize: function(object) {
		
		Object.extend(this, object);
		
		var regEx = /^K\.ClickTracker\:/;
		
		if (regEx.test(top.name)) {
			
			var json = top.name.replace(regEx, '');
				
			new Ajax.Request(
				'/click-tracker/track',
				{
					method: 'post',
					parameters: {json: json}
				}
			);
			
		}
		
		top.name = '';
		
		if (this.tracking.indexOf(location.pathname) >= 0) {
			
			this.data = {path: location.pathname, coordinates: []};
			
			this.offset = Element.cumulativeOffset(this.element);
			
			Event.observe(document.body, 'click', this.onClick.bindAsEventListener(this));
			Event.observe(window, 'unload', this.onUnload.bindAsEventListener(this));
			
		}
		
	},
	
	onClick: function(event) {
		
		var pointer = Event.pointer(event);
		
		this.data.coordinates.push([pointer.x - this.offset[0], pointer.y - this.offset[1]]);
		
	},
	
	onUnload: function(event) {
		
		if (this.data.coordinates.length > 0) {
			top.name = 'K.ClickTracker:' + $H(this.data).toJSON();
		}
		
	},
	
	render: function() {
		
		new Ajax.Request(
			'/click-tracker/fetch',
			{
				method: 'get',
				parameters: {path: location.pathname},
				onComplete: this.onRenderResponse.bind(this)
			}
		);
		
	},
	
	onRenderResponse: function(response) {
		
		response = response.responseJSON;
		
		if (response.clicks) {
		
			var
				width = document.body.clientWidth,
				height = document.body.clientHeight,
				canvasElement = K.Element('canvas', {width: width, height: height});
			
			Element.setStyle(canvasElement, {width: width + 'px', height: height + 'px', position: 'absolute', left: '0', top: '0', zIndex: 1000000000});
			
			document.body.appendChild(canvasElement);
			
			var canvas = canvasElement.getContext('2d');
			
			canvas.fillStyle = 'rgba(0, 0, 0, 0.5)';
			canvas.fillRect(0, 0, width, height);
			
			
			for (var i = 0; i < response.clicks.length; i++) {
				
				var 
					click = response.clicks[i],
					x = parseInt(click.x) + this.offset[0],
					y = parseInt(click.y) + this.offset[1];
				
				var gradient = canvas.createRadialGradient(x, y, 0, x, y, 10);
				gradient.addColorStop(0, 'rgba(255, 255, 255, 0.5)');
				gradient.addColorStop(1, 'rgba(255, 255, 255, 0)');
	
				canvas.fillStyle = gradient;
				
				canvas.fillRect(x - 10, y - 10, 20, 20);
				
			}
			
		}
		
	}
	
};

function map() {
	K.ClickTracker.render();	
}





String.prototype.wrap = function(limit, separator) {
	
	var index = limit, string = this;
	
	while (index < string.length) {
		string = string.substr(0, index) + separator + string.substr(index);	
		index += separator.length + limit;
	}
	
	return string;
	
};
