<commit_before>/*!
 * DOM.js
 *
 * Copyright (c) 2013 Maksim Chemerisuk
 *
 * 1) encapsulation: completely hides native objects
 * 2) usability: provides more friendly apis
 * 3) safety: api can't be changed after initialization
 * 4) performance: use native methods where it's possible
 * 5) self-documenting: every error message has a link with detailed explaination
 * 6) jsdoc, generate documentation by it
 */
/*jslint browser:true*/
/*global define CustomEvent */
// TODO: replace, remove, specs
(function (window, document, undefined) {
    "use strict";

    var factory = (function() {
        var storage = [];

            return {
                get: function(guid) {
                    return storage[guid];
                },
                remove: function(guid) {
                    storage[guid] = null;
                },
                create: function(native) {
                    if (!native) return new NullElement();

                    var instance = native._DOM;

                    if (!instance) {
                        if (native.length === undefined) {
                            native._DOM = (instance = new DOMElement(native));
                        } else {
                            instance = new DOMElements();

                            Array.prototype.forEach.call(native, function (e) {
                                instance.push(factory.create(e));
                            });
                        }

                        instance.guid = storage.push(native) - 1;
                        // data should be a simple object without toString, hasOwnProperty etc.
                        instance.data = Object.create(null);
                    }

                    return instance;
                }
            };
        })(),
        // helpers
        rquickIs = /^(\w*)(?:#([\w\-]+))?(?:\[([\w\-]+)\])?(?:\.([\w\-]+))?$/,

        quickParse = function(selector) {
            var quick = rquickIs.exec(selector);
            // TODO: support attribute value check
            if (quick) {
                //   0  1    2   3          4
                // [ _, tag, id, attribute, class ]
                quick[1] = (quick[1] || "").toLowerCase();
                quick[4] = quick[4] ? " " + quick[4] + " " : "";
            } else {
                throw new DOMMethodError("quickParse");
            }

            return quick;
        },
        quickIs = function (elem, m) {
            var attrs = elem.attributes || {};

            return (
                (!m[1] || elem.nodeName.toLowerCase() === m[1]) &&
                (!m[2] || (attrs.id || {}).value === m[2]) &&
                (!m[3] || m[3] in attrs) &&
                (!m[4] || ~(" " + (attrs["class"] || "").value  + " ").indexOf(m[4]))
            );
        },
        // classes
        DOMElement = function(native) { },
        DOMElements = (function() {
            // Creates clean copy of Array prototype. Inspired by
            // http://dean.edwards.name/weblog/2006/11/hooray/
            var ref = document.getElementsByTagName('script')[0],
                iframe = document.createElement("iframe"),
                ctr;

            iframe.src = "about:blank";
            iframe.style.display = "none";

            ref.parentNode.insertBefore(iframe, ref);
            // store reference to clean Array
            ctr = iframe.contentWindow.Array;
            // cleanup
            ref.parentNode.removeChild(iframe);

            return ctr;
        })(),
        NullElement = function () { },
        // errors
        DOMMethodError = function (methodName, objectName, hashName) {
            this.name = "DOMMethodError";
            // http://domjs.net/doc/{objectName}/{methodName}[#{hashName}]
            this.message = "Invalid call of the " + methodName +
                " method. See http://domjs.net/doc/" + methodName + " for details";
        };

    // DOMElement

    DOMElement.prototype = {
        find: function (filter) {
            if (typeof filter !== "string") {
                throw new DOMMethodError("find");
            }

            var element;

            if (filter.charAt(0) === "#" && filter.indexOf(" ") === -1) {
                element = document.getElementById(filter.substr(1));
            } else {
                element = this.querySelector(filter);
            }

            return factory.create(element);
        },
        findAll: function(filter) {
            if (typeof filter !== "string") {
                throw new DOMMethodError("search");
            }

            var elements;

            if (filter.charAt(0) === ">") {
                filter = filter.length > 1 ? quickParse(filter.substr(1).trim()) : null;
                elements = [];

                for (var n = this.firstChild; n; n = n.nextSibling) {
                    if (n.nodeType === 1 && (!filter || quickIs(n, filter))) {
                        elements.push(n);
                    }
                }
            } else {
                elements = this.querySelectorAll(filter);
            }

            return factory.create(elements);
        },
        on: (function () {
            var getArgValue = function(arg) {
                    return this[arg];
                },
                eventElementProperties = ["target", "currentTarget", "relatedTarget"],
                processHandlers = function (event, filter, handler, thisPtr) {
                    if (typeof handler !== "function") {
                        throw new DOMMethodError("on");
                    }

                    filter = filter ? quickParse(filter) : null;
                    thisPtr = thisPtr || this._DOM;

                    var nativeEventHandler = function (e) {
                            var originalProperties = {};

                            // modify event object
                            eventElementProperties.forEach(function(prop) {
                                var element = e[prop];

                                delete e[prop];

                                e[prop] = factory.create(element);

                                originalProperties[prop] = element;
                            });

                            handler.call(thisPtr, e);

                            // restore event object
                            eventElementProperties.forEach(function(prop) {
                                e[prop] = originalProperties[prop];
                            });
                        };
                    // TODO: store handler in _events property of the native element
                    this.addEventListener(event, !filter ? nativeEventHandler : function (e) {
                        for (var el = e.target, root = this.parentNode; el !== root; el = el.parentNode) {
                            if (quickIs(el, filter)) {
                                nativeEventHandler(e);

                                break;
                            }
                        }
                    }, false);
                };

            return function (event, filter, handler, thisPtr) {
                var filterType = typeof filter,
                    eventType = typeof event;

                if (filterType === "function") {
                    thisPtr = handler;
                    handler = filter;
                    filter = undefined;
                } else if (filter && filterType !== "string") {
                    throw new DOMMethodError("on");
                }

                if (eventType === "string") {
                    processHandlers.call(this, event, filter, handler, thisPtr);
                } else if (event && eventType === "object") {
                    Object.keys(event).forEach(function (eventType) {
                        processHandlers.call(this, eventType, undefined, event[eventType], thisPtr);
                    }, this);
                } else {
                    throw new DOMMethodError("on");
                }

                return this;
            };
        })(),
        fire: function (eventType, detail) {
            var event;

            if (detail !== undefined) {
                event = new CustomEvent(eventType, {detail: detail});
            } else {
                event = document.createEvent(eventType);
                event.initEvent(eventType, true, true);
            }

            this.dispatchEvent(event);

            return this;
        },
        get: function (name) {
            var value = this[name];

            if (value === undefined) {
                value = this.getAttribute(name);
            }

            return value;
        },
        set: (function () {
            var processAttribute = function (name, value) {
                var valueType = typeof value;

                if (valueType === "function") {
                    value = value.call(this._DOM, DOMElement.prototype.get.call(this, name));
                } else if (valueType !== "string") {
                    throw new DOMMethodError("set");
                }

                if (name in this) {
                    this[name] = value;
                } else {
                    this.setAttribute(name, value);
                }
            };

            return function (name, value) {
                var nameType = typeof name;

                if (nameType === "string") {
                    processAttribute.call(this, name, value);
                } else if (name && nameType === "object") {
                    Object.keys(name).forEach(function (attrName) {
                        processAttribute.call(this, attrName, name[attrName]);
                    }, this);
                } else {
                    throw new DOMMethodError("set");
                }

                return this;
            };
        })(),
        is: function (filter) {
            var quick = quickParse(filter);

            if (!quick) {
                throw new DOMMethodError("is");
            }

            return quickIs(this, quick);
        },
        call: function (name) {
            var functor = this[name], result;

            if (typeof functor !== "function") {
                throw new DOMMethodError("call");
            }

            result = functor.apply(this, arguments.length > 1 ?
                Array.prototype.splice.call(arguments, 1) : undefined);

            return result === undefined ? this : result;
        },
        show: function () {
            this.style.display = "";

            return this;
        },
        hide: function () {
            this.style.display = "none";

            return this;
        },
        remove: function () {
            this.parentNode.removeChild(this);
            // cleanup cache entry
            factory.remove(this._DOM.guid);

            return this;
        },
        replace: function (elem) {
            if (elem.constructor === DOMElement) {
                this.replaceChild(this, elem._native);
            } else {
                throw new DOMMethodError("replace");
            }

            return this;
        },
        clone: function (deep) {
            return factory.create(this.clone(deep));
        },
        css: function () {
            return window.getComputedStyle(this);
        }
    };

    // dom manipulation
    (function () {
        var strategies = {
            after: function (element) {
                this.parentNode.insertBefore(element, this.nextSibling);
            },
            before: function (element) {
                this.parentNode.insertBefore(element, this);
            },
            append: function (element) {
                this.appendChild(element);
            },
            prepend: function (element) {
                this.insertBefore(element, this.firstChild);
            }
        };

        Object.keys(strategies).forEach(function (methodName) {
            var process = strategies[methodName];

            DOMElement.prototype[methodName] = function (element) {
                var fragment;

                if (element.constructor === DOMElement) {
                    fragment = factory.get(element.guid);
                } else if (element.constructor === DOMElements) {
                    fragment = document.createDocumentFragment();

                    factory.get(element.guid).forEach(function (element) {
                        fragment.appendChild(factory.get(element.guid));
                    });
                } else {
                    throw new DOMMethodError(methodName);
                }

                process.call(this, fragment);

                return this;
            };
        });
    })();

    // classes manipulation
    (function () {
        var rclass = /[\n\t\r]/g,
            strategies = document.documentElement.classList ? {
                hasClass: function (className) {
                    return this.classList.constains(className);
                },
                addClass: function (className) {
                    this.classList.add(className);
                },
                removeClass: function (className) {
                    this.classList.remove(className);
                },
                toggleClass: function (className) {
                    this.classList.toggle(className);
                }
            } : {
                hasClass: function (className) {
                    return !!~((" " + this.className + " ")
                            .replace(rclass, " ")).indexOf(" " + className + " ");
                },
                addClass: function (className) {
                    if (!strategies.hasClass.call(this, className)) {
                        this.className += " " + className;
                    }
                },
                removeClass: function (className) {
                    this.className = (" " + this.className + " ")
                            .replace(rclass, " ").replace(" " + className + " ", " ").trim();
                },
                toggleClass: function (className) {
                    var originalClassName = this.className;

                    strategies.addClass.call(this, className);

                    if (originalClassName !== this.className) {
                        strategies.removeClass.call(this, className);
                    }
                }
            };

        Object.keys(strategies).forEach(function (methodName) {
            var process = strategies[methodName];

            DOMElement.prototype[methodName] = function (className) {
                if (typeof className !== "string") {
                    throw new DOMMethodError(methodName);
                }

                var classes = className.split(" ");

                if (methodName === "hasClass") {
                    return classes.any(process, this);
                } else {
                    classes.forEach(process, this);

                    return this;
                }
            };
        });

    })();

    // add guid support to prototype methods
    Object.keys(DOMElement.prototype).forEach(function (methodName) {
        var method = DOMElement.prototype[methodName];

        DOMElement.prototype[methodName] = function () {
            var element = factory.get(this.guid),
                getter = function() {
                    var result = method.apply(element, arguments);

                    return result === element ? this : result;
                };

            Object.defineProperty(this, methodName, {
                value: getter,
                writable: false,
                configurable: false
            });

            return getter.apply(this, arguments);
        };
    });

    // DOMElements

    // extend DOMElements.prototype with only specific methods
    ["set", "on", "show", "hide", "addClass", "removeClass", "toggleClass"].forEach(function (methodName) {
        var process = function (elem) {
                // 'this' will be an arguments object
                elem[methodName].apply(elem, this);
            };

        DOMElements.prototype[methodName] = function () {
            this.forEach(process, arguments);

            return this;
        };
    });

    // initialize null element prototype
    NullElement.prototype = {};

    Object.keys(DOMElement.prototype).forEach(function (method) {
        // each method is a noop function
        NullElement.prototype[method] = function () {};
    });

    // DOMMethodError
    DOMMethodError.prototype = new Error();

    // finish prototypes
    [DOMElement, DOMElements, NullElement].forEach(function (ctr) {
        // fix constructor
        ctr.prototype.constructor = ctr;
        // lock interfaces
        if (Object.freeze) {
            Object.freeze(ctr.prototype);
        }
    });

    // initialize publicAPI
    var publicAPI = Object.create(factory.create(document.documentElement), {
        create: {
            value: function (tagName, attrs, content) {
                if (typeof tagName !== "string") {
                    throw new DOMMethodError("create");
                }

                var elem = factory.create(document.createElement(tagName));

                if (attrs && typeof attrs === "object") {
                    elem.set(attrs);
                }

                if (content) {
                    if (typeof content === "string") {
                        elem.set("innerHTML", content);
                    } else if (content.constructor === DOMElement ||
                        content.constructor === DOMElements) {
                        elem.append(content);
                    }
                }

                return elem;
            }
        },
        ready: {
            value: (function () {
                var readyCallbacks = null,
                    readyProcess = function () {
                        if (readyCallbacks) {
                            // trigger callbacks
                            readyCallbacks.forEach(function (callback) {
                                callback();
                            });
                            // cleanup
                            readyCallbacks = null;
                        }
                    };

                if (document.readyState !== "complete") {
                    readyCallbacks = [];

                    document.addEventListener("DOMContentLoaded", readyProcess, false);
                    // additional handler for complex cases
                    window.addEventListener("load", readyProcess, false);
                }

                // return implementation
                return function (callback) {
                    if (typeof callback !== "function") {
                        throw new DOMMethodError("ready");
                    }

                    if (readyCallbacks) {
                        readyCallbacks.push(callback);
                    } else {
                        callback();
                    }
                };
            })()
        }
    });

    // make static properties readonly
    Object.getOwnPropertyNames(publicAPI).forEach(function (prop) {
        var desc = Object.getOwnPropertyDescriptor(publicAPI, prop);

        desc.configurable = false;
        desc.writable = false;

        Object.defineProperty(publicAPI, prop, desc);
    });

    // register API
    if (window.define) {
        define("DOM", [], function () { return publicAPI; });
    } else {
        window.DOM = publicAPI;
    }

})(window, document, undefined);<commit_msg>function ( -> function(<commit_after>/*!
 * DOM.js
 *
 * Copyright (c) 2013 Maksim Chemerisuk
 *
 * 1) encapsulation: completely hides native objects
 * 2) usability: provides more friendly apis
 * 3) safety: api can't be changed after initialization
 * 4) performance: use native methods where it's possible
 * 5) self-documenting: every error message has a link with detailed explaination
 * 6) jsdoc, generate documentation by it
 */
/*jslint browser:true*/
/*global define CustomEvent */
// TODO: replace, remove, specs
(function(window, document, undefined) {
    "use strict";

    var factory = (function() {
        var storage = [];

            return {
                get: function(guid) {
                    return storage[guid];
                },
                remove: function(guid) {
                    storage[guid] = null;
                },
                create: function(native) {
                    if (!native) return new NullElement();

                    var instance = native._DOM;

                    if (!instance) {
                        if (native.length === undefined) {
                            native._DOM = (instance = new DOMElement(native));
                        } else {
                            instance = new DOMElements();

                            Array.prototype.forEach.call(native, function(e) {
                                instance.push(factory.create(e));
                            });
                        }

                        instance.guid = storage.push(native) - 1;
                        // data should be a simple object without toString, hasOwnProperty etc.
                        instance.data = Object.create(null);
                    }

                    return instance;
                }
            };
        })(),
        // helpers
        rquickIs = /^(\w*)(?:#([\w\-]+))?(?:\[([\w\-]+)\])?(?:\.([\w\-]+))?$/,

        quickParse = function(selector) {
            var quick = rquickIs.exec(selector);
            // TODO: support attribute value check
            if (quick) {
                //   0  1    2   3          4
                // [ _, tag, id, attribute, class ]
                quick[1] = (quick[1] || "").toLowerCase();
                quick[4] = quick[4] ? " " + quick[4] + " " : "";
            } else {
                throw new DOMMethodError("quickParse");
            }

            return quick;
        },
        quickIs = function(elem, m) {
            var attrs = elem.attributes || {};

            return (
                (!m[1] || elem.nodeName.toLowerCase() === m[1]) &&
                (!m[2] || (attrs.id || {}).value === m[2]) &&
                (!m[3] || m[3] in attrs) &&
                (!m[4] || ~(" " + (attrs["class"] || "").value  + " ").indexOf(m[4]))
            );
        },
        // classes
        DOMElement = function(native) { },
        DOMElements = (function() {
            // Creates clean copy of Array prototype. Inspired by
            // http://dean.edwards.name/weblog/2006/11/hooray/
            var ref = document.getElementsByTagName('script')[0],
                iframe = document.createElement("iframe"),
                ctr;

            iframe.src = "about:blank";
            iframe.style.display = "none";

            ref.parentNode.insertBefore(iframe, ref);
            // store reference to clean Array
            ctr = iframe.contentWindow.Array;
            // cleanup
            ref.parentNode.removeChild(iframe);

            return ctr;
        })(),
        NullElement = function() { },
        // errors
        DOMMethodError = function(methodName, objectName, hashName) {
            this.name = "DOMMethodError";
            // http://domjs.net/doc/{objectName}/{methodName}[#{hashName}]
            this.message = "Invalid call of the " + methodName +
                " method. See http://domjs.net/doc/" + methodName + " for details";
        };

    // DOMElement

    DOMElement.prototype = {
        find: function(filter) {
            if (typeof filter !== "string") {
                throw new DOMMethodError("find");
            }

            var element;

            if (filter.charAt(0) === "#" && filter.indexOf(" ") === -1) {
                element = document.getElementById(filter.substr(1));
            } else {
                element = this.querySelector(filter);
            }

            return factory.create(element);
        },
        findAll: function(filter) {
            if (typeof filter !== "string") {
                throw new DOMMethodError("search");
            }

            var elements;

            if (filter.charAt(0) === ">") {
                filter = filter.length > 1 ? quickParse(filter.substr(1).trim()) : null;
                elements = [];

                for (var n = this.firstChild; n; n = n.nextSibling) {
                    if (n.nodeType === 1 && (!filter || quickIs(n, filter))) {
                        elements.push(n);
                    }
                }
            } else {
                elements = this.querySelectorAll(filter);
            }

            return factory.create(elements);
        },
        on: (function() {
            var getArgValue = function(arg) {
                    return this[arg];
                },
                eventElementProperties = ["target", "currentTarget", "relatedTarget"],
                processHandlers = function(event, filter, handler, thisPtr) {
                    if (typeof handler !== "function") {
                        throw new DOMMethodError("on");
                    }

                    filter = filter ? quickParse(filter) : null;
                    thisPtr = thisPtr || this._DOM;

                    var nativeEventHandler = function(e) {
                            var originalProperties = {};

                            // modify event object
                            eventElementProperties.forEach(function(prop) {
                                var element = e[prop];

                                delete e[prop];

                                e[prop] = factory.create(element);

                                originalProperties[prop] = element;
                            });

                            handler.call(thisPtr, e);

                            // restore event object
                            eventElementProperties.forEach(function(prop) {
                                e[prop] = originalProperties[prop];
                            });
                        };
                    // TODO: store handler in _events property of the native element
                    this.addEventListener(event, !filter ? nativeEventHandler : function(e) {
                        for (var el = e.target, root = this.parentNode; el !== root; el = el.parentNode) {
                            if (quickIs(el, filter)) {
                                nativeEventHandler(e);

                                break;
                            }
                        }
                    }, false);
                };

            return function(event, filter, handler, thisPtr) {
                var filterType = typeof filter,
                    eventType = typeof event;

                if (filterType === "function") {
                    thisPtr = handler;
                    handler = filter;
                    filter = undefined;
                } else if (filter && filterType !== "string") {
                    throw new DOMMethodError("on");
                }

                if (eventType === "string") {
                    processHandlers.call(this, event, filter, handler, thisPtr);
                } else if (event && eventType === "object") {
                    Object.keys(event).forEach(function(eventType) {
                        processHandlers.call(this, eventType, undefined, event[eventType], thisPtr);
                    }, this);
                } else {
                    throw new DOMMethodError("on");
                }

                return this;
            };
        })(),
        fire: function(eventType, detail) {
            var event;

            if (detail !== undefined) {
                event = new CustomEvent(eventType, {detail: detail});
            } else {
                event = document.createEvent(eventType);
                event.initEvent(eventType, true, true);
            }

            this.dispatchEvent(event);

            return this;
        },
        get: function(name) {
            var value = this[name];

            if (value === undefined) {
                value = this.getAttribute(name);
            }

            return value;
        },
        set: (function() {
            var processAttribute = function(name, value) {
                var valueType = typeof value;

                if (valueType === "function") {
                    value = value.call(this._DOM, DOMElement.prototype.get.call(this, name));
                } else if (valueType !== "string") {
                    throw new DOMMethodError("set");
                }

                if (name in this) {
                    this[name] = value;
                } else {
                    this.setAttribute(name, value);
                }
            };

            return function(name, value) {
                var nameType = typeof name;

                if (nameType === "string") {
                    processAttribute.call(this, name, value);
                } else if (name && nameType === "object") {
                    Object.keys(name).forEach(function(attrName) {
                        processAttribute.call(this, attrName, name[attrName]);
                    }, this);
                } else {
                    throw new DOMMethodError("set");
                }

                return this;
            };
        })(),
        is: function(filter) {
            var quick = quickParse(filter);

            if (!quick) {
                throw new DOMMethodError("is");
            }

            return quickIs(this, quick);
        },
        call: function(name) {
            var functor = this[name], result;

            if (typeof functor !== "function") {
                throw new DOMMethodError("call");
            }

            result = functor.apply(this, arguments.length > 1 ?
                Array.prototype.splice.call(arguments, 1) : undefined);

            return result === undefined ? this : result;
        },
        show: function() {
            this.style.display = "";

            return this;
        },
        hide: function() {
            this.style.display = "none";

            return this;
        },
        remove: function() {
            this.parentNode.removeChild(this);
            // cleanup cache entry
            factory.remove(this._DOM.guid);

            return this;
        },
        replace: function(elem) {
            if (elem.constructor === DOMElement) {
                this.replaceChild(this, elem._native);
            } else {
                throw new DOMMethodError("replace");
            }

            return this;
        },
        clone: function(deep) {
            return factory.create(this.clone(deep));
        },
        css: function() {
            return window.getComputedStyle(this);
        }
    };

    // dom manipulation
    (function() {
        var strategies = {
            after: function(element) {
                this.parentNode.insertBefore(element, this.nextSibling);
            },
            before: function(element) {
                this.parentNode.insertBefore(element, this);
            },
            append: function(element) {
                this.appendChild(element);
            },
            prepend: function(element) {
                this.insertBefore(element, this.firstChild);
            }
        };

        Object.keys(strategies).forEach(function(methodName) {
            var process = strategies[methodName];

            DOMElement.prototype[methodName] = function(element) {
                var fragment;

                if (element.constructor === DOMElement) {
                    fragment = factory.get(element.guid);
                } else if (element.constructor === DOMElements) {
                    fragment = document.createDocumentFragment();

                    factory.get(element.guid).forEach(function(element) {
                        fragment.appendChild(factory.get(element.guid));
                    });
                } else {
                    throw new DOMMethodError(methodName);
                }

                process.call(this, fragment);

                return this;
            };
        });
    })();

    // classes manipulation
    (function() {
        var rclass = /[\n\t\r]/g,
            strategies = document.documentElement.classList ? {
                hasClass: function(className) {
                    return this.classList.constains(className);
                },
                addClass: function(className) {
                    this.classList.add(className);
                },
                removeClass: function(className) {
                    this.classList.remove(className);
                },
                toggleClass: function(className) {
                    this.classList.toggle(className);
                }
            } : {
                hasClass: function(className) {
                    return !!~((" " + this.className + " ")
                            .replace(rclass, " ")).indexOf(" " + className + " ");
                },
                addClass: function(className) {
                    if (!strategies.hasClass.call(this, className)) {
                        this.className += " " + className;
                    }
                },
                removeClass: function(className) {
                    this.className = (" " + this.className + " ")
                            .replace(rclass, " ").replace(" " + className + " ", " ").trim();
                },
                toggleClass: function(className) {
                    var originalClassName = this.className;

                    strategies.addClass.call(this, className);

                    if (originalClassName !== this.className) {
                        strategies.removeClass.call(this, className);
                    }
                }
            };

        Object.keys(strategies).forEach(function(methodName) {
            var process = strategies[methodName];

            DOMElement.prototype[methodName] = function(className) {
                if (typeof className !== "string") {
                    throw new DOMMethodError(methodName);
                }

                var classes = className.split(" ");

                if (methodName === "hasClass") {
                    return classes.any(process, this);
                } else {
                    classes.forEach(process, this);

                    return this;
                }
            };
        });

    })();

    // add guid support to prototype methods
    Object.keys(DOMElement.prototype).forEach(function(methodName) {
        var method = DOMElement.prototype[methodName];

        DOMElement.prototype[methodName] = function() {
            var element = factory.get(this.guid),
                getter = function() {
                    var result = method.apply(element, arguments);

                    return result === element ? this : result;
                };

            Object.defineProperty(this, methodName, {
                value: getter,
                writable: false,
                configurable: false
            });

            return getter.apply(this, arguments);
        };
    });

    // DOMElements

    // extend DOMElements.prototype with only specific methods
    ["set", "on", "show", "hide", "addClass", "removeClass", "toggleClass"].forEach(function(methodName) {
        var process = function(elem) {
                // 'this' will be an arguments object
                elem[methodName].apply(elem, this);
            };

        DOMElements.prototype[methodName] = function() {
            this.forEach(process, arguments);

            return this;
        };
    });

    // initialize null element prototype
    NullElement.prototype = {};

    Object.keys(DOMElement.prototype).forEach(function(method) {
        // each method is a noop function
        NullElement.prototype[method] = function() {};
    });

    // DOMMethodError
    DOMMethodError.prototype = new Error();

    // finish prototypes
    [DOMElement, DOMElements, NullElement].forEach(function(ctr) {
        // fix constructor
        ctr.prototype.constructor = ctr;
        // lock interfaces
        if (Object.freeze) {
            Object.freeze(ctr.prototype);
        }
    });

    // initialize publicAPI
    var publicAPI = Object.create(factory.create(document.documentElement), {
        create: {
            value: function(tagName, attrs, content) {
                if (typeof tagName !== "string") {
                    throw new DOMMethodError("create");
                }

                var elem = factory.create(document.createElement(tagName));

                if (attrs && typeof attrs === "object") {
                    elem.set(attrs);
                }

                if (content) {
                    if (typeof content === "string") {
                        elem.set("innerHTML", content);
                    } else if (content.constructor === DOMElement ||
                        content.constructor === DOMElements) {
                        elem.append(content);
                    }
                }

                return elem;
            }
        },
        ready: {
            value: (function() {
                var readyCallbacks = null,
                    readyProcess = function() {
                        if (readyCallbacks) {
                            // trigger callbacks
                            readyCallbacks.forEach(function(callback) {
                                callback();
                            });
                            // cleanup
                            readyCallbacks = null;
                        }
                    };

                if (document.readyState !== "complete") {
                    readyCallbacks = [];

                    document.addEventListener("DOMContentLoaded", readyProcess, false);
                    // additional handler for complex cases
                    window.addEventListener("load", readyProcess, false);
                }

                // return implementation
                return function(callback) {
                    if (typeof callback !== "function") {
                        throw new DOMMethodError("ready");
                    }

                    if (readyCallbacks) {
                        readyCallbacks.push(callback);
                    } else {
                        callback();
                    }
                };
            })()
        }
    });

    // make static properties readonly
    Object.getOwnPropertyNames(publicAPI).forEach(function(prop) {
        var desc = Object.getOwnPropertyDescriptor(publicAPI, prop);

        desc.configurable = false;
        desc.writable = false;

        Object.defineProperty(publicAPI, prop, desc);
    });

    // register API
    if (window.define) {
        define("DOM", [], function() { return publicAPI; });
    } else {
        window.DOM = publicAPI;
    }

})(window, document, undefined);<|endoftext|>


<commit_before>'use strict';

/**
 * @name open311
 * @description controller to handle open311 compliant requests
 * @see {@link http://wiki.open311.org/GeoReport_v2/| GeoReporter V2}
 * @since 0.1.0
 * @version 0.1.0
 * @public
 */

//dependencies
const _ = require('lodash');
const config = require('config');
const mongoose = require('mongoose');
const Service = mongoose.model('Service');
const ServiceRequest = mongoose.model('ServiceRequest');

//constants
const OPEN311_STATUS_OPEN = 'open';
const parseDate = function (date) {
  date = Date.parse(date);
  date = (date ? new Date(date) : undefined);
  return date;
};

//TODO handle date & other open 311 filter on get request(s) status
//TODO handle specific client issues
//TODO present error in 311 specifications
//TODO add support to API Key(client id & client credentials)

module.exports = {
  /**
   * @name discovery
   * @description handle /discovery request
   * @param  {HttpRequest} request  http request
   * @param  {HttpResponse} response http response
   * @since 0.1.0
   * @version 0.1.0
   * @public
   */
  discovery: function (request, response /*, next*/ ) {
    const _config = config.get('open311');
    response.ok(_config.discovery);
  },

  /**
   * @name services
   * @description handle /services request.
   *
   *              It provides a list of all acceptable service request types
   *              and their associated service codes.
   *
   *
   * @param  {HttpRequest} request  http request
   * @param  {HttpResponse} response http response
   * @since 0.1.0
   * @version 0.1.0
   * @public
   */
  services: function (request, response, next) {

    //initialize query criteria
    let criteria = { isExternal: true };

    //obtain provided jurisdiction criteria
    const jurisdiction = _.get(request, 'query.jurisdiction_id');

    //merge & clean criteria
    criteria = _.merge({}, criteria, { jurisdiction: jurisdiction });
    criteria = _.omitBy(criteria, _.isUndefined);

    Service
      .find(criteria)
      .populate('group')
      .populate('jurisdiction')
      .exec(function (error, services) {
        if (error) {
          next(error);
        } else {
          //map services to open311 compliant service list
          services = _.map(services, function (service) {
            return service.toOpen311();
          });

          response.ok(services);
        }
      });
  },

  /**
   * @function
   * @name open311.index()
   * @description display a list of all service request in open311 compliant fomart
   * @param  {HttpRequest} request  a http request
   * @param  {HttpResponse} response a http response
   * @see {@link http://wiki.open311.org/GeoReport_v2/#get-service-requests}
   */
  index: function (request, response, next) {
    //TODO filter per client
    //TODO filter by service_request_id
    //TODO filter by service_code

    //initialize query criteria
    let criteria = {};

    //obtain provided jurisdiction criteria
    criteria.jurisdiction = _.get(request, 'query.jurisdiction_id');

    //filter by provided status criteria
    criteria.status = _.get(request, 'query.status');
    if (!_.isEmpty(criteria.status)) {
      criteria.resolvedAt =
        (criteria.status === OPEN311_STATUS_OPEN ? { $eq: null } : { $ne: null });
    }
    delete criteria.status;

    //filter by provided start date criteria
    criteria.startedAt = parseDate(_.get(request, 'query.start_date'));
    if (_.isDate(criteria.startedAt)) {
      criteria.createdAt = { $gte: criteria.startedAt };
    }
    delete criteria.startedAt;

    //filter by provided end date criteria
    criteria.endedAt = parseDate(_.get(request, 'query.end_date'));
    if (_.isDate(criteria.endedAt)) {
      criteria.createdAt =
        _.merge({}, criteria.createdAt, { $lte: criteria.endedAt });
    }
    delete criteria.endedAt;

    //update criteria with reporter filters
    //TODO update specification
    criteria.skip = _.get(request, 'query.skip', 0);
    criteria.limit = _.get(request, 'query.limit', 10);
    criteria = _.merge({}, _.get(request.mquery, 'query'), criteria);

    //merge & clean criteria
    criteria = _.omitBy(criteria, _.isUndefined);

    //TODO make use of stream api

    ServiceRequest
      .find(criteria)
      .sort(_.get(request, 'mquery.sort', { createdAt: -1 }))
      .skip(criteria.skip)
      .limit(criteria.limit)
      .exec(function (error, serviceRequests) {
        if (error) {
          next(error);
        } else {
          //map serviceRequests to open311 compliant serviceRequest list
          serviceRequests =
            _.map(serviceRequests, function (serviceRequest) {
              return serviceRequest.toOpen311();
            });

          response.ok(serviceRequests);
        }
      });
  },

  /**
   * @function
   * @name open311.create()
   * @description create a new service request from open311 compliant client
   * @param  {HttpRequest} request  a http request
   * @param  {HttpResponse} response a http response
   */
  create: function (request, response, next) {
    //TODO support media upload & media_url
    let serviceRequest = request.body;

    ServiceRequest
      .createFromOpen311Client(serviceRequest,
        function (error, open311Response /*, serviceRequest*/ ) {
          if (error) {
            next(error);
          } else {
            response.created(open311Response);
          }
        });
  },

  /**
   * @function
   * @name open311.show()
   * @description display a specific service request in open 311 compliant format
   * @param  {HttpRequest} request  a http request
   * @param  {HttpResponse} response a http response
   */
  show: function (request, response, next) {

    //obtain service request code
    const code = request.params.id;

    ServiceRequest
      .findOne({ code: code })
      .exec(function (error, serviceRequest) {
        if (error || !serviceRequest) {
          if (!error) {
            error = new Error('Not Found');
            error.status = 404;
          }
          next(error);
        } else {
          response.ok([serviceRequest.toOpen311()]);
        }
      });
  }

};
<commit_msg>fix limiting<commit_after>'use strict';

/**
 * @name open311
 * @description controller to handle open311 compliant requests
 * @see {@link http://wiki.open311.org/GeoReport_v2/| GeoReporter V2}
 * @since 0.1.0
 * @version 0.1.0
 * @public
 */

//dependencies
const _ = require('lodash');
const config = require('config');
const mongoose = require('mongoose');
const Service = mongoose.model('Service');
const ServiceRequest = mongoose.model('ServiceRequest');

//constants
const OPEN311_STATUS_OPEN = 'open';
const parseDate = function (date) {
  date = Date.parse(date);
  date = (date ? new Date(date) : undefined);
  return date;
};

//TODO handle date & other open 311 filter on get request(s) status
//TODO handle specific client issues
//TODO present error in 311 specifications
//TODO add support to API Key(client id & client credentials)

module.exports = {
  /**
   * @name discovery
   * @description handle /discovery request
   * @param  {HttpRequest} request  http request
   * @param  {HttpResponse} response http response
   * @since 0.1.0
   * @version 0.1.0
   * @public
   */
  discovery: function (request, response /*, next*/ ) {
    const _config = config.get('open311');
    response.ok(_config.discovery);
  },

  /**
   * @name services
   * @description handle /services request.
   *
   *              It provides a list of all acceptable service request types
   *              and their associated service codes.
   *
   *
   * @param  {HttpRequest} request  http request
   * @param  {HttpResponse} response http response
   * @since 0.1.0
   * @version 0.1.0
   * @public
   */
  services: function (request, response, next) {

    //initialize query criteria
    let criteria = { isExternal: true };

    //obtain provided jurisdiction criteria
    const jurisdiction = _.get(request, 'query.jurisdiction_id');

    //merge & clean criteria
    criteria = _.merge({}, criteria, { jurisdiction: jurisdiction });
    criteria = _.omitBy(criteria, _.isUndefined);

    Service
      .find(criteria)
      .populate('group')
      .populate('jurisdiction')
      .exec(function (error, services) {
        if (error) {
          next(error);
        } else {
          //map services to open311 compliant service list
          services = _.map(services, function (service) {
            return service.toOpen311();
          });

          response.ok(services);
        }
      });
  },

  /**
   * @function
   * @name open311.index()
   * @description display a list of all service request in open311 compliant fomart
   * @param  {HttpRequest} request  a http request
   * @param  {HttpResponse} response a http response
   * @see {@link http://wiki.open311.org/GeoReport_v2/#get-service-requests}
   */
  index: function (request, response, next) {
    //TODO filter per client
    //TODO filter by service_request_id
    //TODO filter by service_code

    //initialize query criteria
    let criteria = {};

    //obtain provided jurisdiction criteria
    criteria.jurisdiction = _.get(request, 'query.jurisdiction_id');

    //filter by provided status criteria
    criteria.status = _.get(request, 'query.status');
    if (!_.isEmpty(criteria.status)) {
      criteria.resolvedAt =
        (criteria.status === OPEN311_STATUS_OPEN ? { $eq: null } : { $ne: null });
    }
    delete criteria.status;

    //filter by provided start date criteria
    criteria.startedAt = parseDate(_.get(request, 'query.start_date'));
    if (_.isDate(criteria.startedAt)) {
      criteria.createdAt = { $gte: criteria.startedAt };
    }
    delete criteria.startedAt;

    //filter by provided end date criteria
    criteria.endedAt = parseDate(_.get(request, 'query.end_date'));
    if (_.isDate(criteria.endedAt)) {
      criteria.createdAt =
        _.merge({}, criteria.createdAt, { $lte: criteria.endedAt });
    }
    delete criteria.endedAt;

    //update criteria with reporter filters
    //TODO update specification
    const skip = _.get(request, 'query.skip', 0);
    const limit = _.get(request, 'query.limit', 10);
    criteria = _.merge({}, _.get(request.mquery, 'query'), criteria);

    //merge & clean criteria
    criteria = _.omitBy(criteria, _.isUndefined);

    //TODO make use of stream api

    ServiceRequest
      .find(criteria)
      .sort(_.get(request, 'mquery.sort', { createdAt: -1 }))
      .skip(skip)
      .limit(limit)
      .exec(function (error, serviceRequests) {
        if (error) {
          next(error);
        } else {
          //map serviceRequests to open311 compliant serviceRequest list
          serviceRequests =
            _.map(serviceRequests, function (serviceRequest) {
              return serviceRequest.toOpen311();
            });

          response.ok(serviceRequests);
        }
      });
  },

  /**
   * @function
   * @name open311.create()
   * @description create a new service request from open311 compliant client
   * @param  {HttpRequest} request  a http request
   * @param  {HttpResponse} response a http response
   */
  create: function (request, response, next) {
    //TODO support media upload & media_url
    let serviceRequest = request.body;

    ServiceRequest
      .createFromOpen311Client(serviceRequest,
        function (error, open311Response /*, serviceRequest*/ ) {
          if (error) {
            next(error);
          } else {
            response.created(open311Response);
          }
        });
  },

  /**
   * @function
   * @name open311.show()
   * @description display a specific service request in open 311 compliant format
   * @param  {HttpRequest} request  a http request
   * @param  {HttpResponse} response a http response
   */
  show: function (request, response, next) {

    //obtain service request code
    const code = request.params.id;

    ServiceRequest
      .findOne({ code: code })
      .exec(function (error, serviceRequest) {
        if (error || !serviceRequest) {
          if (!error) {
            error = new Error('Not Found');
            error.status = 404;
          }
          next(error);
        } else {
          response.ok([serviceRequest.toOpen311()]);
        }
      });
  }

};
<|endoftext|>


<commit_before>/**
 * @license
 * Copyright 2014 Google Inc. All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

var $documents = [];

if ( window ) $documents.push(window.document);

// TODO: clean this up, hide $WID__ in closure
var $WID__ = 0;
function $addWindow(w) {
   w.window.$WID = $WID__++;
   $documents.push(w.document);
}
function $removeWindow(w) {
  for ( var i = $documents.length - 1 ; i >= 0 ; i-- ) {
    if ( ! $documents[i].defaultView || $documents[i].defaultView === w )
      $documents.splice(i,1);
  }
}

/** Replacement for getElementById **/
// TODO(kgr): remove this is deprecated, use X.$ instead()
var $ = function (id) {
  console.log('Deprecated use of GLOBAL.$.');
  for ( var i = 0 ; i < $documents.length ; i++ ) {
    if ( document.FOAM_OBJECTS && document.FOAM_OBJECTS[id] )
      return document.FOAM_OBJECTS[id];

    var ret = $documents[i].getElementById(id);

    if ( ret ) return ret;
  }
  return undefined;
};
/** Replacement for getElementByClassName **/
// TODO(kgr): remove this is deprecated, use X.$$ instead()
var $$ = function (cls) {
  console.log('Deprecated use of GLOBAL.$$.');
  for ( var i = 0 ; i < $documents.length ; i++ ) {
    var ret = $documents[i].getElementsByClassName(cls);

    if ( ret.length > 0 ) return ret;
  }
  return [];
};

var FOAM = function(map, opt_X, seq) {
   var obj = JSONUtil.mapToObj(opt_X || X, map, undefined, seq);
   return obj;
};

/**
 * Register a lazy factory for the specified name within a
 * specified context.
 * The first time the name is looked up, the factory will be invoked
 * and its value will be stored in the named slot and then returned.
 * Future lookups to the same slot will return the originally created
 * value.
 **/
FOAM.putFactory = function(ctx, name, factory) {
  ctx.__defineGetter__(name, function() {
    console.log('Bouncing Factory: ', name);
    delete ctx[name];
    return ctx[name] = factory();
  });
};

var   USED_MODELS = {};
var UNUSED_MODELS = {};
var NONMODEL_INSTANCES = {}; // for things such as interfaces

FOAM.browse = function(model, opt_dao, opt_X) {
   var Y = opt_X || X.sub(undefined, "FOAM BROWSER");

   if ( typeof model === 'string' ) model = Y[model];

   var dao = opt_dao || Y[model.name + 'DAO'] || Y[model.plural];

   if ( ! dao ) {
      Y[model.name + 'DAO'] = [].dao;
   }

   var ctrl = Y.foam.ui.DAOController.create({
     model:     model,
     dao:       dao,
     useSearchView: false
   });

  if ( ! Y.stack ) {
    var w = opt_X ? opt_X.window : window;
    Y.stack = Y.foam.ui.StackView.create();
    var win = Y.foam.ui.layout.Window.create({ window: w, data: Y.stack }, Y);
    document.body.insertAdjacentHTML('beforeend', win.toHTML());
    win.initHTML();
    Y.stack.setTopView(ctrl);
  } else {
    Y.stack.pushView(ctrl);
  }
};

var arequire = function(modelName, opt_X) {
  var X = opt_X || GLOBAL.X;
  var model = X.lookup(modelName);
  if ( ! model ) {
    if ( ! X.ModelDAO ) {
      // if ( modelName !== 'Template' ) console.warn('Unknown Model in arequire: ', modelName);
      return aconstant();
    }

    // check whether we have already hit the ModelDAO to load the model
    if ( ! X.arequire$ModelLoadsInProgress ) {
      X.set('arequire$ModelLoadsInProgress', {} );
    } else {
      if ( X.arequire$ModelLoadsInProgress[modelName] ) {
        return X.arequire$ModelLoadsInProgress[modelName];
      }
    }

    var future = afuture();
    X.arequire$ModelLoadsInProgress[modelName] = future.get;

    X.ModelDAO.find(modelName, {
      put: function(m) {
        // Contextualize the model for this context
        m.X = X;

        m.arequire()(function(m) {
          X.arequire$ModelLoadsInProgress[modelName] = false;
          X.registerModel(m);
          future.set(m);
        });
      },
      error: function() {
        var args = argsToArray(arguments);
        console.warn.apply(console, ['Could not load model: ', modelName].concat(args));
        X.arequire$ModelLoadsInProgress[modelName] = false;
        future.set(undefined);
      }
    });

    return future.get;
  }

  return model.arequire()
}

var FOAM_POWERED = '\
\
FOAM\
 POWERED';

/** Lookup a '.'-separated package path, creating sub-packages as required. **/
function packagePath(X, path) {
  function packagePath_(Y, path, i) {
    if ( i === path.length ) return Y;
    if ( ! Y[path[i]] ) {
      Y[path[i]] = {};
      // console.log('************ Creating sub-path: ', path[i]);
      if ( i == 0 ) GLOBAL[path[i]] = Y[path[i]];
    }
    return packagePath_(Y[path[i]], path, i+1);
  }
  return path ? packagePath_(X, path.split('.'), 0) : GLOBAL;
}

function registerModel(model, opt_name, fastMode) {
  var root    = model.package ? this : GLOBAL;
  var name    = model.name;
  var package = model.package;

  if ( opt_name ) {
    var a = opt_name.split('.');
    name = a.pop();
    package = a.join('.');
  }

  var path = packagePath(root, package);
  if ( fastMode )
    path[name] = model;
  else
    Object.defineProperty(path, name, { value: model, configurable: true });

  // TODO: this is broken
  // update the cache if this model was already FOAM.lookup'd
  if ( root.lookupCache_ ) {
    var cache = root.lookupCache_;
    var modelRegName = (package ? package + '.' : '') + name;
//    if ( cache[modelRegName] ) {
      // console.log("registerModel: in lookupCache_, replaced model ", modelRegName );
      cache[modelRegName] = model;
//    }
  }

  this.onRegisterModel(model);
}

var CLASS = function(m) {

  // Don't Latch these Models, as we know that we'll need them on startup
  var EAGER = {
    'Method': true,
    'BooleanProperty': true,
    'Action': true,
    'FunctionProperty': true,
    'Constant': true,
    'Message': true,
    'ArrayProperty': true,
    'StringArrayProperty': true,
    'Template': true,
    'Arg': true,
    'Relationship': true,
    'ViewFactoryProperty': true,
    'FactoryProperty': true,
    'foam.ui.Window': true,
    'StringProperty': true,
    'foam.html.Element': true,
    'Expr': true,
    'AbstractDAO': true,
    'foam.ui.FoamTagView': true,
    'foam.ui.View': true,
    'foam.ui.DestructiveDataView': true,
    'foam.ui.HTMLViewTrait': true,
    'foam.core.types.DocumentInstallProperty': true,
    'foam.ui.BaseView': true,
    'foam.patterns.ChildTreeTrait': true
  };

  /** Lazily create and register Model first time it's accessed. **/
  function registerModelLatch(path, m) {
    var id = m.package ? m.package + '.' + m.name : m.name;

    if ( EAGER[id] ) {
      USED_MODELS[id] = true;
      var work = [];
      var model = JSONUtil.mapToObj(X, m, Model, work);
      if ( work.length > 0 ) {
        model.extra__ = aseq.apply(null, work);
      }

      X.registerModel(model, undefined, true);

      return model;
    }

    GLOBAL.lookupCache_[id] = undefined;
    UNUSED_MODELS[id] = true;
    var triggered = false;

    //console.log("Model Getting defined: ", m.name, X.NAME);
    Object.defineProperty(m.package ? path : GLOBAL, m.name, {
      get: function triggerModelLatch() {
        if ( triggered ) return null;
        triggered = true;
        // console.time('registerModel: ' + id);
        USED_MODELS[id] = true;
        UNUSED_MODELS[id] = undefined;

        var work = [];
        // console.time('buildModel: ' + id);
        var model = JSONUtil.mapToObj(X, m, Model, work);
        // console.timeEnd('buildModel: ' + id);

        if ( work.length > 0 ) {
          model.extra__ = aseq.apply(null, work);
        }

        X.registerModel(model);

        // console.timeEnd('registerModel: ' + id);
        return model;
      },
      configurable: true
    });
  }

  if ( document && document.currentScript ) m.sourcePath = document.currentScript.src;

  registerModelLatch(packagePath(X, m.package), m);
}

var MODEL = CLASS;

function INTERFACE(imodel) {
  // Unless in DEBUG mode, just discard interfaces as they're only used for type checking.
  // if ( ! DEBUG ) return;
  var i = JSONUtil.mapToObj(X, imodel, Interface);
  packagePath(X, i.package)[i.name] = i;

  var id = i.package ? i.package + '.' + i.name : i.name;
  NONMODEL_INSTANCES[id] = true;
}

/** Called when a Model is registered. **/
function onRegisterModel(m) {
  // NOP
}
<commit_msg>Remove views from EAGER set of models until we have reliable ordering from the build tools.<commit_after>/**
 * @license
 * Copyright 2014 Google Inc. All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

var $documents = [];

if ( window ) $documents.push(window.document);

// TODO: clean this up, hide $WID__ in closure
var $WID__ = 0;
function $addWindow(w) {
   w.window.$WID = $WID__++;
   $documents.push(w.document);
}
function $removeWindow(w) {
  for ( var i = $documents.length - 1 ; i >= 0 ; i-- ) {
    if ( ! $documents[i].defaultView || $documents[i].defaultView === w )
      $documents.splice(i,1);
  }
}

/** Replacement for getElementById **/
// TODO(kgr): remove this is deprecated, use X.$ instead()
var $ = function (id) {
  console.log('Deprecated use of GLOBAL.$.');
  for ( var i = 0 ; i < $documents.length ; i++ ) {
    if ( document.FOAM_OBJECTS && document.FOAM_OBJECTS[id] )
      return document.FOAM_OBJECTS[id];

    var ret = $documents[i].getElementById(id);

    if ( ret ) return ret;
  }
  return undefined;
};
/** Replacement for getElementByClassName **/
// TODO(kgr): remove this is deprecated, use X.$$ instead()
var $$ = function (cls) {
  console.log('Deprecated use of GLOBAL.$$.');
  for ( var i = 0 ; i < $documents.length ; i++ ) {
    var ret = $documents[i].getElementsByClassName(cls);

    if ( ret.length > 0 ) return ret;
  }
  return [];
};

var FOAM = function(map, opt_X, seq) {
   var obj = JSONUtil.mapToObj(opt_X || X, map, undefined, seq);
   return obj;
};

/**
 * Register a lazy factory for the specified name within a
 * specified context.
 * The first time the name is looked up, the factory will be invoked
 * and its value will be stored in the named slot and then returned.
 * Future lookups to the same slot will return the originally created
 * value.
 **/
FOAM.putFactory = function(ctx, name, factory) {
  ctx.__defineGetter__(name, function() {
    console.log('Bouncing Factory: ', name);
    delete ctx[name];
    return ctx[name] = factory();
  });
};

var   USED_MODELS = {};
var UNUSED_MODELS = {};
var NONMODEL_INSTANCES = {}; // for things such as interfaces

FOAM.browse = function(model, opt_dao, opt_X) {
   var Y = opt_X || X.sub(undefined, "FOAM BROWSER");

   if ( typeof model === 'string' ) model = Y[model];

   var dao = opt_dao || Y[model.name + 'DAO'] || Y[model.plural];

   if ( ! dao ) {
      Y[model.name + 'DAO'] = [].dao;
   }

   var ctrl = Y.foam.ui.DAOController.create({
     model:     model,
     dao:       dao,
     useSearchView: false
   });

  if ( ! Y.stack ) {
    var w = opt_X ? opt_X.window : window;
    Y.stack = Y.foam.ui.StackView.create();
    var win = Y.foam.ui.layout.Window.create({ window: w, data: Y.stack }, Y);
    document.body.insertAdjacentHTML('beforeend', win.toHTML());
    win.initHTML();
    Y.stack.setTopView(ctrl);
  } else {
    Y.stack.pushView(ctrl);
  }
};

var arequire = function(modelName, opt_X) {
  var X = opt_X || GLOBAL.X;
  var model = X.lookup(modelName);
  if ( ! model ) {
    if ( ! X.ModelDAO ) {
      // if ( modelName !== 'Template' ) console.warn('Unknown Model in arequire: ', modelName);
      return aconstant();
    }

    // check whether we have already hit the ModelDAO to load the model
    if ( ! X.arequire$ModelLoadsInProgress ) {
      X.set('arequire$ModelLoadsInProgress', {} );
    } else {
      if ( X.arequire$ModelLoadsInProgress[modelName] ) {
        return X.arequire$ModelLoadsInProgress[modelName];
      }
    }

    var future = afuture();
    X.arequire$ModelLoadsInProgress[modelName] = future.get;

    X.ModelDAO.find(modelName, {
      put: function(m) {
        // Contextualize the model for this context
        m.X = X;

        m.arequire()(function(m) {
          X.arequire$ModelLoadsInProgress[modelName] = false;
          X.registerModel(m);
          future.set(m);
        });
      },
      error: function() {
        var args = argsToArray(arguments);
        console.warn.apply(console, ['Could not load model: ', modelName].concat(args));
        X.arequire$ModelLoadsInProgress[modelName] = false;
        future.set(undefined);
      }
    });

    return future.get;
  }

  return model.arequire()
}

var FOAM_POWERED = '\
\
FOAM\
 POWERED';

/** Lookup a '.'-separated package path, creating sub-packages as required. **/
function packagePath(X, path) {
  function packagePath_(Y, path, i) {
    if ( i === path.length ) return Y;
    if ( ! Y[path[i]] ) {
      Y[path[i]] = {};
      // console.log('************ Creating sub-path: ', path[i]);
      if ( i == 0 ) GLOBAL[path[i]] = Y[path[i]];
    }
    return packagePath_(Y[path[i]], path, i+1);
  }
  return path ? packagePath_(X, path.split('.'), 0) : GLOBAL;
}

function registerModel(model, opt_name, fastMode) {
  var root    = model.package ? this : GLOBAL;
  var name    = model.name;
  var package = model.package;

  if ( opt_name ) {
    var a = opt_name.split('.');
    name = a.pop();
    package = a.join('.');
  }

  var path = packagePath(root, package);
  if ( fastMode )
    path[name] = model;
  else
    Object.defineProperty(path, name, { value: model, configurable: true });

  // TODO: this is broken
  // update the cache if this model was already FOAM.lookup'd
  if ( root.lookupCache_ ) {
    var cache = root.lookupCache_;
    var modelRegName = (package ? package + '.' : '') + name;
//    if ( cache[modelRegName] ) {
      // console.log("registerModel: in lookupCache_, replaced model ", modelRegName );
      cache[modelRegName] = model;
//    }
  }

  this.onRegisterModel(model);
}

var CLASS = function(m) {

  // Don't Latch these Models, as we know that we'll need them on startup
  var EAGER = {
    'Method': true,
    'BooleanProperty': true,
    'Action': true,
    'FunctionProperty': true,
    'Constant': true,
    'Message': true,
    'ArrayProperty': true,
    'StringArrayProperty': true,
    'Template': true,
    'Arg': true,
    'Relationship': true,
    'ViewFactoryProperty': true,
    'FactoryProperty': true,
    'foam.ui.Window': true,
    'StringProperty': true,
    'foam.html.Element': true,
    'Expr': true,
    'AbstractDAO': true
  };

  /** Lazily create and register Model first time it's accessed. **/
  function registerModelLatch(path, m) {
    var id = m.package ? m.package + '.' + m.name : m.name;

    if ( EAGER[id] ) {
      USED_MODELS[id] = true;
      var work = [];
      var model = JSONUtil.mapToObj(X, m, Model, work);
      if ( work.length > 0 ) {
        model.extra__ = aseq.apply(null, work);
      }

      X.registerModel(model, undefined, true);

      return model;
    }

    GLOBAL.lookupCache_[id] = undefined;
    UNUSED_MODELS[id] = true;
    var triggered = false;

    //console.log("Model Getting defined: ", m.name, X.NAME);
    Object.defineProperty(m.package ? path : GLOBAL, m.name, {
      get: function triggerModelLatch() {
        if ( triggered ) return null;
        triggered = true;
        // console.time('registerModel: ' + id);
        USED_MODELS[id] = true;
        UNUSED_MODELS[id] = undefined;

        var work = [];
        // console.time('buildModel: ' + id);
        var model = JSONUtil.mapToObj(X, m, Model, work);
        // console.timeEnd('buildModel: ' + id);

        if ( work.length > 0 ) {
          model.extra__ = aseq.apply(null, work);
        }

        X.registerModel(model);

        // console.timeEnd('registerModel: ' + id);
        return model;
      },
      configurable: true
    });
  }

  if ( document && document.currentScript ) m.sourcePath = document.currentScript.src;

  registerModelLatch(packagePath(X, m.package), m);
}

var MODEL = CLASS;

function INTERFACE(imodel) {
  // Unless in DEBUG mode, just discard interfaces as they're only used for type checking.
  // if ( ! DEBUG ) return;
  var i = JSONUtil.mapToObj(X, imodel, Interface);
  packagePath(X, i.package)[i.name] = i;

  var id = i.package ? i.package + '.' + i.name : i.name;
  NONMODEL_INSTANCES[id] = true;
}

/** Called when a Model is registered. **/
function onRegisterModel(m) {
  // NOP
}
<|endoftext|>


<commit_before>/**
 * @license
 * Copyright 2013 Google Inc. All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

var $documents = [];

if ( window ) $documents.push(window.document);

// TODO: clean this up, hide $WID__ in closure
var $WID__ = 0;
function $addWindow(w) {
   w.window.$WID = $WID__++;
   $documents.push(w.document);
}
function $removeWindow(w) {
  for ( var i = $documents.length - 1 ; i >= 0 ; i-- ) {
    if ( ! $documents[i].defaultView || $documents[i].defaultView === w )
      $documents.splice(i,1);
  }
}

/** Replacement for getElementById **/
// TODO(kgr): remove this is deprecated, use X.$ instead()
var $ = function (id) {
  for ( var i = 0 ; i < $documents.length ; i++ ) {
    if ( document.FOAM_OBJECTS && document.FOAM_OBJECTS[id] )
      return document.FOAM_OBJECTS[id];

    var ret = $documents[i].getElementById(id);

    if ( ret ) return ret;
  }
  return undefined;
};
/** Replacement for getElementByClassName **/
// TODO(kgr): remove this is deprecated, use X.$$ instead()
var $$ = function (cls) {
  for ( var i = 0 ; i < $documents.length ; i++ ) {
    var ret = $documents[i].getElementsByClassName(cls);

    if ( ret.length > 0 ) return ret;
  }
  return [];
};

var FOAM = function(map, opt_X) {
   var obj = JSONUtil.mapToObj(opt_X || X, map);
   return obj;
};

/**
 * Register a lazy factory for the specified name within a
 * specified context.
 * The first time the name is looked up, the factory will be invoked
 * and its value will be stored in the named slot and then returned.
 * Future lookups to the same slot will return the originally created
 * value.
 **/
FOAM.putFactory = function(ctx, name, factory) {
  ctx.__defineGetter__(name, function() {
    console.log('Bouncing Factory: ', name);
    delete ctx[name];
    return ctx[name] = factory();
  });
};

var UNUSED_MODELS = {};
var USED_MODELS   = {};

// Package + Model Definition Support
(function(X) {

  function defineLocalProperty(o, name, factory) {
    var value = factory(o, name);
    Object.defineProperty(o, name, { get: function() {
      return o == this ? value : defineLocalProperty(this, name, factory);
    } });
    return value;
  }

  function defineLazyModel(o, name, model) {

  }

  function defineModel(o, name, model) {

  }

  function packagePath_(root, parent, path, i) {
    console.log('path_ ', i);
    if ( i == path.length ) return parent;

    var head = path[i];
    if ( ! parent[head] ) {
      var map = { __root__: root };

      defineLocalProperty(parent, head, function(o) {
        return o == parent ? map : { __proto__: map, __root__: o.__root__ || o };
      });
    }

    return packagePath_(root, parent[head], path, i+1);
  }

  function packagePath(X, path) {
    return path ? packagePath_(X, X, path.split('.'), 0) : X;
  }

  X.XpackagePath = packagePath;
  X.XregisterModel = function (model, opt_name) {

  };

  /*
  X.MODEL = X.CLASS = function(m) {

  }
  */
})(this);

var X1 = this.sub({}, 'X1');
var X2 = X1.sub({}, 'X2');
var abc = XpackagePath(X1, 'a.b.c');
X2.a;

// Lazy Model Definition - Only creates Model when first referenced
function MODEL(m) {

//  var thisX = this.registerPackagePath(model.package);

  if ( document && document.currentScript ) m.sourcePath = document.currentScript.src;

  var path = this; // packagePath(GLOBAL, m.path);

  UNUSED_MODELS[m.name] = true;
  Object.defineProperty(GLOBAL, m.name, {
    get: function () {
      USED_MODELS[m.name] = true;
      delete UNUSED_MODELS[m.name];
      Object.defineProperty(GLOBAL, m.name, {value: null, configurable: true});
      registerModel(JSONUtil.mapToObj(X, m, Model));
      return this[m.name];
    },
    configurable: true
  });
}

var CLASS = MODEL;

FOAM.browse = function(model, opt_dao, opt_X) {
   var Y = opt_X || X.sub(undefined, "FOAM BROWSER");

   if ( typeof model === 'string' ) model = Y[model];

   var dao = opt_dao || Y[model.name + 'DAO'] || Y[model.plural];

   if ( ! dao ) {
      dao = Y.StorageDAO.create({ model: model });
      Y[model.name + 'DAO'] = dao;
   }

   var ctrl = Y.DAOController.create({
     model:     model,
     dao:       dao,
     useSearchView: false
   });

  if ( ! Y.stack ) {
    var w = opt_X ? opt_X.window : window;
    var win = Y.Window.create({ window: w });

    Y.stack = Y.StackView.create();
    win.view = Y.stack;
    Y.stack.setTopView(ctrl);
  } else {
    Y.stack.pushView(ctrl);
  }
};

FOAM.lookup = function(key, opt_X) {
  if ( ! ( typeof key === 'string' ) ) return key;

  var path = key.split('.');
  var root = opt_X || GLOBAL;
  for ( var i = 0 ; i < path.length ; i++ ) root = root[path[i]];

  return root;
};

function arequire(modelName, opt_X) {
  var X = opt_X || GLOBAL;
  var model = FOAM.lookup(modelName, X);

  /** This is so that if the model is arequire'd concurrently the
   *  initialization isn't done more than once.
   **/
  if ( ! model ) console.log(modelName, 'not found');
  if ( ! model.required__ ) {
    // TODO: eventually this should just call the arequire() method on the Model
    var args = [];
    if ( model.templates ) for ( var i = 0 ; i < model.templates.length ; i++ ) {
      var t = model.templates[i];
      args.push(aseq(
        aevalTemplate(model.templates[i]),
        (function(t) { return function(ret, m) {
          model.getPrototype()[t.name] = m;
          ret();
        };})(t)
      ));
    }

    model.required__ = amemo(aseq(
      apar.apply(apar, args),
      aconstant(model)));
  }

  return model.required__;
}

var FOAM_POWERED = '\
\
FOAM\
 POWERED';
<commit_msg>More package work.<commit_after>/**
 * @license
 * Copyright 2013 Google Inc. All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

var $documents = [];

if ( window ) $documents.push(window.document);

// TODO: clean this up, hide $WID__ in closure
var $WID__ = 0;
function $addWindow(w) {
   w.window.$WID = $WID__++;
   $documents.push(w.document);
}
function $removeWindow(w) {
  for ( var i = $documents.length - 1 ; i >= 0 ; i-- ) {
    if ( ! $documents[i].defaultView || $documents[i].defaultView === w )
      $documents.splice(i,1);
  }
}

/** Replacement for getElementById **/
// TODO(kgr): remove this is deprecated, use X.$ instead()
var $ = function (id) {
  for ( var i = 0 ; i < $documents.length ; i++ ) {
    if ( document.FOAM_OBJECTS && document.FOAM_OBJECTS[id] )
      return document.FOAM_OBJECTS[id];

    var ret = $documents[i].getElementById(id);

    if ( ret ) return ret;
  }
  return undefined;
};
/** Replacement for getElementByClassName **/
// TODO(kgr): remove this is deprecated, use X.$$ instead()
var $$ = function (cls) {
  for ( var i = 0 ; i < $documents.length ; i++ ) {
    var ret = $documents[i].getElementsByClassName(cls);

    if ( ret.length > 0 ) return ret;
  }
  return [];
};

var FOAM = function(map, opt_X) {
   var obj = JSONUtil.mapToObj(opt_X || X, map);
   return obj;
};

/**
 * Register a lazy factory for the specified name within a
 * specified context.
 * The first time the name is looked up, the factory will be invoked
 * and its value will be stored in the named slot and then returned.
 * Future lookups to the same slot will return the originally created
 * value.
 **/
FOAM.putFactory = function(ctx, name, factory) {
  ctx.__defineGetter__(name, function() {
    console.log('Bouncing Factory: ', name);
    delete ctx[name];
    return ctx[name] = factory();
  });
};

var UNUSED_MODELS = {};
var USED_MODELS   = {};

// Package + Model Definition Support
(function(X) {

  function defineLocalProperty(o, name, factory) {
    var value = factory(o, name);
    Object.defineProperty(o, name, { get: function() {
      return o == this ? value : defineLocalProperty(this, name, factory);
    } });
    return value;
  }

  function defineLazyModel(o, name, model) {

  }

  function bindModelToX(model, Y) {
    return Y === GLOBAL ? model : {
      __proto__: model,
      create: function(args, opt_X) {
        return this.__proto__.create(args, opt_X || Y);
      }
    };
  }

  function packagePath_(root, parent, path, i) {
    console.log('path_ ', i);
    if ( i == path.length ) return parent;

    var head = path[i];
    if ( ! parent[head] ) {
      var map = { __root__: root };

      defineLocalProperty(parent, head, function(o) {
        return o == parent ? map : { __proto__: map, __root__: o.__root__ || o };
      });
    }

    return packagePath_(root, parent[head], path, i+1);
  }

  function packagePath(X, path) {
    return path ? packagePath_(X, X, path.split('.'), 0) : X;
  }

  /** opt_name includes path **/
  function registerModel(o, model, opt_name) {
    var name    = model.name;
    var package = model.package;

    if ( opt_name ) {
      var a = opt_name.split('.');
      name = a.pop();
      package = a.join('.');
    }

    defineLocalProperty(
      packagePath(o, package),
      name,
      function(o) { return bindModelToX(model, o.__root__ || o); });
  }

  X.XpackagePath   = packagePath;
  X.XregisterModel = registerModel;

  X.XMODEL = X.XCLASS = function(m) {
    if ( document && document.currentScript ) m.sourcePath = document.currentScript.src;

    var fullName = m.package ? m.package + "." + m.name : m.name;
    UNUSED_MODELS[fullName] = true;

    var path = packagePath(this, model.package);
    Object.defineProperty(path, m.name, {
      get: function () {
        USED_MODELS[fullName] = true;
        delete UNUSED_MODELS[fullName];
        Object.defineProperty(path, m.name, {value: null, configurable: true});
        registerModel(this, JSONUtil.mapToObj(X, m, Model));
        return this[m.name];
      },
      configurable: true
    });
  }
})(this);

var X1 = this.sub({}, 'X1');
var X2 = X1.sub({}, 'X2');
var abc = XpackagePath(X1, 'a.b.c');
X2.a;

// Lazy Model Definition - Only creates Model when first referenced
function MODEL(m) {

//  var thisX = this.registerPackagePath(model.package);

  if ( document && document.currentScript ) m.sourcePath = document.currentScript.src;

  var path = this; // packagePath(GLOBAL, m.path);

  UNUSED_MODELS[m.name] = true;
  Object.defineProperty(GLOBAL, m.name, {
    get: function () {
      USED_MODELS[m.name] = true;
      delete UNUSED_MODELS[m.name];
      Object.defineProperty(GLOBAL, m.name, {value: null, configurable: true});
      registerModel(JSONUtil.mapToObj(X, m, Model));
      return this[m.name];
    },
    configurable: true
  });
}

var CLASS = MODEL;

FOAM.browse = function(model, opt_dao, opt_X) {
   var Y = opt_X || X.sub(undefined, "FOAM BROWSER");

   if ( typeof model === 'string' ) model = Y[model];

   var dao = opt_dao || Y[model.name + 'DAO'] || Y[model.plural];

   if ( ! dao ) {
      dao = Y.StorageDAO.create({ model: model });
      Y[model.name + 'DAO'] = dao;
   }

   var ctrl = Y.DAOController.create({
     model:     model,
     dao:       dao,
     useSearchView: false
   });

  if ( ! Y.stack ) {
    var w = opt_X ? opt_X.window : window;
    var win = Y.Window.create({ window: w });

    Y.stack = Y.StackView.create();
    win.view = Y.stack;
    Y.stack.setTopView(ctrl);
  } else {
    Y.stack.pushView(ctrl);
  }
};

FOAM.lookup = function(key, opt_X) {
  if ( ! ( typeof key === 'string' ) ) return key;

  var path = key.split('.');
  var root = opt_X || GLOBAL;
  for ( var i = 0 ; i < path.length ; i++ ) root = root[path[i]];

  return root;
};

function arequire(modelName, opt_X) {
  var X = opt_X || GLOBAL;
  var model = FOAM.lookup(modelName, X);

  /** This is so that if the model is arequire'd concurrently the
   *  initialization isn't done more than once.
   **/
  if ( ! model ) console.log(modelName, 'not found');
  if ( ! model.required__ ) {
    // TODO: eventually this should just call the arequire() method on the Model
    var args = [];
    if ( model.templates ) for ( var i = 0 ; i < model.templates.length ; i++ ) {
      var t = model.templates[i];
      args.push(aseq(
        aevalTemplate(model.templates[i]),
        (function(t) { return function(ret, m) {
          model.getPrototype()[t.name] = m;
          ret();
        };})(t)
      ));
    }

    model.required__ = amemo(aseq(
      apar.apply(apar, args),
      aconstant(model)));
  }

  return model.required__;
}

var FOAM_POWERED = '\
\
FOAM\
 POWERED';
<|endoftext|>