/**
 * TOC:
 * 		JS.Event
 * 			JS.Event.addListener(element, event_name, function)
 * 			JS.Event.stopEvent(event)
 * 			JS.Event.stopPropagation(event)
 * 			JS.Event.preventDefault(event)
 * 
 * 		JS.Event.CustomEvent
 *	 		JS.Event.CustomEvent.addListener (event_name, function[, data[, function_execution_scope]])
 *	 		JS.Event.CustomEvent.removeListener (event_name[, function])
 *	 		JS.Event.CustomEvent.fire (event_name)
 * 
 * 		JS.Dom
 * 			JS.Dom.get(id)
 * 			JS.Dom.getElementsByClassName(className, nodeTagName, scope)
 * 			JS.Dom.getAncestorByTagName(nodeTagName, node)
 * 			JS.Dom.getPreviousSibling(node)
 * 			JS.Dom.getNextSibling(node)
 * 			JS.Dom.hasClassName(element, className)
 * 			JS.Dom.addClassName(element, className)
 * 			JS.Dom.removeClassName(element, className)
 * 			JS.Dom.getPosition(node)
 * 			JS.Dom.query(css_query[, context])
 * 			JS.Dom.reflow(node[, property])
 * 
 * 		JS.extend(target, source1, ...)
 * 
 * 		JS.Object
 * 			JS.Object.isArray(obj)
 * 			JS.Object.isObject(obj)
 * 			JS.Object.isFunction(obj)
 * 			JS.Object.each(obj, function)
 * 			JS.Object.indexOf(obj, value)
 * 
 * 		JS.Ajax
 * 			JS.Ajax.post(url, postData, callback)
 * 			JS.Ajax.get(url, getData, callback)
 * 			JS.Ajax.form(form_node, callback)
 * 		
 * 		JS.Hash
 * 			JS.Hash.getValue(key)
 * 			JS.Hash.getData()
 * 
 * 		JS.Ajax
 * 			JS.Ajax.request (url, type, data, callback, config)
 * 			JS.Ajax.post (url, postData, callback, config)
 * 			JS.Ajax.get (url, getData, callback, config)
 * 			JS.Ajax.form (form_node, callback)
 */

var JS = {};
window.JS = JS;

/**
 * Event functions
 */
JS.Event = { 
	_readyListeners: [],
	
	/* Add content ready listener */
	addReadyListener: function (fn) {
		if (JS.Event._bindReady.isReady) {
			fn();
		} else {
			JS.Event._readyListeners.push(fn);
			JS.Event._bindReady();
		}
	},
	
	/* Cross browser add listener */
	addListener: function() {
		if ( window.addEventListener ) {
	        return function(el, type, fn) {
				el = JS.Dom.get(el);
	            el.addEventListener(type, fn, false);
	        };
	    } else if ( window.attachEvent ) {
	        return function(el, type, fn) {
	            el = JS.Dom.get(el);
				var f = function() {
	                fn.call(el, window.event);
	            };
	            el.attachEvent('on'+type, f);
	        };
	    } else {
	        return function(el, type, fn) {
	            el = JS.Dom.get(el);
				el['on'+type] = fn;
	        }
	    }
	}(),
	
	/* stopPropagation + preventDefault */
    stopEvent: function(ev) {
        var ev = (ev ? ev : window.event);
		JS.Event.stopPropagation(ev);
        JS.Event.preventDefault(ev);
    },

    /* Stops event propagation */
    stopPropagation: function(ev) {
        var ev = (ev ? ev : window.event);
		if (ev) {
			if (ev.stopPropagation) {
	            ev.stopPropagation();
	        } else {
	            ev.cancelBubble = true;
	        }
		}
    },

    /* Stop event default behavior */
    preventDefault: function(ev) {
        var ev = (ev ? ev : window.event);
		if (ev) {
			if (ev.preventDefault) {
	            ev.preventDefault();
	        } else {
	            ev.returnValue = false;
	        }
		}
    },
	
	/* Trigger 'ready' event callbacks */
	_triggerReady: function () {
		JS.Event._bindReady.isReady = true;
		
		for(var i=0,j=JS.Event._readyListeners.length; i<j; i++) {
			JS.Event._readyListeners[i]();
		}
		
		JS.Event._readyListeners = [];
	},
	
	/* Bind document 'ready' event */
	_bindReady: function () {
		if ( JS.Event._bindReady.readyBinded ) return;
		JS.Event._bindReady.readyBinded = true;
		JS.Event._bindReady.isReady = false;
	
		// Try native event listener
		if ( document.addEventListener ) {
			JS.Event.addListener(document, 'DOMContentLoaded', JS.Event._triggerReady);
		} else if ( document.attachEvent ) {
			document.attachEvent("onreadystatechange", function(){
				if ( document.readyState === "complete" ) {
					document.detachEvent( "onreadystatechange", arguments.callee );
					JS.Event._triggerReady();
				}
			});
	
			if ( document.documentElement.doScroll && window == window.top ) (function(){
				if ( JS.Event._bindReady.isReady ) return;
	
				try {
					// If IE is used, use the trick by Diego Perini
					// http://javascript.nwbox.com/IEContentLoaded/
					document.documentElement.doScroll("left");
				} catch( error ) {
					setTimeout( arguments.callee, 0 );
					return;
				}
	
				JS.Event._triggerReady();
			})();
		}
	
		// A fallback to window.onload, that will always work
		JS.Event.addListener(window, 'load', JS.Event._triggerReady);
	}
	
};

/**
 * Custom event object
 * @param {Object} event_name
 */
JS.Event.CustomEvent = function () {
	var self = this;
	
	//Set variables
	this.listeners = {};
	
	//Listen to unload event to remove all functions
	JS.Event.addListener(window, 'unload', function () { self.onUnload(); });
};

//Page has been unloaded
JS.Event.CustomEvent.prototype.onUnload = function () {
	for(var i in this.listeners) {
		for(var k=0,n=this.listeners[i].length; k<n; k++) {
			delete(this.listeners[i][k]);
		}
	}
};

//List of listeners
JS.Event.CustomEvent.prototype.listeners = {};

//Subscribe to the event
JS.Event.CustomEvent.prototype.addListener = function (event_type, func, data, this_target) {
	if (!event_type) return;
	if (typeof func != 'function') return;
	if (typeof data != 'object') data = {};
	if (!this_target) this_target = func;
	
	if (!this.listeners[event_type]) this.listeners[event_type] = [];
	this.listeners[event_type].push({this_target: this_target, data: data, func: func});
};

//Remove event listener
JS.Event.CustomEvent.prototype.removeListener = function (event_type, func) {
	if (typeof func != 'function') {
		if (this.listeners[event_type]) {
			this.listeners[event_type] = [];
		}
	} else {
		if (this.listeners[event_type]) {
			func._JSEventCustomEventTag = true;
			for(var i=0,j=this.listeners[event_type].length; i<j; i++) {
				if (this.listeners[event_type][i]._JSEventCustomEventTag) {
					this.listeners[event_type][i] = null;
				}
			}
			
			func._JSEventCustomEventTag = null;
		}
	}
};

//Fire event and call all listeners
JS.Event.CustomEvent.prototype.fire = function (event_type, target) {
	if (!this.listeners[event_type]) return true;
	
	var event_type = event_type;
	var e = {
		data: {},
		target: (target ? target : null),
		type: event_type,
		stopPropagation: false,
		preventDefault: false
	};
	
	var callback_arguments = [{}];
	for(var i=1,j=arguments.length; i<j; i++) {
		callback_arguments.push(arguments[i]);
	}

	for(var i=0,j=this.listeners[event_type].length; i<j; i++) {
		if (this.listeners[event_type][i]) {
			e.data = this.listeners[event_type][i].data;
			callback_arguments[0] = e;
			
			this.listeners[event_type][i].func.apply(this.listeners[event_type][i].this_target, callback_arguments);
			
			if (e.preventDefault) return false;
			if (e.stopPropagation) return true;
		}
	}
	
	return true;
};



/**
 * DOM functions
 */
JS.Dom = {
	/**
	 * Returns HTMLNode or null
	 * id accepts HTMLNode or string, which is element id
	 * 
	 * @param {Object} id
	 * @return {Object}
	 */
	get: function (id) {
		//Return id if it's already a node
		if (id && id.nodeType) return id;
		if (id === window || id === document) return id;
		
		//Return node
		if (typeof id == 'string')
			return document.getElementById(id);
		else
			return null;
	},
	
	/**
	 * Returns viewport width, height, left scroll position and top scroll position
	 * 
	 * @return {Object}
	 */
	getViewportPosition: function () {
		var pos = {
			left: 0,		//Left scroll
			top: 0,			//Top scroll
			width: 0,		//Client width
			height: 0		//Client height
		};

		//Top scroll
		if (document.body.scrollTop) {
			pos.top = document.body.scrollTop;
		} else if (window.pageYOffset) {
			pos.top = window.pageYOffset;
		} else if (document.body.parentElement) {
			pos.top = document.body.parentElement.scrollTop;
		}
		
		//Left scroll
		if (document.body.scrollLeft) {
			pos.left = document.body.scrollLeft;
		} else if (window.pageXOffset) {
			pos.left = window.pageXOffset;
		} else if (document.body.parentElement) {
			pos.left = document.body.parentElement.scrollLeft;
		}
		
		//Get document visible part width
		if (document.documentElement && document.documentElement.clientWidth) {
			pos.width = document.documentElement.clientWidth;
		} else if (document.body) {
			pos.width = document.body.clientWidth;
		}
		
		//Get document visible part height
		if (document.documentElement && document.documentElement.clientHeight) {
			pos.height = document.documentElement.clientHeight;
		} else if (document.body) {
			pos.height = document.body.clientHeight;
		}

		return pos;
	},
	
	/**
	 * Returns left and top position of the node relative to the document
	 * 
	 * @param {Object} node
	 * @return {Object}
	 */
	getPosition: function (node) {
		var node = JS.Dom.get(node);
		var pos = {left: 0, top: 0};
		
		while(node && node.offsetParent && node.tagName != 'BODY') {
			pos.left += node.offsetLeft;
			pos.top += node.offsetTop;
			
			node = node.offsetParent;
		}
		
		return pos;
	},
	
	/**
	 * Returns previous sibling which is html node or null if node doesn't
	 * have previous siblings
	 * 
	 * @param {Object} node
	 * @return {Object}
	 */
	getPreviousSibling: function (node) {
		if (!node || !node.previousSibling) return null;
		
		node = node.previousSibling;
		
		while(node) {
			if (node.nodeType == 1) return node;
			node = node.previousSibling;
		}
		
		return null;
	},
	
	/**
	 * Returns ancestor node of the 'node' which has specific tag name
	 * or null if such ancestor is not found
	 *  
	 * @param {Object} nodeTagName
	 * @param {Object} node
	 * @return {Object}
	 */
	getAncestorByTagName: function (nodeTagName, node) {
		node = JS.Dom.get(node);
		
		if (!nodeTagName) {
			if (node && node.parentNode)
				return node.parentNode;
			else
				return null;
		}
		
		nodeTagName = nodeTagName.toLowerCase();
		
		while(node) {
			if (node.tagName && node.tagName.toLowerCase() == nodeTagName) {
				return node;
			}
			
			node = node.parentNode;
		}
		
		return null;
	},
	
	/**
	 * Returns all elements which has specified class name (optionally also filtered by tag name)
	 * which are children of the 'owner' node (if not specified, then document) 
	 * 
	 * @param {String} className
	 * @param {String} nodeTagName
	 * @param {Object} owner
	 * @return {Object}
	 */
	getElementsByClassName: function (className, nodeTagName, owner) {
		var owner = (owner ? JS.Dom.get(owner) : document);
		var r = [];
		
		//Use it if browser supports it
		if (owner.getElementsByClassName) {
			var nodes = owner.getElementsByClassName(className);
			
			if (nodeTagName) {
				for(var i=0,j=nodes.length; i<j; i++) {
					if (nodes[i].tagName == nodeTagName) {
						r.push(nodes[i]);
					}
				}
			} else {
				r = nodes;
			}
			
		} else {
			var nodeTagName = (nodeTagName ? nodeTagName : '*'); 
			var nodes = owner.getElementsByTagName(nodeTagName);
			
			for(var i=0,j=nodes.length; i<j; i++) {
				if (JS.Dom.hasClassName(nodes[i], className))
					r.push(nodes[i]);
			}
		}
		
		return r;
	},
	
	/**
	 * Returns true if given html node has specified class name
	 * 
	 * @param {Object} el
	 * @param {String} className
	 * @return {Boolean}
	 */
	hasClassName: function (el, className) {
		className = className.toString();
		
		if (className.indexOf(' ') != -1) {
			className = className.split(' ');
			for(var i=0,j=className.length; i<j; i++) {
				if (className[i] && !JS.Dom.hasClassName(el, className[i])) return false;
			}
			return true;
		}
		
		el = JS.Dom.get(el);
		if (el) {
			var curClass = el.className || '';
			var curClassSplit = curClass.split(' ');
			for(var i=0,j=curClassSplit.length; i<j; i++)
				if (curClassSplit[i] == className) return true;
				
			return false;
		}
		
		return false;
	},
	
	/**
	 * Add class name to the specified element
	 * 
	 * @param {Object} el
	 * @param {String} className
	 */
	addClassName: function (el, className) {
		className = className.toString();
		
		if (className.indexOf(' ') != -1) {
			className = (className + '').split(' ');
			for(var i=0,j=className.length; i<j; i++) {
				if (className[i]) JS.Dom.addClassName(el, [className[i]]);
			}
			return;
		}
		
		el = JS.Dom.get(el);
		if (el) {
			var curClass = el.className || '';
			if (!JS.Dom.hasClassName(el, className))
				el.className += (className == '' ? className : ' ' + className);
		}
	},
	
	removeClassName: function (el, className) {
		className = className.toString();
		
		if (className.indexOf(' ') != -1) {
			className = (className + '').split(' ');
			for(var i=0,j=className.length; i<j; i++) {
				if (className[i]) JS.Dom.removeClassName(el, [className[i]]);
			}
			return;
		}
		
		el = JS.Dom.get(el);
		if (el) {
			var curClass = el.className || '';
			var curClassSplit = curClass.split(' ');
			var newClass = [];
			for(var i=0,j=curClassSplit.length; i<j; i++)
				if (curClassSplit[i] != className) newClass.push(curClassSplit[i]);
				
			el.className = newClass.join(' ');
		}
	},
	
	nextSibling: function (el) {
		var ns = el.nextSibling;
		while(ns && ns.nodeType != 1) {
			ns = ns.nextSibling;
		}
		
		return ns;
	},
	
	getNextSibling: function (el) {
		this.getNextSibling = this.nextSibling;
		return this.nextSibling(el);
	},
	
	/**
	 * Find elements by CSS query
	 * Supported syntax is 'div, #node_id, .classname, div.classname'
	 * Supported attribute syntax is 'a[rel="lightbox"], a[rel]'
	 * 
	 * @param {String} query
	 * @param {Object} context
	 * @return {Array}
	 */
	query: function (query, context) {
		if (document.querySelectorAll) {
			if (JS.Object.isArray(context)) context = context.shift();
			if (!context) context = document;
			return JS.Object.toArray(context.querySelectorAll(query));
		}
		if (!context) context = [document];
		if (!JS.Object.isArray(context)) context = [context];
		
		return JS.Dom._unique(JS.Dom._query(query, context));
	},
	
	_filterByAttribute: function (items, attribute) {
		if (!attribute) return items;
		if (!JS.Object.isArray(items) && items.nodeType) {
			var r = JS.Dom._filterByAttribute([items], attribute);
			return (r.length ? r[0] : null);
		}
		
		var r = [];
		
		for(var i=0,j=items.length; i<j; i++) {
			var a = items[i].getAttribute(attribute.key);
			
			if (a) {
				if (attribute.val) {
					if (a == attribute.val || (attribute.key == 'href' && a.indexOf(attribute.val) != -1))
						r.push(items[i]);
				} else {
					r.push(items[i]);
				}
			}
		}
		
		return r;
	},
	
	_query: function (query, context) {
		var selectors = query.replace(/\s{2,}/g, ' ')
							 .replace(/\s*,\s*/g, ',')
							 .replace(/^\s*|\s*$/g, '')
							 .split(',');
		var res = [];
		
		if (selectors.length > 1) {
			for(var i=0,j=selectors.length; i<j; i++) {
				res = res.concat(JS.Dom.query(selectors[i], context));
			}
			return res;
		} else {
			var query = query.split(' ');
			var selector = query.shift();
			var attribute = null;
			
			if (attribute = selector.match(/\[([^\]=]+)=?([a-z0-9\#\s\-_"'\[\]]*)\]/)) {
				selector = selector.replace(attribute[0], '');
				attribute = {key: attribute[1], val: attribute[2].replace(/^"|"$/g, '')};
			}
			
			if (selector.charAt(0) == '#') {
				var el = document.getElementById(selector.substr(1));
				if (el && !context.length || JS.Dom._isAncestor(el, context)) {
					res.push(el);
				}
			} else {
				var s_i = selector.indexOf('.');
				if (s_i != -1) {
					var tag = selector.substr(0, s_i).toUpperCase();
					var classname = selector.substr(s_i + 1);
					
					for(var i=0,j=context.length; i<j; i++) {
						var r = JS.Dom.getElementsByClassName(classname, tag, context[i]);
						res = res.concat(JS.Object.toArray(r));
					}
				} else {
					for(var i=0,j=context.length; i<j; i++) {
						res = res.concat(JS.Object.toArray(context[i].getElementsByTagName(selector)));
					}
				}
			}
			
			if (attribute) {
				res = JS.Dom._filterByAttribute(res, attribute);
			}
			
			if (query.length) {
				if (res.length) {
					return JS.Dom._query(query.join(' '), res);
				} else {
					return [];
				}
			} else {
				return res;
			}
		}
	},
	
	_isAncestor: function (el, context) {
		if (el) {
			for(var i=0,j=context.length; i<j; i++) {
				var p = el;
				while((p = p.parentNode)) {
					if (p === context[i]) {
						return true;
					}
				}
			}
		}
		
		return false;
	},
		
	_unique: function (nodes) {
		var res = [];
		
		for(var i=0,j=nodes.length; i<j; i++) {
			var f = false;
			for(var n=0,k=res.length; n<k; n++) {
				if (res[n] === nodes[i]) {
					f = true; break;
				}
			}
			if (!f) res.push(nodes[i]);
		}
		
		return res;
	},
	
	reflow: function (node, property) {
		var property = (property ? property : 'margin');
		var b = JS.Object.browser();
		
		if (b.ie6 || b.ie7) {
			if (!node._reflowStyle) node._reflowStyle = 'px';
			if (node._reflowStyle == 'px') {
				node._reflowStyle = 'em';
			} else {
				node._reflowStyle = 'px';
			}
			
			eval('node.style.' + property + ' = 0 + node._reflowStyle');
		}
	}
};


/**
 * Copies all properties on the first argument from other arguments
 * If object contains properties which are also objects, then those are also copied (cloned)
 */
JS.extend = function () {
	var obj = (typeof arguments[0] == 'object' ? arguments[0] : {});
	
	for(var i=1,j=arguments.length; i<j; i++) {
		var o = arguments[i];
		
		if (typeof o == 'object') {
			for(var n in o) {
				if (o.hasOwnProperty(n)) {
					if (typeof o[n] == 'object' && o[n] && !o[n].nodeType && typeof obj[n] == 'object' && obj[n] && !obj[n].nodeType) {
						obj[n] = JS.extend(obj[n], o[n]);	//Copy all properties
					} else if (obj[n] === undefined && typeof o[n] == 'object' && o[n] && !o[n].nodeType) {
						obj[n] = JS.extend({}, obj[n]);		//Clone
					} else {
						obj[n] = o[n];						//Set
					}
				}
			}
		}
	}
	
	return obj;
};



/**
 * Array/Object functions
 */
JS.Object = {
	/* Returns true if given object is Array */
	isArray: function (obj) {
		//return toString.call(obj) === "[object Array]";
		return obj && obj.constructor === Array;
	},
	
	/* Returns true if given object is Object */
	isObject: function (obj) {
		//toString.call(obj) return false negative results in FF in some cases
		return (obj.toString() === "[object Object]");
	},
	
	/* Returns true if given object is Function */
	isFunction: function (obj) {
		//return toString.call(obj) === "[object Function]";
		return obj && (obj.constructor === Function);
	},
	
	/* Convert object into an array */
	toArray: function (obj) {
		var arr = new Array();
		
		if (obj.length !== undefined && obj.length !== null) {
			for(var i=0,j=obj.length; i<j; i++) {
				if (obj[i] !== undefined && obj[i] !== null) {
					arr.push(obj[i]);
				}
			}
		} else {
			for(var i in obj) {
				if (obj.hasOwnProperty(i)) {
					arr.push(obj[i]);
				}
			}
		}
		
		return arr;
	},
	
	/**
	 * Itterate through all Array/Object items
	 * 
	 * @param {Object} array
	 * @param {Function} func
	 */
	each: function (array, func) {
		if (JS.Object.isFunction(func)) {
			if (JS.Object.isArray(array)) {
				//If argument is Array
				for(var i=0,j=array.length; i<j; i++) {
					func.apply(array[i], [i, array[i]]);
				}
			} else if (JS.Object.isObject(array)) {
				//If argument is Object
				for(var i in array) {
					if (array.hasOwnProperty(i)) {
						func.apply(array[i], [i, array[i]]);
					}
				}
			}
		}
	},

	/**
	 * Returns index in which value is in object
	 * 
	 * @param {Object} obj
	 * @param {Object} item
	 * @return {Boolean}
	 */
	indexOf: function (obj, value) {
		//Use browser implementation
		if (obj.indexOf) {
			return obj.indexOf(value);
		}
		
		if (JS.Object.isArray(obj)) {
			for(var i=0,j=obj.length; i<j; i++) {
				if (obj[i] === value) return i;
			}
		} else if (JS.Object.isObject(obj)) {
			for(var i in obj) {
				if (obj[i] === value) return i;
			}
		}
		return -1;
	},
	
	/**
	 * Detect browser
	 */
	browser: function () {
		if (typeof JS.Object.browser.list != 'undefined') { return JS.Object.browser.list; }
		
		var browser=navigator.userAgent;
		
		JS.Object.browser.list = {
			ff: (browser.indexOf('Mozilla') == 0 && browser.indexOf('MSIE') == -1 && browser.indexOf('WebKit') == -1 ? true : false),
			safari: (browser.indexOf('Mozilla') == 0 && browser.indexOf('AppleWebKit') != -1 ? true : false),
			opera: (browser.indexOf('Opera') == 0 ? true : false),
			ie6: (browser.match(/MSIE\s6\.0/) ? true : false),
			ie7: (browser.match(/MSIE\s7\.0/) ? true : false),
			ie8: (browser.match(/MSIE\s8\.0/) ? true : false)
		};
		
		return JS.Object.browser.list;
	}
};


JS.Hash = {
	cache_str: '',
	cache_data: {},
	
	parseHashStr: function (str) {
		var hash_parts = str.split('&'),
			hash_data = {};
			
		for(var i=0,j=hash_parts.length; i<j; i++) {
			var v = hash_parts[i].split('=');
				v[0] = decodeURIComponent(v[0]);
				v[1] = (v[1] ? decodeURIComponent(v[1]) : '');
				
			hash_data[v[0]] = v[1];
		}
		
		return hash_data;
	},
	
	getData: function () {
		var hash_string = document.location.hash.replace(/^#/, '');
		
		if (JS.Hash.cache_str == hash_string) {
			return JS.Hash.cache_data;
		} else {
			var hash_parts = hash_string.split('&'),
				hash_data = JS.Hash.parseHashStr(hash_string);
			
			JS.Hash.cache_str = hash_string;
			JS.Hash.cache_data = hash_data;
			
			return hash_data;
		}
	},
	
	getValue: function (key) {
		var hash_data = JS.Hash.getData();
		
		if (typeof hash_data[key] !== 'undefined') {
			return hash_data[key];
		} else {
			return null;
		}
	},
	
	addValue: function (key, value) {
		var d = JS.Hash.getData(),
			str = [];
			
		d[key] = value;
		
		for(var i in d) {
			var s = encodeURIComponent(i);
			if (d[i] || d[i] === 0) s += '=' + encodeURIComponent(d[i]);
			str.push(s);
		}
		
		JS.Hash.set(str.join('&'));
	},
	
	removeValue: function (key) {
		var d = JS.Hash.getData(),
			str = [];
		
		if (typeof d[key] == 'undefined') return;
		delete(d[key]);
		
		for(var i in d) {
			var s = encodeURIComponent(i);
			if (d[i] || d[i] === 0) s += '=' + encodeURIComponent(d[i]);
			str.push(s);
		}
		
		JS.Hash.set(str.join('&'));
	},
	
	set: function (str) {
		document.location.hash = str;
	}
};



/**
 * Ajax functions
 */
JS.Ajax = {
	//Ready state change event
	_readyStateChange: function (xhr, callback, mode) {
		//alert(xhr.readyState);
		if (xhr.readyState == 4) {
			if (xhr.status == 200) {
				if (typeof callback == 'function') {
					
					var xhr_data = null;
					var success = true;
					
					if (mode == 'json') {
						try {
							eval('xhr_data=' + xhr.responseText + ';');
						} catch (e) {
							xhr_data = null;
							success = false;
						}
					} else {
						xhr_data = xhr.responseText;
					}
					
					callback(success, xhr_data);
				}
			} else {
				if (typeof callback == 'function') {
					callback(false, {});
				}
			}
		}
	},
	//Execute Ajax request
	request: function (url, type, data, callback, config) {
		var xhr = window.ActiveXObject ? new ActiveXObject("Microsoft.XMLHTTP") : new XMLHttpRequest();
		var callback = callback;
		var mode = (config && config.mode ? config.mode : 'json');
		
		type = type.toUpperCase();
		type = (type != 'GET' && type != 'POST' ? 'GET' : type);
		
		var dataString = [];
		if (data) {
			for(var i in data) {
				dataString.push(encodeURIComponent(i) + '=' + encodeURIComponent(data[i]));
			}
			dataString = dataString.join('&');
		}
			
		if (type == 'GET') {
			url = url + (url.match(/\?/) ? '&' : '?') + dataString;
			dataString = null;
		}
		
		xhr.onreadystatechange = function () { JS.Ajax._readyStateChange(xhr, callback, mode); };
		xhr.open(type, url, true);
		
		try {
			if ( type == 'POST' && dataString )
				xhr.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");

			xhr.setRequestHeader("X-Requested-With", "XMLHttpRequest");
			xhr.setRequestHeader("Accept", "json, */*");
		} catch(e){}
		
		try {
			xhr.send(dataString);
		} catch(e) {}
		
		return xhr;
	},
	//Post data to the server
	post: function (url, postData, callback, config) {
		JS.Ajax.request(url, 'POST', postData, callback, config);
	},
	//Get data form the server
	get: function (url, getData, callback, config) {
		JS.Ajax.request(url, 'GET', getData, callback, config);
	},
	
	/**
	 * Collect data from the form and send as Ajax request using
	 * form method and action
	 * 
	 * @param {Object} form_node
	 * @return {XMLHttpRequest}
	 */
	form: function (form_node, callback) {
		var form_node = JS.Dom.get(form_node); 
		var tag_name = (form_node ? form_node.tagName : '');
		
		if (form_node.tagName == 'FORM')
		{
			var formData = {};
			var vals = {};
			
			var eInp = form_node.getElementsByTagName('input');
			var eSelect = form_node.getElementsByTagName('select');
			var eTextarea = form_node.getElementsByTagName('textarea');
			
			for(var i=0,j=eInp.length; i<j; i++) {
				var n = eInp[i].name;
				if (!n) continue;
				
				switch(eInp[i].type) {
					case 'radio':
						if (!vals[n]) vals[n] = '';
						if (eInp[i].selected) vals[n] = eInp[i].value;
						break;
					case 'checkbox':
						if (!vals[n]) vals[n] = 0;
						if (eInp[i].checked) vals[n] = 1;
						break;
					default:
						vals[n] = eInp[i].value;
				}
			}
			
			for(var i=0,j=eSelect.length; i<j; i++) {
				if (eSelect[i].name) {
					if (eSelect[i].options[eSelect[i].selectedIndex])
						vals[eSelect[i].name] = eSelect[i].options[eSelect[i].selectedIndex].value;
				}
			}
			
			for(var i=0,j=eTextarea.length; i<j; i++) {
				if (eTextarea[i].name) {
					vals[eTextarea[i].name] = eTextarea[i].value;
				}
			}
			
			var arrList = {};
			
			for(var i in vals) {
				if (vals.hasOwnProperty(i)) {
					if (i.substr(i.length - 2, 2) == '[]') {
						if (!arrList[i])
							arrList[i] = 0;
						else
							arrList[i]++;
							
						formData[i.substr(0, i.length - 1) + arrList[i] + ']'] = vals[i];
					} else {
						formData[i] = vals[i];
					}
				}
			}
			
			var method = (form_node.getAttribute('method') ? form_node.getAttribute('method') : 'POST');
			var action = (form_node.getAttribute('action') ? form_node.getAttribute('action') : document.location.href.replace(/\#.+/, ''));
			
			JS.Ajax.request(action, method, formData, callback);
		}
	}
};

/**
 * Onelv specific functions
 */
JS.OneLv = {
	showPresentation: function (url) {
		var left = Math.round((screen.width - 1000) / 2);
		var top = Math.round((screen.height - 700) / 2);
		
		var w = window.open(url,"Presentation", "left=" + left + ",top=" + top + ",scrollbars=no,menubar=no,width=1000,height=700,toolbar=no,resizable=No,location=no,titlebar=no");
		w.focus();
		
		
	},
	htmlEntities: function (html) {
		return html.replace(/</g, '&lt;')
				   .replace(/>/g, '&gt;')
				   .replace(/'/g, '&#039;')
				   .replace(/"/g, '&quot;');
	}
};