
var userAgent = navigator.userAgent.toLowerCase();

var base = {

	selectors: false,
	sizzleSrc: './sizzle.js',

	reClassNameCache: {},

	browser: {
		version: (userAgent.match(/.+(?:rv|it|ra|ie)[\/: ]([\d.]+)/) || [])[1],
		webkit: /webkit/.test(userAgent),
		opera: /opera/.test(userAgent),
		msie: /msie/.test(userAgent) && !/opera/.test(userAgent),
		mozilla: /mozilla/.test(userAgent) && !/(compatible|webkit)/.test(userAgent),
		msafari: /apple.*mobile.*safari/.test(userAgent)
	},

	extend: function () {
		function ext(destination, source) {
			for (var property in source) {
				destination[property] = source[property];
			}
		}
		if (arguments.length == 2) {
			ext(arguments[0], arguments[1]);
		} else {
			var l = arguments.length, i = l-1, src = arguments[i];
			while (i--) {
				ext(arguments[i], src);
			}
		}
	},

	getCachedRegEx: function (className) {
		return ( base.reClassNameCache[className] || 
			( base.reClassNameCache[className] = new RegExp('(?:^|\\s+)' + className + '(?:\\s+|$)')));
	},

	_fire: function (element, eventName, data) {
		var event = document.createEvent('HTMLEvents');
		event.initEvent(eventName, true, true);
		event.data = data || {};
		element.dispatchEvent(event);
	},
	
	/// http://unscriptable.com/index.php/2009/03/19/hi-performance-javascript-tips-1/
	toArray: (function (slice) {
		return function toArray (object) {
			return slice.call(object, 0);
		}
	})(Array.prototype.slice)
};

base.extend(Object.prototype, {
	toQueryString: function () {
		var params = [];
		for (var key in this) {
			var str = encodeURIComponent(key) + '=';
			var value = (Object.prototype.toString.call(this[key])==='[object Array]') ? this[key].join(',') : this[key];
			str += encodeURIComponent(value);
			params.push(str);
		}
		return params.join('&');
	}
});

base.extend(Array.prototype, NodeList.prototype, {

	first: function () { return this[0]; },

	each: function (iterator, context) {
		if (context) { iterator = iterator.bind(context); }
		try {
			var c = this.length, i = 0;
			while (i < c) {
				if (typeof this[i] !== 'function') { iterator(this[i]); }
				i++;
			}
		} catch(e) { throw e; }
		return this;
	},

	eachAfter: function (iterator, interval, dir, context, callback) {
		iterator = (context) ? iterator.bind(context) : iterator;
		callback = callback || function () {};
		var dir = dir || 1;
		var c = this.length, i = 0;
		if (dir < 0) {
			var t = c;
			c = i;
			i = (t == 0) ? 0 : t - 1;
		}
		try {
			var eachIterator = setInterval(function () {
				if (i === c) {
					if (callback) { callback(); }
					clearInterval(eachIterator);
					return this;
				} else {
					if (typeof this[i] !== 'function') { iterator(this[i]); }
					i = i + dir;
				}
			}.bind(this), (interval || 1000));

		} catch(e) { throw e; }
	}
});

base.extend(Function.prototype, {
	bind: function () {
		var method = this, args = Array.prototype.slice.call(arguments), object = args.shift();
		return function () {
			return method.apply(object, args.concat(Array.prototype.slice.call(arguments)));
		};
	},

	bindAsEventListener: function () {
		var method = this, args = Array.prototype.slice.call(arguments), object = args.shift();
		return function (event) {
			return method.apply(object, [event || window.event].concat(Array.prototype.slice.call(arguments)));
		};
	}
});

var io = function (url, options) {
	this.options = {
		method: 'post',
		asynchronous: true,
		contentType: 'application/x-www-form-urlencoded',
		encoding: 'UTF-8',
		params: {},
		format: 'text',
		sanitizeJSON: false
	};
	base.extend(this.options, options || {});
	this.options.method = this.options.method.toLowerCase();
	this.xhr = new XMLHttpRequest();
	var params = this.options.params.toQueryString();
	if (this.options.method.toLowerCase() == 'get') {
		url += (url.indexOf('?') >= 0) ? '&' : '?' + params;
	}
	try {
		this.xhr.open(this.options.method, url, this.options.asynchronous);
		this.xhr.onreadystatechange = this._onStateChange.bind(this);
		var headers = {
			'X-Requested-With': 'XMLHttpRequest',
			'Accept': '*/*'
		};
		if (this.options.method == 'post') {
			headers['Content-type'] = this.options.contentType + (this.options.encoding ? '; charset=' + this.options.encoding : '');
		}
		for (var name in headers) {
			this.xhr.setRequestHeader(name, headers[name]);
		}
		this._body = this.options.method.toLowerCase() == 'post' ? (this.options.postBody || params) : null;
		this.xhr.send(this._body);

	} catch(e) { console.error('request error', e); }
};

base.extend(io, {
	response: function (response, format, sanitize) {
		switch (format.toLowerCase()) {
		case 'xml':
			var xml = (response.responseXML) ? response.responseXML : new String('');
			return xml;
			break;
		case 'json':
			var json = response.responseText;
			if (sanitize) { json = json.sub(/^\/\*-secure-([\s\S]*)\*\/\s*$/, '#{1}'); }
			try {
				var str = response.responseText.replace(/\\./g, '@').replace(/"[^"\\\n\r]*"/g, '');
				if ((/^[,:{}\[\]0-9.\-+Eaeflnr-u \n\r\t]*$/).test(str)) { return eval('(' + json + ')'); }
			} catch(e) { console.error('json eval error', e); }
			break;
		case 'text':
			return response.responseText;
			break;
		default:
			return response;
			break;
		}
	},

	Events: ['Uninitialized', 'Connected', 'Requested', 'Processing', 'Complete', 'Failure', 'Success']
});

base.extend(io.prototype, {
	_onStateChange: function () {
		var readyState = this.xhr.readyState;
		if (readyState === 4) {
			this._complete = true;
			var res = new io.response(this.xhr, this.options.format, this.options.sanitizeJSON);
			if (this.options.onFailure || this.options.onSuccess) {
				var successCode = (!this.xhr.status) ? 5 : (this.xhr.status >= 200 && this.xhr.status < 300) ? 6 : 5;
				(this.options['on' + io.Events[successCode]] ||
				function () {})(res);
			}(this.options['on' + io.Events[readyState]] ||
			function () {})(res);
		} else if (readyState > 1 && !((readyState == 4) && this._complete)) {
			(this.options['on' + io.Events[readyState]] ||
			function () {})();
		}
	}
});

var Template = function (template) {
	this.template = template;
	return this.template;
};

base.extend(Template.prototype, {
	parse: function (object) {
		this.data = object;
		this.output = this.template;
		this.output = this.output.replace(/#\{(\w+)\}/g, this._replaceCallback.bind(this));
		return this.output;
	},
	_replaceCallback: function (match1, match2) {
		return this.data[match2] || '';
	}
});

var Element = function (type, atts, content) {
	this.el = document.createElement(type);
	for (var attr in atts) { this.el.setAttribute(attr, atts[attr]); }
	this.el.innerHTML = content || '';
	return this.el;
};

base.extend(String.prototype, {
	isBlank: function () {
		return /^\s*$/.test(this);
	},
	toHTML: function () {
		var div = document.createElement('div');
		div.innerHTML = this;
		return div.childNodes;
	},
	trim: function (match) {
		var re = new RegExp(match) || new RegExp(/\s+?/);
		return this.replace(re, '');
	}
});

base.extend(Event.prototype, {
	stop: function () {
		this.preventDefault();
		this.stopPropagation();
	}
});

base.extend(HTMLElement.prototype, {
	data: {},
	setCSS: function (css) {
		for (var prop in css) { this.setStyle(prop, css[prop]); }
		return this;
	},
	getStyle: function (prop) {
		return document.defaultView.getComputedStyle(this,"").getPropertyValue(prop);
	},
	setStyle: function (prop, val) {
		this.style[prop] = val;
		return this;
	},
	hasAttribute: function (attributeName) {
		return this.getAttribute(attributeName) !== undefined;
	},
	hasClass: function (className) {
		var re = base.getCachedRegEx(className);
		return re.test(this.className);
	},
	addClass: function (className) {
		if (!this.hasClass(className)) {
			this.className += ' ' + className.trim();
			///console.log("addClass "+this.id+" [ "+className+" ]\n   "+this.className);
		}
		return this;
	},
	removeClass: function (className) {
		if (this.hasClass(className)) {
			var re = base.getCachedRegEx(className);
			this.className = this.className.replace(re, ' ');
			if (this.hasClass(className)) {
				this.removeClass(className);
			} else {
				this.className = this.className.trim();
			}
		}
		return this;
	},
	toggleClass: function (className) {
		if (this.hasClass(className)) {
			this.removeClass(className);
		} else {
			this.addClass(className);
		}
	},
	getXY: function () {
		var valueX = 0, valueY = 0;
		var element = this;
		do {
			valueY += element.offsetTop || 0;
			valueX += element.offsetLeft || 0;
			element = element.offsetParent;
		} while (element);
		var offset = [valueX, valueY];
		offset.x = valueX,
		offset.y = valueY;
		return offset;
	},
	remove: function (el) {
		el = el || this;
                (el.parentNode).removeChild(el);
	},
	clean: function() {
		var isEmpty = /\S/;
		this.each(function(el) {
			for (var cur=el.firstChild,next ; cur ; cur=next ) {
				next = cur.curSibling;
				if (cur.nodeType == 3 && !isEmpty.test(cur.nodeValue))
					{ el.removeChild(cur); }
			}
		});
		return this;
	},
	fire: function (eventName, data) {
		base._fire(this, eventName, data);
	},
	findParentByTagName: function(localName) {
		localName = localName.toLowerCase();
		node = this;
		while (node && (node.nodeType != 1 || node.localName.toLowerCase() != localName))
		{
			node = node.parentNode;
		}
		console.log("findParentByTagName node: "+node);
		return node;
	}
});

base.extend(document, {
	_loaded: false,
	fire: function (eventName, data) { base._fire(this, eventName, data); }
});
/*
<div class="info">
These apps open in a new window. Don&#8217;t forget to save them to your home screen to enable full-screen mode.
</div>
*/

//-----------------------------------------
(function () {

	var timer;

	function fireContentLoaded() {
		if (!document._loaded && timer) { timer=window.clearInterval(timer); }

		document.fire('dom:loaded');
		document._loaded = true;
	}

	if (typeof console !== 'object') {
		console = {
			log: function () {},
			alert: function () {},
			warn: function () {},
			info: function () {},
			time: function () {},
			timeEnd: function () {},
			error: function () {}
		};
	}

	if (typeof document.querySelectorAll === 'function') {
		window.$ = function (selector, context) {
			context = ( !! context) ? context : document;
			base.selectors = true;
			return context.querySelectorAll(selector);
		};
		document.addEventListener('DOMContentLoaded', fireContentLoaded, false);
	} else {
		console.warn('Selectors API not available. Falling back on Sizzle query selector.');

		var sizzle = new Element('script', {
			type: 'text/javascript',
			src: base.sizzleSrc
		});
		
		sizzle.onload = function () {
			window.$ = Sizzle;
			if (base.browser.webkit && parseInt(base.browser.version) < 525) {
				console.info('DOMContentLoaded not available. Falling back on document.readyState.');
				timer = window.setInterval(function () {
					if (/loaded|complete/.test(document.readyState)) {
						fireContentLoaded();
					}
				}, 0);
			} else {
				document.addEventListener('DOMContentLoaded', fireContentLoaded, false);
			}
		};

		sizzle.onerror = function () {
			console.error('Horrible failure getting Sizzle. That sucks.');
		};

		document.getElementsByTagName('head')[0].appendChild(sizzle);
	}
})();

