/*
 * Sunlight Labs JavaScript library
 * Version: 1.3
 * Author: Jeremy Carbaugh
 *
 * Version 1.3
 * - added abort to Sunlight.Ajax.Synchronized
 * - added onFailure callback
 *
 * Version 1.2
 * - moved asQueryString from Object method to Sunlight.HTTP
 * - added Sunlight.Ajax.Synchronized methods
 * - added conditional $ shortcut for document.getElementById()
 * - added callback to Sunlight.Application.loadScript()
 *
 * Version 1.1
 * - removed prototype dependency
 * - added asQueryString method to Object
 * - added loadScript, loadStylesheet, and addToHead functions
 *
 * Version 1.0
 * - new version of everything
 * - developed with Prototype 1.5.0
 */
 
/*global ActiveXObject, XMLHttpRequest, document, window */

var $ = function(id) {
    return document.getElementById(id);
};

var Sunlight = {

	Application: {

		/*
		 * Simon Willison's addLoadEvent function
		 * http://simonwillison.net/2004/May/26/addLoadEvent/
	 	 */

		addLoadEvent: function(func) {
			var oldonload = window.onload;
			if (typeof window.onload !== 'function') {
				window.onload = func;
			} else {
				window.onload = function() {
					if (oldonload) {
						oldonload();
					}
					func();
				};
			}
		},

		addUnloadEvent: function(func) {
			var oldonunload = window.onunload;
			if (typeof window.onunload !== 'function') {
				window.onunload = func;
			} else {
				window.onunload = function() {
					if (oldonunload) {
						oldonunload();
					}
					func();
				};
			}
		},

		loadScript: function(url, callback) {
		    var scriptElem = document.createElement('script');
		    scriptElem.type = 'text/javascript';
		    scriptElem.src = url;
		    if (callback) {
		        scriptElem.onreadystatechange = function () {
                    if (this.readyState === 'complete') {
                        callback(this);
                    }
                };
                scriptElem.onload = function() {
                    callback(this);
                };
		    }
		    Sunlight.DOM.addToHead(scriptElem);
		},

		loadStylesheet: function(url, attributes) {
		    var styleElem = document.createElement('link');
		    styleElem.type = 'text/css';
		    styleElem.rel = 'stylesheet';
		    styleElem.href = url;
		    if (attributes) {
		        for (var prop in attributes) {
		            if (typeof attributes[prop] !== 'function') {
		                styleElem[prop] = attributes[prop];
	                }
		        }
		    } else {
		        styleElem.media = 'screen';
		    }
		    Sunlight.DOM.addToHead(styleElem);
		}

	},

	Ajax: {

		onFailure: function(transport) { },

		Proxy: {

			url: 'http://sunlightlabs.com/proxy/',

			get: function(action, params, onSuccess, onFailure) {
				params.paction = action;
				params.pmethod = 'get';
				Sunlight.Ajax.get(
					Sunlight.Ajax.Proxy.url, params, onSuccess, onFailure);
			},

			post: function(action, params, onSuccess, onFailure) {
				params.paction = action;
				params.pmethod = 'post';
				Sunlight.Ajax.post(
					Sunlight.Ajax.Proxy.url, params, onSuccess, onFailure);
			}

		},

		Synchronized: {

		    channels: [],

		    init: function(channel) {
		        Sunlight.Ajax.Synchronized.channels[channel] = 0;
		    },

		    abort: function(channel) {
		        Sunlight.Ajax.Synchronized.channels[channel]++;
		    },

		    get: function(channel, url, params, onSuccess, onFailure) {
    			Sunlight.Ajax.Synchronized.request(channel, url, params, 'get',
												   onSuccess, onFailure);
    		},

    		post: function(channel, url, params, onSuccess, onFailure) {
    			Sunlight.Ajax.Synchronized.request(channel, url, params, 'post',
												   onSuccess, onFailure);
    		},

    		request: function(channel, url, params, method, onSuccess, onFailure) {
    		    var synId = ++Sunlight.Ajax.Synchronized.channels[channel];
    		    var synSuccess = null;
				var synFailure = null;
    		    if (onSuccess) {
        		    synSuccess = function(transport) {
        		        if (synId === Sunlight.Ajax.Synchronized.channels[channel]) {
        		            onSuccess(transport);
        		        }
        		    };
    		    }
				if (onFailure) {
					synFailure = function(transport) {
						if (synId === Sunlight.Ajax.Synchronized.channels[channel]) {
							onFailure(transport);
						}
					};
				}
    		    Sunlight.Ajax.request(url, params, 'post', synSuccess, synFailure);
    		}

		},

		get: function(url, params, onSuccess, onFailure) {
			Sunlight.Ajax.request(url, params, 'get', onSuccess, onFailure);
		},

		post: function(url, params, onSuccess, onFailure) {
			Sunlight.Ajax.request(url, params, 'post', onSuccess, onFailure);
		},

		/*
		 * init() and request() are based on Jeremy Keith's
		 * XMLHttpRequest functions from his book Bulletproof Ajax.
		 */

		request: function(url, params, method, onSuccess, onFailure) {
		    var transport = Sunlight.Ajax.init();
		    if (transport) {
		        transport.onreadystatechange = function() {
		            if (transport.readyState === 4) {
		                if (transport.status === 200 || transport.status === 304) {
		                    if (onSuccess) {
		                        onSuccess(transport);
	                        }
		                } else {
		                    Sunlight.Ajax.onFailure(transport);
		                    if (onFailure) {
		                        onFailure(transport);
		                    }
		                }
		            }
		        };
    		    var qs = (params) ? Sunlight.HTTP.toQueryString(params) : null;
		        if (method === 'get') {
		            if (qs) {
		                url += '?' + qs;
	                }
		            transport.open('GET', url, true);
		            transport.send(null);
		        } else if (method === 'post') {
		            transport.open('POST', url, true);
		            transport.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
		            transport.send(qs);
		        }
		    }
		},

    	init: function() {
            var xhr = false;
            if (window.XMLHttpRequest) {
                xhr = new XMLHttpRequest();
            } else if (window.ActiveXObject) {
                try {
                    xhr = new ActiveXObject('Msxml2.XMLHTTP');
                } catch (msxml2Error) {
                    try {
                        xhr = new ActiveXObject('Microsoft.XMLHTTP');
                    } catch (msError) {
                        // Awww, you lose. Your client sucks.
                    }
                }
            }
            return xhr;
		}

	},

	DOM: {

    	addToHead: function(elem) {
    	    var headElems = document.getElementsByTagName('head');
    	    if (headElems.length === 0) {
    	        var docElem = document.documentElement;
    	        var headElem = document.createElement('head');
    	        headElem.appendChild(elem);
    	        docElem.appendChild(headElem);
    	    } else {
    	        headElems[0].appendChild(elem);
            }
    	},

		clearNode: function(node) {
			while (node.childNodes.length > 0) {
				node.removeChild(node.childNodes[0]);
			}
		},

		getText: function(node) {
		    var text = '';
		    var childNodes = node.childNodes;
		    var childCount = node.childNodes.length;
		    for (var i = 0; i < childCount; i++) {
                var childNode = childNodes[i];
                if (childNode.nodeType === 3) {
                    text += childNode.nodeValue;
                } else if (childNode.nodeType === 1) {
                    text += Sunlight.DOM.getText(childNode);
                }
		    }
		    return text;
		}

	},

	HTTP: {

        toQueryString: function(obj) {
            var queryString = '';
            var props = [];
            for (var prop in obj) {
                if (typeof obj[prop] !== 'function') {
                    var str = encodeURIComponent(prop) + '=' +
                        encodeURIComponent(obj[prop]);
                    props.push(str);
                }
            }
            if (props.length > 0) {
                queryString = props.join('&');
            }
            return queryString;
        }

	},

	JSON: {

		evaluate: function(jsonStr) {
			try {
				var obj = eval('(' + jsonStr + ')');
				return obj;
			} catch (e) {
				return null;
			}
		}

	}

};