/* A few useful utility functions. */

// Capture a method on an object.
function method(obj, name) {
    return function () {
        obj[name].apply(obj, arguments);
    };
}

// The value used to signal the end of a sequence in iterators.
var StopIteration = {
    toString: function () {
        return "StopIteration"
    }
};

// Apply a function to each element in a sequence.
function forEach(iter, f) {
    if (iter.next) {
        try {
            while (true) f(iter.next());
        }
        catch (e) {
            if (e != StopIteration) throw e;
        }
    }
    else {
        for (var i = 0; i < iter.length; i++)
            f(iter[i]);
    }
}

// Map a function over a sequence, producing an array of results.
function map(iter, f) {
    var accum = [];
    forEach(iter, function (val) {
        accum.push(f(val));
    });
    return accum;
}

// Create a predicate function that tests a string againsts a given
// regular expression. No longer used but might be used by 3rd party
// parsers.
function matcher(regexp) {
    return function (value) {
        return regexp.test(value);
    };
}

// Test whether a DOM node has a certain CSS class. Much faster than
// the MochiKit equivalent, for some reason.
function hasClass(element, className) {
    var classes = element.className;
    return classes && new RegExp("(^| )" + className + "($| )").test(classes);
}

// Insert a DOM node after another node.
function insertAfter(newNode, oldNode) {
    var parent = oldNode.parentNode;
    parent.insertBefore(newNode, oldNode.nextSibling);
    return newNode;
}

function removeElement(node) {
    if (node.parentNode)
        node.parentNode.removeChild(node);
}

function clearElement(node) {
    while (node.firstChild)
        node.removeChild(node.firstChild);
}

// Check whether a node is contained in another one.
function isAncestor(node, child) {
    while (child = child.parentNode) {
        if (node == child)
            return true;
    }
    return false;
}

// The non-breaking space character.
var nbsp = "\u00a0";
var matching = {
    "{": "}", "[": "]", "(": ")",
    "}": "{", "]": "[", ")": "("
};

// Standardize a few unportable event properties.
function normalizeEvent(event) {
    if (!event.stopPropagation) {
        event.stopPropagation = function () {
            this.cancelBubble = true;
        };
        event.preventDefault = function () {
            this.returnValue = false;
        };
    }
    if (!event.stop) {
        event.stop = function () {
            this.stopPropagation();
            this.preventDefault();
        };
    }

    if (event.type == "keypress") {
        event.code = (event.charCode == null) ? event.keyCode : event.charCode;
        event.character = String.fromCharCode(event.code);
    }
    return event;
}

// Portably register event handlers.
function addEventHandler(node, type, handler, removeFunc) {
    function wrapHandler(event) {
        handler(normalizeEvent(event || window.event));
    }

    if (typeof node.addEventListener == "function") {
        node.addEventListener(type, wrapHandler, false);
        if (removeFunc) return function () {
            node.removeEventListener(type, wrapHandler, false);
        };
    }
    else {
        node.attachEvent("on" + type, wrapHandler);
        if (removeFunc) return function () {
            node.detachEvent("on" + type, wrapHandler);
        };
    }
}

function nodeText(node) {
    return node.textContent || node.innerText || node.nodeValue || "";
}

function nodeTop(node) {
    var top = 0;
    while (node.offsetParent) {
        top += node.offsetTop;
        node = node.offsetParent;
    }
    return top;
}

function isBR(node) {
    var nn = node.nodeName;
    return nn == "BR" || nn == "br";
}
function isSpan(node) {
    var nn = node.nodeName;
    return nn == "SPAN" || nn == "span";
}
