top.DEBUG = true;

function debug(text) {

    if (top.DEBUG) {
        console.debug(text);
        document.body.innerHTML = text;
    }
}


function get_cookie(name) {

    if (document.cookie.length > 0) {
        var c_start = document.cookie.indexOf(name + "=");
        if (c_start != -1) {
            c_start = c_start + name.length + 1;
            var c_end = document.cookie.indexOf(";", c_start);
            if (c_end == -1) {
                c_end = document.cookie.length;
            }
            return unescape(document.cookie.substring(c_start, c_end));
        }
    }

    return "";
}


function set_cookie(name, value, days) {

    var expires = "";
    if (days) {
        var date = new Date();
        date.setTime(date.getTime()+(days*24*60*60*1000));
        expires = "; expires="+date.toGMTString();
    }

    document.cookie = name + "=" + value + expires + "; path=/";
}


function delete_cookie(name) {

    set_cookie(name, "", -1);
}


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


function create_http_request() {

    var request = null;
    try {  // Mozilla, Safari
        request = new XMLHttpRequest();
    } catch (e) {  // IE
        try {
            request = new ActiveXObject("Msxml2.XMLHTTP");
        } catch (e) {
            try {
                request = new ActiveXObject("Microsoft.XMLHTTP");
            } catch (e) {
            }
        }
    }

    return request;
}

function make_http_request(service, method, params, handler) {
    /*
     * Makes an asynchronous HTTP request.
     *
     * service is a CGI program that implements an API. It accepts a method and its parameters as a
     *   GET request and returns a JSON string.
     * method is the name of a method to be called within the API implemented by the service.
     * params is a dictionary of arguments and their values.
     * handler is a function that is called after the request completes.
     */

    var request = create_http_request();

    // TODO: The response object needs to be defined better.
    function ready_state_change_handler() {
        if (request.readyState == 4) {
            if (request.status == 200) {
                if (handler) {
                    var response = eval('(' + request.responseText + ')');
                    handler(response);
                }
            } else {
                debug(request.responseText);

                if (handler) {
                    var response = {'status': false,
                                    'http_status_code': request.status,
                                    'output': request.responseText};
                    handler(response);
                }
            }
        }
    }

    var query = ['method=' + method,
                 'params=' + escape(JSON.encode(params))];
    var url = service + '?' + query.join('&');
    request.open('GET', url, true);
    request.onreadystatechange = ready_state_change_handler;
    request.send(null);
}


function ajax(api, method, params) {
    /*
     * Makes an asynchronous HTTP request.
     *
     * api: Name of the API.
     * method: Name of a method to be called within the API.
     * params: A map of arguments and their values.
     */
    var request = create_http_request();
    var result = {"status": false, "output": null};

    var query = ['method=' + method, 'params=' + escape(JSON.encode(params))];
    var url = "/api/" + api + '?' + query.join('&');
    request.open('GET', url, false);
    request.send(null);

    if (request.status == 200) {
        var response = eval('(' + request.responseText + ')');
        result.status = response.status;
        result.output = response.output;
    } else {
        result.status = false;
        result.output = request.responseText;
    }

    return result;
}


function utf8_encode(string) {

    string = string.replace(/\r\n/g, "\n");
    var encoded_string = '';

    for (var i=0; i < string.length; i++) {
    var c = string.charCodeAt(i);
    if (c <= 127) {
        encoded_string += String.fromCharCode(c);
    } else if((c > 127) && (c < 2048)) {
        encoded_string += String.fromCharCode((c >> 6) | 192);
        encoded_string += String.fromCharCode((c & 63) | 128);
    } else {
        encoded_string += String.fromCharCode((c >> 12) | 224);
        encoded_string += String.fromCharCode(((c >> 6) & 63) | 128);
        encoded_string += String.fromCharCode((c & 63) | 128);
    }
    }

    return encoded_string;
}


function utf8_decode(encoded_string) {
    var decoded_string = '';
    var i = 0;
    var c = c1 = c2 = 0;

    while (i < encoded_string.length ) {
    c = encoded_string.charCodeAt(i);
    if (c < 128) {
        decoded_string += String.fromCharCode(c);
        i++;
    } else if((c > 191) && (c < 224)) {
        c2 = encoded_string.charCodeAt(i+1);
        decoded_string += String.fromCharCode(((c & 31) << 6) | (c2 & 63));
        i += 2;
    } else {
        c2 = encoded_string.charCodeAt(i+1);
        c3 = encoded_string.charCodeAt(i+2);
        decoded_string += String.fromCharCode(((c & 15) << 12) | ((c2 & 63) << 6) | (c3 & 63));
        i += 3;
    }
    }

    return decoded_string;
}


// This should do an AJAX post instead.
function post(url, data) {

    var form = document.createElement('form');
    form.style.display = 'none';
    form.action = url;
    form.method = 'post';
    for (var param in data) {
    var input = document.createElement('input');
    input.name = param;
    input.value = data[param];
    form.appendChild(input);
    }
    document.body.appendChild(form);
    form.submit();
}


function redirect_to_home() {
    window.location = '/home.html';
}


// TODO: Clean this up. Should this function really accept objects as well as ids?
function dom_object(element) {

    if (typeof element == 'string') {
        var element_id = element;
        element = document.getElementById(element);
    }

    return element;
}


function dom_children_of(element_id, child_tagname) {
    var children = [];
    var all_children = document.getElementById(element_id).childNodes;
    for (var i=0; i < all_children.length; ++i) {
    if (all_children[i].nodeName.toLowerCase() == child_tagname.toLowerCase()) {
        children[children.length] = all_children[i];
    }
    }

    return children;
}

function create(type, contents, attributes) {
    var e = document.createElement(type);

    if (attributes) {
        for (var attr in attributes) {
            if (attr == 'class') {
                // IE needs 'className' for setAttribute while Mozilla needs 'class'. The following
                // works on both.
                e.className = attributes[attr];
            } else {
                e.setAttribute(attr, attributes[attr]);
            }
        }
    }

    if (contents) {
        if (type == 'input') {
            e.value = contents;
        } else {
            var c = document.createTextNode(contents);
            e.appendChild(c);
        }
    }

    return e;
}


function create_div(classes) {
    var div = document.createElement('div');

    if (classes) {
        div.className = classes;
    }

    return div;
}


function create_img(src, title, classes) {
    var img = document.createElement('img');
    img.src = src;
    if (title) {
        img.alt = title;
        img.title = title;
    }

    if (classes) {
        img.className = classes;
    }

    return img;
}


// TODO: Combine on_load and on_click into a generic event 'attacher'.
function on_load(handler) {
    var old_onload = window.onload;
    if (typeof window.onload != 'function') {
        window.onload = handler;
    } else {
        window.onload = function() {
            if (old_onload) {
                old_onload();
            }
            handler();
        }
    }
}

// TODO: Deprecate this with the `attach` method.
function on_click(element, handler) {

    element = dom_object(element);
    add_event_handler(element, 'click', handler);
}


var click_handlers = {}

function add_event_handler(element, event, handler) {

    element = dom_object(element);

    // This is to support remove click handlers.
    if (event == 'click' && element.id) {
        if (!click_handlers[element.id]) {
            click_handlers[element.id] = [];
        }

        click_handlers[element.id].push(handler);
    }

    if (element.attachEvent) {  // For Internet Explorer.
        if (event == 'blur') {
            event = 'onblur';
        } else if (event == 'change') {
            event = 'onchange';
        } else if (event == 'click') {
            event = 'onclick';
        } else if (event == 'focus') {
            event = 'onfocus';
        } else if (event == 'resize') {
            event = 'onresize';
        }

        element.attachEvent(event, handler);
    } else if (element.addEventListener) {  // For all other browsers.
        element.addEventListener(event, handler, false);
    }
}


function remove_click_handlers(element_id) {
    var element = dom_object(element_id);
    if (click_handlers[element.id]) {
        handlers = click_handlers[element.id];
        for (var i=0; i < handlers.length; ++i) {
            if (element.removeEventListener) {
                element.removeEventListener('click', handlers[i], false);
            } else if (element.detachEvent) {
                element.detachEvent('onclick', handlers[i]);
            }
        }
    }
}


// DEPRECATE
function add_class(element, class_name) {

    // TODO: Move the class name to the end of the classes list if it already exists.
    element = dom_object(element);

    var current_class_name = element.className;
    if (current_class_name) {
        element.className = current_class_name + ' ' + class_name;
    } else {
        element.className = class_name;
    }
}

function add_style(element, style) {

    element = dom_object(element);
    var current_styles = element.className.split(' ');
    var new_styles = [];
    for (var i=0; i < current_styles.length; ++i) {
        var s = current_styles[i];
        if (s != style) {
            new_styles.push(s);
        }
    }

    new_styles.push(style);
    element.className = new_styles.join(' ');
}


function remove_style(element, style) {

    element = dom_object(element);
    var current_styles = element.className.split(' ');
    var new_styles = [];
    for (var i=0; i < current_styles.length; ++i) {
        var s = current_styles[i];
        if (s != style) {
            new_styles.push(s);
        }
    }

    element.className = new_styles.join(' ');
}


function set_class(element, class_name) {

    element = dom_object(element);
    element.className = class_name;
}


function value(element) {

    element = dom_object(element);
    var value = element.value.trim();
    return value;
}


function make_link(element, action) {

    set_class(element, 'link');
    on_click(element, action);
}

// Eric Wendelin <emwendelin@gmail.com>
// http://eriwen.com/javascript/js-stack-trace/

function print_stack_trace() {

    var callstack = [];
    var isCallstackPopulated = false;
    try {
        i.dont.exist+=0; //does not exist - that's the point
    } catch(e) {
        if (e.stack) { //Firefox
            var lines = e.stack.split("\n");
            for (var i = 0, len = lines.length; i < len; i++) {
                if (lines[i].match(/^\s*[A-Za-z0-9\-_\$]+\(/)) {
                    callstack.push(lines[i]);
                }
            }
            //Remove call to printStackTrace()
            callstack.shift();
            isCallstackPopulated = true;
        }
        else if (window.opera && e.message) { //Opera
            var lines = e.message.split("\n");
            for (var i = 0, len = lines.length; i < len; i++) {
                if (lines[i].match(/^\s*[A-Za-z0-9\-_\$]+\(/)) {
                    var entry = lines[i];
                    //Append next line also since it has the file info
                    if (lines[i+1]) {
                        entry += " at " + lines[i+1];
                        i++;
                    }
                    callstack.push(entry);
                }
            }
            //Remove call to printStackTrace()
            callstack.shift();
            isCallstackPopulated = true;
        }
    }
    if (!isCallstackPopulated) { //IE and Safari
        var currentFunction = arguments.callee.caller;
        while (currentFunction) {
            var fn = currentFunction.toString();
            //If we can't get the function name set to "anonymous"
            var fname = fn.substring(fn.indexOf("function") + 8, fn.indexOf("(")) || "anonymous";
            callstack.push(fname);
            currentFunction = currentFunction.caller;
        }
    }
    output(callstack);
}



function output(arr){

    alert(arr.join('\n\n'));
}


function show(element) {

    element = dom_object(element);
    element.style.display = 'block';
}


function hide(element) {

    element = dom_object(element);
    element.style.display = 'none';
}


function get_closure(func) {

    var args = Array.prototype.slice.apply(arguments);

    return function () {
        // `this` is the DOM element over which closure was called.
        func.apply(this, args.slice(1));
    };
}


function find_position(object) {

    var x = y = 0;

    do {
        if (object.offsetParent) {
            x += object.offsetLeft;
            y += object.offsetTop;
        }
    } while (object = object.offsetParent);

    return {'x': x, 'y': y};
}


function set_position(element, x, y) {

    var element = dom_object(element);

    element.style.left = x;
    element.style.top = y;
}


function find_dimensions(element) {

    var width = element.scrollWidth;
    var height = element.scrollHeight;

    return {'width': width, 'height': height};
}


function set_dimensions(element, width, height) {

    var element = dom_object(element);
    element.style.width = width;

    if (height != undefined) {
        element.style.height = height;
    }
}

function get_value(element) {

    var element = dom_object(element);
    return element.value;
}

function set_value(element, value) {

    var element = dom_object(element);
    element.value = value;
}


function set_title(title) {
    top.document.title = title;
}


function cool_input(input, background) {

    var input = dom_object(input);
    var value = null;

    add_style(input, 'empty');
    set_value(input, background);

    var on_focus = function (e) {

        if (!value) {
            input.value = '';
        }

        remove_style(input, 'empty');
        add_style(input, 'non-empty');
    };

    var on_blur = function () {
                      
        if (input.value) {
            
            value = input.value;
            remove_style(input, 'empty');
            add_style(input, 'non-empty');

        } else {

            remove_style(input, 'non-empty');
            add_style(input, 'empty');
            input.value = background;
        }
    };

    add_event_handler(input, 'focus', on_focus);
    add_event_handler(input, 'blur', on_blur);

    this.get_value = function () {
        return value;
    }

    this.set_value = function (new_value) {
        value = new_value;
        if (!value) {
            remove_style(input, 'non-empty');
            add_style(input, 'empty');
            input.value = background;
        } else {
            remove_style(input, 'empty');
            add_style(input, 'non-empty');
            input.value = value;
        }
    }
}
