Subversion Repositories Applications.papyrus

Rev

Blame | Last modification | View Log | RSS feed

if(!dojo._hasResource["dojox.dtl.html"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
dojo._hasResource["dojox.dtl.html"] = true;
dojo.provide("dojox.dtl.html");

dojo.require("dojox.dtl._base");

dojox.dtl.ObjectMap = function(){
        this.contents = [];
}
dojo.extend(dojox.dtl.ObjectMap, {
        get: function(key){
                var contents = this.contents;
                for(var i = 0, content; content = contents[i]; i++){
                        if(content[0] === key){
                                return content[1];
                        }
                }
        },
        put: function(key, value){
                var contents = this.contents;
                for(var i = 0, content; content = contents[i]; i++){
                        if(content[0] === key){
                                if(arguments.length == 1){
                                        contents.splice(i, 1);
                                        return;
                                }
                                content[1] = value;
                                return;
                        }
                }
                contents.push([key, value]);
        },
        toString: function(){ return "dojox.dtl.ObjectMap"; }
});

dojox.dtl.html = {
        types: dojo.mixin({change: -11, attr: -12, elem: 1, text: 3}, dojox.dtl.text.types),
        _attributes: {},
        _re: /(^\s+|\s+$)/g,
        _re2: /\b([a-zA-Z]+)="/g,
        _re3: /<!--({({|%).*?(%|})})-->/g,
        _re4: /^function anonymous\(\)\s*{\s*(.*)\s*}$/,
        _trim: function(/*String*/ str){
                return str.replace(this._re, "");
        },
        getTemplate: function(text){
                if(typeof this._commentable == "undefined"){
                        // Check to see if the browser can handle comments
                        this._commentable = false;
                        var div = document.createElement("div");
                        div.innerHTML = "<!--Test comment handling, and long comments, using comments whenever possible.-->";
                        if(div.childNodes.length && div.childNodes[0].nodeType == 8 && div.childNodes[0].data == "comment"){
                                this._commentable = true;
                        }
                }

                if(!this._commentable){
                        // Strip comments
                        text = text.replace(this._re3, "$1");
                }

                var match;
                while(match = this._re2.exec(text)){
                        this._attributes[match[1]] = true;
                }
                var div = document.createElement("div");
                div.innerHTML = text;
                var output = { pres: [], posts: []}
                while(div.childNodes.length){
                        if(!output.node && div.childNodes[0].nodeType == 1){
                                output.node = div.removeChild(div.childNodes[0]);
                        }else if(!output.node){
                                output.pres.push(div.removeChild(div.childNodes[0]));
                        }else{
                                output.posts.push(div.removeChild(div.childNodes[0]));
                        }
                }

                if(!output.node){
                        throw new Error("Template did not provide any content");
                }

                return output;
        },
        tokenize: function(/*Node*/ node, /*Array?*/ tokens, /*Array?*/ preNodes, /*Array?*/ postNodes){
                tokens = tokens || [];
                var first = !tokens.length;
                var types = this.types;

                var children = [];
                for(var i = 0, child; child = node.childNodes[i]; i++){
                        children.push(child);
                }

                if(preNodes){
                        for(var i = 0, child; child = preNodes[i]; i++){
                                this._tokenize(node, child, tokens);
                        }
                }

                tokens.push([types.elem, node]);
                tokens.push([types.change, node]);

                for(var key in this._attributes){
                        var value = "";
                        if(key == "class"){
                                value = node.className || value;
                        }else if(key == "for"){
                                value = node.htmlFor || value;
                        }else if(node.getAttribute){
                                value = node.getAttribute(key, 2) || value;
                                if(key == "href" || key == "src"){
                                        if(dojo.isIE){
                                                var hash = location.href.lastIndexOf(location.hash);
                                                var href = location.href.substring(0, hash).split("/");
                                                href.pop();
                                                href = href.join("/") + "/";
                                                if(value.indexOf(href) == 0){
                                                        value = value.replace(href, "");
                                                }
                                                value = value.replace(/%20/g, " ").replace(/%7B/g, "{").replace(/%7D/g, "}").replace(/%25/g, "%");
                                        }
                                        if(value.indexOf("{%") != -1 || value.indexOf("{{") != -1){
                                                node.setAttribute(key, "");
                                        }
                                }
                        }
                        if(typeof value == "function"){
                                value = value.toString().replace(this._re4, "$1");
                        }
                        if(typeof value == "string" && (value.indexOf("{%") != -1 || value.indexOf("{{") != -1 || (value && dojox.dtl.text.getTag("attr:" + key, true)))){
                                tokens.push([types.attr, node, key, value]);
                        }
                }

                if(!children.length){
                        tokens.push([types.change, node.parentNode, true]);
                        if(postNodes){
                                for(var i = 0, child; child = postNodes[i]; i++){
                                        this._tokenize(node, child, tokens);
                                }
                        }
                        return tokens;
                }

                for(var i = 0, child; child = children[i]; i++){
                        this._tokenize(node, child, tokens);
                }

                if(node.parentNode && node.parentNode.tagName){
                        tokens.push([types.change, node.parentNode, true]);
                        node.parentNode.removeChild(node);
                }
                
                if(postNodes){
                        for(var i = 0, child; child = postNodes[i]; i++){
                                this._tokenize(node, child, tokens);
                        }
                }

                if(first){
                        tokens.push([types.change, node, true]);
                }

                return tokens;
        },
        _tokenize: function(parent, child, tokens){
                var types = this.types;
                var data = child.data;
                switch(child.nodeType){
                        case 1:
                                this.tokenize(child, tokens);
                                break;
                        case 3:
                                if(data.match(/[^\s\n]/)){
                                        if(data.indexOf("{{") != -1 || data.indexOf("{%") != -1){
                                                var texts = dojox.dtl.text.tokenize(data);
                                                for(var j = 0, text; text = texts[j]; j++){
                                                        if(typeof text == "string"){
                                                                tokens.push([types.text, text]);
                                                        }else{
                                                                tokens.push(text);
                                                        }
                                                }
                                        }else{
                                                tokens.push([child.nodeType, child]);
                                        }
                                }
                                if(child.parentNode) child.parentNode.removeChild(child);
                                break;
                        case 8:
                                if(data.indexOf("{%") == 0){
                                        tokens.push([types.tag, this._trim(data.substring(2, data.length - 3))]);
                                }
                                if(data.indexOf("{{") == 0){
                                        tokens.push([types.varr, this._trim(data.substring(2, data.length - 3))]);
                                }
                                if(child.parentNode) child.parentNode.removeChild(child);
                                break;
                }
        }
}

dojox.dtl.HtmlTemplate = function(/*String|dojo._Url*/ obj){
        // summary: Use this object for HTML templating
        var dd = dojox.dtl;
        var ddh = dd.html;

        if(!obj.node){
                if(typeof obj == "object"){
                        obj = dojox.dtl.text.getTemplateString(obj);
                }
                obj = ddh.getTemplate(obj);
        }

        var tokens = ddh.tokenize(obj.node, [], obj.pres, obj.posts);
        var parser = new dd.HtmlParser(tokens);
        this.nodelist = parser.parse();
}
dojo.extend(dojox.dtl.HtmlTemplate, {
        _count: 0,
        _re: /\bdojo:([a-zA-Z0-9_]+)\b/g,
        setClass: function(str){
                this.getRootNode().className = str;
        },
        getRootNode: function(){
                return this.rootNode;
        },
        getBuffer: function(){
                return new dojox.dtl.HtmlBuffer();
        },
        render: function(context, buffer){
                buffer = buffer || this.getBuffer();
                this.rootNode = null;
                var onSetParent = dojo.connect(buffer, "onSetParent", this, function(node){
                        if(!this.rootNode){
                                this.rootNode = node || true;
                        }
                });
                var output = this.nodelist.render(context || new dojox.dtl.Context({}), buffer);
                dojo.disconnect(onSetParent);
                buffer._flushCache();
                return output;
        },
        unrender: function(context, buffer){
                return this.nodelist.unrender(context, buffer);
        },
        toString: function(){ return "dojox.dtl.HtmlTemplate"; }
});

dojox.dtl.HtmlBuffer = function(/*Node*/ parent){
        // summary: Allows the manipulation of DOM
        // description:
        //              Use this to append a child, change the parent, or
        //              change the attribute of the current node.
        this._parent = parent;
        this._cache = [];
}
dojo.extend(dojox.dtl.HtmlBuffer, {
        concat: function(/*DOMNode*/ node){
                if(!this._parent) return this;
                if(node.nodeType){
                        var caches = this._getCache(this._parent);
                        if(node.parentNode === this._parent){
                                // If we reach a node that already existed, fill in the cache for this same parent
                                var i = 0;
                                for(var i = 0, cache; cache = caches[i]; i++){
                                        this.onAddNode(node);
                                        this._parent.insertBefore(cache, node);
                                }
                                caches.length = 0;
                        }
                        if(!node.parentNode || !node.parentNode.tagName){
                                if(!this._parent.childNodes.length){
                                        this.onAddNode(node);
                                        this._parent.appendChild(node);
                                }else{
                                        caches.push(node);
                                }
                        }
                }
                return this;
        },
        remove: function(obj){
                if(typeof obj == "string"){
                        this._parent.removeAttribute(obj);
                }else{
                        if(obj.parentNode === this._parent){
                                this.onRemoveNode();
                                this._parent.removeChild(obj);
                        }
                }
                return this;
        },
        setAttribute: function(key, value){
                if(key == "class"){
                        this._parent.className = value;
                }else if(key == "for"){
                        this._parent.htmlFor = value;
                }else if(this._parent.setAttribute){
                        this._parent.setAttribute(key, value);
                }
                return this;
        },
        setParent: function(node, /*Boolean?*/ up){
                if(!this._parent) this._parent = node;
                var caches = this._getCache(this._parent);
                if(caches && caches.length && up){
                        for(var i = 0, cache; cache = caches[i]; i++){
                                if(cache !== this._parent && (!cache.parentNode || !cache.parentNode.tagName)){
                                        this.onAddNode(cache);
                                        this._parent.appendChild(cache);
                                }
                        }
                        caches.length = 0;
                }

                this.onSetParent(node, up);
                this._parent = node;
                return this;
        },
        getParent: function(){
                return this._parent;
        },
        onSetParent: function(){
                // summary: Stub called when setParent is used.
        },
        onAddNode: function(){
                // summary: Stub called when new nodes are added
        },
        onRemoveNode: function(){
                // summary: Stub called when nodes are removed
        },
        _getCache: function(node){
                for(var i = 0, cache; cache = this._cache[i]; i++){
                        if(cache[0] === node){
                                return cache[1];
                        }
                }
                var arr = [];
                this._cache.push([node, arr]);
                return arr;
        },
        _flushCache: function(node){
                for(var i = 0, cache; cache = this._cache[i]; i++){
                        if(!cache[1].length){
                                this._cache.splice(i--, 1);
                        }
                }
        },
        toString: function(){ return "dojox.dtl.HtmlBuffer"; }
});

dojox.dtl.HtmlNode = function(node){
        // summary: Places a node into DOM
        this.contents = node;
}
dojo.extend(dojox.dtl.HtmlNode, {
        render: function(context, buffer){
                return buffer.concat(this.contents);
        },
        unrender: function(context, buffer){
                return buffer.remove(this.contents);
        },
        clone: function(buffer){
                return new dojox.dtl.HtmlNode(this.contents);
        },
        toString: function(){ return "dojox.dtl.HtmlNode"; }
});

dojox.dtl.HtmlNodeList = function(/*Node[]*/ nodes){
        // summary: A list of any HTML-specific node object
        // description:
        //              Any object that's used in the constructor or added
        //              through the push function much implement the
        //              render, unrender, and clone functions.
        this.contents = nodes || [];
}
dojo.extend(dojox.dtl.HtmlNodeList, {
        parents: new dojox.dtl.ObjectMap(),
        push: function(node){
                this.contents.push(node);
        },
        unshift: function(node){
                this.contents.unshift(node);
        },
        render: function(context, buffer, /*Node*/ instance){
                if(instance){
                        var parent = buffer.getParent();
                }
                for(var i = 0; i < this.contents.length; i++){
                        buffer = this.contents[i].render(context, buffer);
                        if(!buffer) throw new Error("Template node render functions must return their buffer");
                }
                if(parent){
                        buffer.setParent(parent, true);
                }
                return buffer;
        },
        unrender: function(context, buffer){
                for(var i = 0; i < this.contents.length; i++){
                        buffer = this.contents[i].unrender(context, buffer);
                        if(!buffer) throw new Error("Template node render functions must return their buffer");
                }
                return buffer;
        },
        clone: function(buffer){
                // summary:
                //              Used to create an identical copy of a NodeList, useful for things like the for tag.
                var dd = dojox.dtl;
                var ddh = dd.html;
                var parent = buffer.getParent();
                var contents = this.contents;
                var nodelist = new dd.HtmlNodeList();
                var cloned = [];
                for(var i = 0; i < contents.length; i++){
                        var clone = contents[i].clone(buffer);
                        if(clone instanceof dd.ChangeNode || clone instanceof dd.HtmlNode){
                                var item = this.parents.get(clone.contents);
                                if(item){
                                        clone.contents = item;
                                }else if(parent !== clone.contents && clone instanceof dd.HtmlNode){
                                        var node = clone.contents;
                                        clone.contents = clone.contents.cloneNode(false);
                                        cloned.push(node);
                                        this.parents.put(node, clone.contents);
                                }
                        }
                        nodelist.push(clone);
                }

                for(var i = 0, clone; clone = cloned[i]; i++){
                        this.parents.put(clone);
                }

                return nodelist;
        },
        toString: function(){ return "dojox.dtl.HtmlNodeList"; }
});

dojox.dtl.HtmlVarNode = function(str){
        // summary: A node to be processed as a variable
        // description:
        //              Will render an object that supports the render function
        //              and the getRootNode function
        this.contents = new dojox.dtl.Filter(str);
        this._lists = {};
}
dojo.extend(dojox.dtl.HtmlVarNode, {
        render: function(context, buffer){
                this._rendered = true;
                var dd = dojox.dtl;
                var ddh = dd.html;
                var str = this.contents.resolve(context);
                if(str && str.render && str.getRootNode){
                        var root = this._curr = str.getRootNode();
                        var lists = this._lists;
                        var list = lists[root];
                        if(!list){
                                list = lists[root] = new dd.HtmlNodeList();
                                list.push(new dd.ChangeNode(buffer.getParent()));
                                list.push(new dd.HtmlNode(root));
                                list.push(str);
                                list.push(new dd.ChangeNode(buffer.getParent(), true));
                        }
                        return list.render(context, buffer);
                }else{
                        if(!this._txt) this._txt = document.createTextNode(str);
                        if(this._txt.data != str) this._txt.data = str;
                        return buffer.concat(this._txt);
                }
                return buffer;
        },
        unrender: function(context, buffer){
                if(this._rendered){
                        this._rendered = false;
                        if(this._curr){
                                return this._lists[this._curr].unrender(context, buffer);
                        }else if(this._txt){
                                return buffer.remove(this._txt);
                        }
                }
                return buffer;
        },
        clone: function(){
                return new dojox.dtl.HtmlVarNode(this.contents.contents);
        },
        toString: function(){ return "dojox.dtl.HtmlVarNode"; }
});

dojox.dtl.ChangeNode = function(node, /*Boolean?*/ up){
        // summary: Changes the parent during render/unrender
        this.contents = node;
        this._up = up;
}
dojo.extend(dojox.dtl.ChangeNode, {
        render: function(context, buffer){
                return buffer.setParent(this.contents, this._up);
        },
        unrender: function(context, buffer){
                return buffer.setParent(this.contents);
        },
        clone: function(buffer){
                return new dojox.dtl.ChangeNode(this.contents, this._up);
        },
        toString: function(){ return "dojox.dtl.ChangeNode"; }
});

dojox.dtl.AttributeNode = function(key, value){
        // summary: Works on attributes
        this._key = key;
        this._value = value;
        this._tpl = new dojox.dtl.Template(value);
        this.contents = "";
}
dojo.extend(dojox.dtl.AttributeNode, {
        render: function(context, buffer){
                var key = this._key;
                var value = this._tpl.render(context);
                if(this._rendered){
                        if(value != this.contents){
                                this.contents = value;
                                return buffer.setAttribute(key, value);
                        }
                }else{
                        this._rendered = true;
                        this.contents = value;
                        return buffer.setAttribute(key, value);
                }
                return buffer;
        },
        unrender: function(context, buffer){
                if(this._rendered){
                        this._rendered = false;
                        this.contents = "";
                        return buffer.remove(this.contents);
                }
                return buffer;
        },
        clone: function(){
                return new dojox.dtl.AttributeNode(this._key, this._value);
        },
        toString: function(){ return "dojox.dtl.AttributeNode"; }
});

dojox.dtl.HtmlTextNode = function(str){
        // summary: Adds a straight text node without any processing
        this.contents = document.createTextNode(str);
}
dojo.extend(dojox.dtl.HtmlTextNode, {
        render: function(context, buffer){
                return buffer.concat(this.contents);
        },
        unrender: function(context, buffer){
                return buffer.remove(this.contents);
        },
        clone: function(){
                return new dojox.dtl.HtmlTextNode(this.contents.data);
        },
        toString: function(){ return "dojox.dtl.HtmlTextNode"; }
});

dojox.dtl.HtmlParser = function(tokens){
        // summary: Turn a simple array into a set of objects
        // description:
        //      This is also used by all tags to move through
        //      the list of nodes.
        this.contents = tokens;
}
dojo.extend(dojox.dtl.HtmlParser, {
        parse: function(/*Array?*/ stop_at){
                var dd = dojox.dtl;
                var ddh = dd.html;
                var types = ddh.types;
                var terminators = {};
                var tokens = this.contents;
                if(!stop_at){
                        stop_at = [];
                }
                for(var i = 0; i < stop_at.length; i++){
                        terminators[stop_at[i]] = true;
                }
                var nodelist = new dd.HtmlNodeList();
                while(tokens.length){
                        var token = tokens.shift();
                        var type = token[0];
                        var value = token[1];
                        if(type == types.change){
                                nodelist.push(new dd.ChangeNode(value, token[2]));
                        }else if(type == types.attr){
                                var fn = dojox.dtl.text.getTag("attr:" + token[2], true);
                                if(fn){
                                        nodelist.push(fn(null, token[2] + " " + token[3]));
                                }else{
                                        nodelist.push(new dd.AttributeNode(token[2], token[3]));
                                }
                        }else if(type == types.elem){
                                var fn = dojox.dtl.text.getTag("node:" + value.tagName.toLowerCase(), true);
                                if(fn){
                                        // TODO: We need to move this to tokenization so that it's before the
                                        //                              node and the parser can be passed here instead of null
                                        nodelist.push(fn(null, value, value.tagName.toLowerCase()));
                                }
                                nodelist.push(new dd.HtmlNode(value));
                        }else if(type == types.varr){
                                nodelist.push(new dd.HtmlVarNode(value));
                        }else if(type == types.text){
                                nodelist.push(new dd.HtmlTextNode(value.data || value));
                        }else if(type == types.tag){
                                if(terminators[value]){
                                        tokens.unshift(token);
                                        return nodelist;
                                }
                                var cmd = value.split(/\s+/g);
                                if(cmd.length){
                                        cmd = cmd[0];
                                        var fn = dojox.dtl.text.getTag(cmd);
                                        if(typeof fn != "function"){
                                                throw new Error("Function not found for ", cmd);
                                        }
                                        var tpl = fn(this, value);
                                        if(tpl){
                                                nodelist.push(tpl);
                                        }
                                }
                        }
                }

                if(stop_at.length){
                        throw new Error("Could not find closing tag(s): " + stop_at.toString());
                }

                return nodelist;
        },
        next: function(){
                // summary: Used by tags to discover what token was found
                var token = this.contents.shift();
                return {type: token[0], text: token[1]};
        },
        skipPast: function(endtag){
                return dojox.dtl.Parser.prototype.skipPast.call(this, endtag);
        },
        getVarNode: function(){
                return dojox.dtl.HtmlVarNode;
        },
        getTextNode: function(){
                return dojox.dtl.HtmlTextNode;
        },
        getTemplate: function(/*String*/ loc){
                return new dojox.dtl.HtmlTemplate(dojox.dtl.html.getTemplate(loc));
        },
        toString: function(){ return "dojox.dtl.HtmlParser"; }
});

dojox.dtl.register.tag("dojox.dtl.tag.event", "dojox.dtl.tag.event", [[/(attr:)?on(click|key(up))/i, "on"]]);
dojox.dtl.register.tag("dojox.dtl.tag.html", "dojox.dtl.tag.html", ["html", "attr:attach", "attr:tstyle"]);

}