User:Mike Dillon/Scripts/easydom.js

This is an old revision of this page, as edited by Mike Dillon (talk | contribs) at 17:07, 24 October 2006 (check .arity of function-typed attrValue). The present address (URL) is a permanent link to this revision, which may differ significantly from the current revision.
Note: After saving, you have to bypass your browser's cache to see the changes. Google Chrome, Firefox, Microsoft Edge and Safari: Hold down the ⇧ Shift key and click the Reload toolbar button. For details and instructions about other browsers, see Wikipedia:Bypass your cache.
// <pre><nowiki>

function buildEasyDomNamespace(namespace) {
    var isType = function (o, t) { return typeof o == t };
    var isBool = function (o) { return isType(o, typeof true); };
    var isString = function (o) { return isType(o, typeof ''); };
    var isNumber = function (o) { return isType(o, typeof 0); };
    var isFunction = function (o) { return isType(o, typeof function () {}); };
    var isObject = function (o) { return isType(o, typeof new Object()); };
    var isUndefined = function (o) { return isType(o, (function (x) { return typeof x })()); };
    var isDefined = function (o) { return !isUndefined(o); }; // NOTE: null is "defined"
    var isPrimitive = function (o) {
        return isString(o) || isNumber(o) || isBool(o) || isFunction(o);
    }
    var isNode = function (o) { return isDefined(o) && o != null && isNumber(o.nodeType); }

    // The tag names that will be created in the namespace
    var tags = [
        "bdo",
        "script",
        "object", "param",
        "iframe",
        "link", "meta",
        "p", "pre",
        "a",
        "div", "span",
        "ul", "ol", "li",
        "img",
        "hr",
        "br",
        "em", "strong", "sup", "sub", "tt", "abbr", "acronym",
        "del", "ins", "cite", "blockquote",
        "table", "tbody", "tfoot", "tr", "th", "td", "col", "colgroup", "caption",
        "form", "input", "select", "option", "optgroup", "button",
        "h1", "h2", "h3", "h4", "h5", "h6",
        "label"
    ];

    // Creates the DOM element
    var createDomElement = function(name) {
        var elem = document.createElement(name);
        return elem;
    };

    var attrNameTranslations = {};

    // Conditionally add I.E. name overrides
    /*@cc_on
    attrNameTranslations["for"] = "htmlFor";
    attrNameTranslations["maxlength"] = "maxLength";
    attrNameTranslations["class"] = "className";
    attrNameTranslations["accesskey"] = "accessKey";
    @*/

    var processSingleDomAttribute = function(elem, attrName, attrValue) {
        // Translate DOM attribute name to match implementation
        if (attrNameTranslations[attrName] != null) {
            attrName = attrNameTranslations[attrName];
        }

        // Invoke function callbacks and use their result as the value,
        // unless the attribute name starts with "on" (i.e. an event handler)
        if (isFunction(attrValue)) {
            // Use direct object property assignment for "on" attributes
            // These properties will not be copied by Node.cloneNode
            if (attrName.indexOf("on") == 0) {
                elem[attrName] = attrValue;
                return;
            }

            // Invoke the callback otherwise if it has zero or one argument
            if (attrValue.arity <= 1) {
                attrValue = attrValue(elem);
            }
        }

        // Skip null values
        if (attrValue == null) {
            return;
        }

        // Stringify non-string values
        if (!isString(attrValue)) {
            attrValue = attrValue.toString();
        }

        // Set the attribute
        elem.setAttribute(attrName, attrValue);
    };

    // Detects if the first element is a hash of attributes and if so,
    // uses it to set attributes on the DOM node
    //
    // Returns the number of elements processed to let the caller know
    // how many of the arguments to skip
    var processDomAttributes = function(elem, args) {
        if (args.length == 0) {
            return 0;
        }

        // No attributes to process if null is the first argument
        if (args[0] == null) {
            return 0;
        }

        // No attributes to process if a "primitive" is the first argument
        if (isPrimitive(args[0])) {
            return 0;
        }

        // No attributes to process if a DOM node is the first argument
        if (isNode(args[0])) {
            return 0;
        }

        // Process the first argument as a hash of attributes
        var attrs = args[0];
        for (var attrName in attrs) {
            processSingleDomAttribute(elem, attrName, attrs[attrName]);
        }

        // Return the number of arguments processed
        return 1;
    };

    // Create the function that creates new DOM element builders
    var createDomElementBuilder = function (name) {
        return function() {
            var elem = createDomElement(name);

            // Process attribute hash, if any and skip the argument count returned
            var firstChild = processDomAttributes(elem, arguments);

            // Process the remaining children, if any
            for (var i = firstChild; i < arguments.length; i++) {
                var child = arguments[i];
                if (child == null) {
                    continue;
                }
                // Convert any non-DOM nodes to text nodes with toString()
                if (!isNode(child)) {
                    child = document.createTextNode(child.toString());
                }
                elem.appendChild(child);
            }

            return elem;
        };
    };

    // Create an anonymous namespace if none was provided
    if (isUndefined(namespace)) namespace = {};

    // Populate the namespace
    for (var i in tags) {
        namespace[tags[i]] = createDomElementBuilder(tags[i]);
    }

    // Return the namespace for those relying on anonymous creation
    return namespace;
}

// Build the Easy DOM functions in an anonymous namespace
easyDom = buildEasyDomNamespace();

// Namespace pollution
easydom = easyDOM = easyDom;

// </nowiki></pre>