
// basic

function $ (id) {
	if (typeof id == 'string') {
		return document.getElementById(id);
	} else {
		return id;
	}
}

function extend (paste, copy) {
	for (var p in copy) {
		paste[p] = copy[p];
	}
	return paste;
}

function retry () {
	for (var a = 0; a < arguments.length; a++) {
		try {
			return arguments[a]();
		} catch (e) {}
	}
	return null;
}

String.prototype.ucfirst = function () {
	var first = this.substr(0, 1);
	var rest = this.substr(1);
	return first.toUpperCase() + rest;
};

String.prototype.trim = function () {
	return this.replace(/^\s+|\s+$/, '');
};

loop = {
	each: function (ary, func, obj) {
		for (var i = 0; i < ary.length; i++) {
			if (obj) {
				func.call(obj, ary[i]);
			} else {
				func(ary[i]);
			}
		}
	}
}


// dom

ClassList = function (element) {
	this.element = $(element);
};
ClassList.prototype = {
	list: function () {
		return this.element.className.split(/\s+/);
	},
	add: function (c) {
		this.del(c);
		this.element.className += ' '+ c;
	},
	del: function (c) {
		this.element.className = this.element.className.replace(c, '');
		this.element.className = this.element.className.replace(/\s+/, ' ');
		this.element.className = this.element.className.trim();
	},
	set: function (c) {
		if (c instanceof Array) {
			c = c.join(' ');
		}
		this.element.className = c;
	},
	match: function (c) {
		var classes = this.list();
		for (var i in classes) {
			if (classes[i] == c) {
				return true;
			}
		}
		return false;
	}
};

Array.fromNodeList = function (nodeList) {
	return nodesToArray(nodeList);
};

function nodesToArray (nodeList) {
	var arr = [];
	for (var n = 0; n < nodeList.length; n++) {
		arr.push(nodeList.item(n));
	}
	return arr;
};

function getChildElements (element, deep) {
	element = $(element);
	if (!element) {
		throw new Error('Must have element as first argument.');
	}
	var arr = [];
	for (var c = 0; c < element.childNodes.length; c++) {
		var child = element.childNodes.item(c);
		if (child.nodeType == 1) {
			arr.push(child);
			if (deep) {
				arr = arr.concat(getChildElements(child, deep));
			}
		}
	}
	return arr;
}

function getElementsByClass (className, element) {
	element = element ? $(element) : window.document;
	var result = [];
	var all = getChildElements(element, true);
	loop.each(all, function (e) {
		var classes = new ClassList(e);
		if (classes.match(className)) {
			result.push(e);
		}
	});
	return result;
}

function getParentByClass (className, element) {
	var parent = element.parentNode;
	if (parent && parent.nodeType == 1) {
		var classes = new ClassList(parent);
		if (classes.match(className)) {
			return parent;
		} else {
			getParentByClass(className, parent);
		}
	} else {
		return null;
	}
}

function getNextElement (element) {
	var sib = $(element);
	while (sib = sib.nextSibling) {
		if (sib.nodeType == 1) {
			return sib;
		}
	}
	return null;
}

function getPreviousElement (element) {
	var sib = $(element);
	while (sib = sib.previousSibling) {
		if (sib.nodeType == 1) {
			return sib;
		}
	}
	return null;
}


// events

Listener = function (element, handle, method, caller) {
	this.element = element;
	this.handle = handle;
	this.method = function (e) {
		e = e || window.event;
		if (e.srcElement && !e.target) {
			e.target = e.srcElement;
		}
		try {
			if (caller) {
				method.call(caller, e);
			} else {
				method(e);
			}
		} catch (r) {
			if (window.onerror) {
				window.onerror(r);
			}
		}
	};
	if (element.addEventListener) {
		element.addEventListener(handle, this.method, false);
	} else if (element.attachEvent) {
		element.attachEvent('on'+ handle, this.method);
	} else {
		throw new Error("Browser doesn't support event listeners.");
	}
};
Listener.prototype.stop = function () {
	if (this.element.removeEventListener) {
		this.element.removeEventListener(this.handle, this.method, false);
	} else if (this.element.detachEvent) {
		this.element.detachEvent('on'+ this.handle, this.method);
	} else {
		return false;
	}
	return true;
};

Interval = function (wait, method, caller) {
	this.handler = window.setInterval(
		function () {
			try {
				if (caller) {
					method.call(caller);
				} else {
					method();
				}
			} catch (r) {
				if (window.onerror) {
					window.onerror(r);
				}
			}
		},
		wait
	);
};
Interval.prototype.stop = function () {
	window.stopInterval(this.handler);
};


// service requests

// facilitates an inline http service request
Service = function (url) {
	this.url = url;
};
Service.prototype = {
	
	// sends a http request one at a time
	request: function (post) {
		if (!this.http) {
			
			// get the http request object
			this.http = retry(
				function () {return new XMLHttpRequest},
				function () {return new ActiveXObject('Msxml2.XMLHTTP')},
				function () {return new ActiveXObject('Microsoft.XMLHTTP')}
			);
			if (!this.http) {
				throw new Error("Service.request could not make a http request.");
			}
			
			// convert an object into a post string
			post = post || this.post;
			if (typeof post != 'string') {
				var query = new Array();
				for (var p in post) {
					if (typeof post[p] != 'function') {
						query.push(p +'='+ encodeURIComponent(post[p]));
					}
				}
				post = query.join('&');
			}
			this.post = post;
			
			// send the request
			if (!this.wait) {
				var me = this;
				this.http.onreadystatechange = function () {
					me.onReadyState(me.http);
				};
			}
			this.http.open(post ? 'post' : 'get', this.url, !this.wait);
			this.http.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
			this.http.send(post);
			
			return true;
		} else {
			return false;
		}
	},
	onReadyState: function (http) {
		if (http.readyState == 4) {
			if (http.status == 200 && this.onSuccess) {
				this.onSuccess(http);
			} else if (this.onFailure) {
				this.onFailure(http);
			}
		}
	},
	onSuccess: function () {},
	onFailure: function () {},
	callback: function (handle, method, caller) {
		handle = handle.ucfirst();
		switch (handle) {
			case 'Success':
			case 'Failure':
				this['on'+ handle] = function (http) {
					if (caller) {
						method.call(caller, http);
					} else {
						method(http);
					}
				};
				return true;
			default:
				return false;
		}
	},
	abort: function () {
		if (this.http) {
			this.http.abort();
		}
	}
};

