/**
 * @fileOverview This is a collection of utility functions and objects for
 *   programming in JavaScript.
 *
 * @version $Id: SCT.js 124 2010-12-06 21:39:17Z tboronczyk $
 */

"use strict";

/**
 * The SCT object serves as a namespace for this collection of utility functions
 *   and objects.
 * @namespace
 */
var SCT = { };

/**
 * Retrieve an element by its id attribute.
 * @param id {String} the id attribute value
 * @return {Element} the matching element
 */
SCT.$ = function (id) {
    return document.getElementById(id);
};

/**
 * Test whether an element has a given class value assigned.
 * @param element {Element} the element to test
 * @param cls {String} the class attribute value
 * @return {Boolean} whether the elment has the class value
 */
SCT.hasClass = function (element, cls) {
    return (element.className || "").match(new RegExp("(\\s|^)" + cls + "(\\s|$)"));
};

/**
 * Retrieve elements by their class attribute.
 * @param cls {String} the class attribute value
 * @return {Array} the matching elements
 */
SCT.$$ = function (cls) {
    var elements = document.getElementsByTagName("*");
    var classElements = [];

    for (var i in elements) {
        if (SCT.hasClass(elements[i], cls)) {
            classElements.push(elements[i]);
        }
    }

    return classElements;
};

/**
 * Add a class value to an element.
 * @param element {Element} the element to receive the class value
 * @param cls {String} the class attribute value
 */
SCT.addClass = function (element, cls) {
    if (!SCT.hasClass(element, cls)) {
        element.className += " " + cls;
    }
};

/**
 * Remove a class value from an element.
 * @param element {Element} the element from which to remove the class value
 * @param cls {String} the class attribute value
 */
SCT.removeClass = function (element, cls) {
    if (SCT.hasClass(element, cls)) {
        var reg = new RegExp("(\\s|^)" + cls + "(\\s|$)");
        element.className = element.className.replace(reg, " ");
    }
};

/**
 * Trim whitespace from a string value.
 * @param str {String} value to trim
 * @return {String} trimmed value
 */
SCT.trim = function (str) {
    return str.replace(/^\s+|\s+$/g, "");
};

/**
 * Perform a request using an XMLHttpRequest object. The callback is invoked
 *   by the XMLHttpRequest object's onreadystatechange event and receives a
 *   reference to the object.
 * @param element {String} HTTP method (e.g. GET, POST)
 * @param uri {String} uniform resource identifier
 * @param data {Object} POST data represented as a JSON object
 * @param func {Function} callback function
 */
SCT.doHttpRequest = function (method, uri, data, func) {
    // obtain an XmlHttpRequest object
    var vers = [
            "Microsoft.XmlHttp",
            "MSXML2.XmlHttp",
            "MSXML2.XmlHttp.3.0",
            "MSXML2.XmlHttp.4.0",
            "MSXML2.XmlHttp.5.0" ];

    var http = null;
    if (typeof XMLHttpRequest !== "undefined") {
        http = new XMLHttpRequest();
    }
    else if (window.ActiveXObject) {
        for (var i in vers) {
            try {
                http = new ActiveXObject(vers[i]);
                break;
            }
            catch (e) { }
        }
    }


    switch (method) {
    // perform GET request
    case "GET":
        http.open(method, uri, true);
        http.setRequestHeader("HTTP_X_REQUESTED_WITH", "XMLHttpRequest");
        /**@ignore*/
        // Late-bound methods are more eloquent, but a bug in SeaMonkey prevents
        // "this" from resolving correctly. We must pass the XmlHttp object into
        // the callback function. More details can be found at
        // https://bugzilla.mozilla.org/show_bug.cgi?id=468250
        http.onreadystatechange = function () {
            if (http.readyState == 4) {
                func(http);
            }
        };
        http.send(null);
        break;

    // perform POST request
    case "POST":
        // encode data for POST request
        var dataStr = [];
        for (var key in data) {
            dataStr.push(encodeURI(key) + "=" + encodeURI(data[key]));
        }
        dataStr = dataStr.join("&"); 

        http.open(method, uri, true);
        http.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");
        http.setRequestHeader("Content-Length", dataStr.length);
        http.setRequestHeader("Connection", "close");
        http.setRequestHeader("HTTP_X_REQUESTED_WITH", "XMLHttpRequest");
        http.onreadystatechange = function () {
            if (http.readyState == 4) {
                func(http);
            }
        };
        http.send(dataStr);
        break;

    // perform other request methods here...

    default:
        break;
    }
};

/**
 * Queues functions to be executed sequentially. Each function is processed
 *   in the order in which they were added to the queue. Processing is stopped
 *   if a function returns false.
 * @class Function processing queue
 * @constructor
 */
SCT.FunctionQueue = function () {
    var funcs = [];

    /**
     * Register a function with a FunctionQueue instance.
     * @param func {Function} function to register
     */
    this.register = function (func) {
        funcs.push(func);
    };

    /**
     * Execute queued functions.
     * @return {Boolean} whether all functions in the queue processed
     *   executed successfully
     */
    this.execute = function () {
        for (var index in funcs) {
            if (funcs[index]() === false) {
                return false;
            }
        }
        return true;
    };
};

SCT.CookieJar = { };

SCT.CookieJar.set = function (name, value, params) {
    params = params || { };
    if (value == undefined) {
        // undefined value expires cookie so override expires
        value = "";
        params.expires = ";expires=Thu, 01 Jan 1970 00:00:01 GMT";
    }
    else if (params.expires == undefined) {
            params.expires = "";
    }
    else {
        var date = new Date();
        // 1000 * 60 * 60 * 24 = 864e5 seconds
        date.setTime(date.getTime() + params.expires * 864e5);
        params.expires = ";expires=" + date.toGMTString();
    }

    document.cookie =
        name + "=" + encodeURIComponent(value)  + params.expires +
        (params.path    == undefined ? "" : ";path="   + params.path) +
        (params.domain  == undefined ? "" : ";domain=" + params.domain) +
        (params.secure  == undefined ? "" : ";secure=" + params.secure);
}

SCT.CookieJar.get = function (name) {
    var i, tmp,
        cookies = document.cookie.split(";");
    for (i in cookies) {
        tmp = cookies[i].split("=");
        if (SCT.trim(tmp[0]) == name) {
            return tmp.length > 1 ? decodeURIComponent(tmp[1]) : "";
        }
    }
    return undefined;
}

