source: Dev/trunk/src/client/dijit/_TemplatedMixin.js @ 524

Last change on this file since 524 was 483, checked in by hendrikvanantwerpen, 11 years ago

Added Dojo 1.9.3 release.

File size: 6.5 KB
Line 
1define([
2        "dojo/cache",   // dojo.cache
3        "dojo/_base/declare", // declare
4        "dojo/dom-construct", // domConstruct.destroy, domConstruct.toDom
5        "dojo/_base/lang", // lang.getObject
6        "dojo/on",
7        "dojo/sniff", // has("ie")
8        "dojo/string", // string.substitute string.trim
9        "./_AttachMixin"
10], function(cache, declare, domConstruct, lang, on, has, string, _AttachMixin){
11
12        // module:
13        //              dijit/_TemplatedMixin
14
15        var _TemplatedMixin = declare("dijit._TemplatedMixin", _AttachMixin, {
16                // summary:
17                //              Mixin for widgets that are instantiated from a template
18
19                // templateString: [protected] String
20                //              A string that represents the widget template.
21                //              Use in conjunction with dojo.cache() to load from a file.
22                templateString: null,
23
24                // templatePath: [protected deprecated] String
25                //              Path to template (HTML file) for this widget relative to dojo.baseUrl.
26                //              Deprecated: use templateString with require([... "dojo/text!..."], ...) instead
27                templatePath: null,
28
29                // skipNodeCache: [protected] Boolean
30                //              If using a cached widget template nodes poses issues for a
31                //              particular widget class, it can set this property to ensure
32                //              that its template is always re-built from a string
33                _skipNodeCache: false,
34
35/*=====
36                // _rendered: Boolean
37                //              Not normally use, but this flag can be set by the app if the server has already rendered the template,
38                //              i.e. already inlining the template for the widget into the main page.   Reduces _TemplatedMixin to
39                //              just function like _AttachMixin.
40                _rendered: false,
41=====*/
42
43                // Set _AttachMixin.searchContainerNode to true for back-compat for widgets that have data-dojo-attach-point's
44                // and events inside this.containerNode.   Remove for 2.0.
45                searchContainerNode: true,
46
47                _stringRepl: function(tmpl){
48                        // summary:
49                        //              Does substitution of ${foo} type properties in template string
50                        // tags:
51                        //              private
52                        var className = this.declaredClass, _this = this;
53                        // Cache contains a string because we need to do property replacement
54                        // do the property replacement
55                        return string.substitute(tmpl, this, function(value, key){
56                                if(key.charAt(0) == '!'){ value = lang.getObject(key.substr(1), false, _this); }
57                                if(typeof value == "undefined"){ throw new Error(className+" template:"+key); } // a debugging aide
58                                if(value == null){ return ""; }
59
60                                // Substitution keys beginning with ! will skip the transform step,
61                                // in case a user wishes to insert unescaped markup, e.g. ${!foo}
62                                return key.charAt(0) == "!" ? value :
63                                        // Safer substitution, see heading "Attribute values" in
64                                        // http://www.w3.org/TR/REC-html40/appendix/notes.html#h-B.3.2
65                                        value.toString().replace(/"/g,"""); //TODO: add &amp? use encodeXML method?
66                        }, this);
67                },
68
69                buildRendering: function(){
70                        // summary:
71                        //              Construct the UI for this widget from a template, setting this.domNode.
72                        // tags:
73                        //              protected
74
75                        if(!this._rendered){
76                                if(!this.templateString){
77                                        this.templateString = cache(this.templatePath, {sanitize: true});
78                                }
79
80                                // Lookup cached version of template, and download to cache if it
81                                // isn't there already.  Returns either a DomNode or a string, depending on
82                                // whether or not the template contains ${foo} replacement parameters.
83                                var cached = _TemplatedMixin.getCachedTemplate(this.templateString, this._skipNodeCache, this.ownerDocument);
84
85                                var node;
86                                if(lang.isString(cached)){
87                                        node = domConstruct.toDom(this._stringRepl(cached), this.ownerDocument);
88                                        if(node.nodeType != 1){
89                                                // Flag common problems such as templates with multiple top level nodes (nodeType == 11)
90                                                throw new Error("Invalid template: " + cached);
91                                        }
92                                }else{
93                                        // if it's a node, all we have to do is clone it
94                                        node = cached.cloneNode(true);
95                                }
96
97                                this.domNode = node;
98                        }
99
100                        // Call down to _WidgetBase.buildRendering() to get base classes assigned
101                        // TODO: change the baseClass assignment to _setBaseClassAttr
102                        this.inherited(arguments);
103
104                        if(!this._rendered){
105                                this._fillContent(this.srcNodeRef);
106                        }
107
108                        this._rendered = true;
109                },
110
111                _fillContent: function(/*DomNode*/ source){
112                        // summary:
113                        //              Relocate source contents to templated container node.
114                        //              this.containerNode must be able to receive children, or exceptions will be thrown.
115                        // tags:
116                        //              protected
117                        var dest = this.containerNode;
118                        if(source && dest){
119                                while(source.hasChildNodes()){
120                                        dest.appendChild(source.firstChild);
121                                }
122                        }
123                }
124
125        });
126
127        // key is templateString; object is either string or DOM tree
128        _TemplatedMixin._templateCache = {};
129
130        _TemplatedMixin.getCachedTemplate = function(templateString, alwaysUseString, doc){
131                // summary:
132                //              Static method to get a template based on the templatePath or
133                //              templateString key
134                // templateString: String
135                //              The template
136                // alwaysUseString: Boolean
137                //              Don't cache the DOM tree for this template, even if it doesn't have any variables
138                // doc: Document?
139                //              The target document.   Defaults to document global if unspecified.
140                // returns: Mixed
141                //              Either string (if there are ${} variables that need to be replaced) or just
142                //              a DOM tree (if the node can be cloned directly)
143
144                // is it already cached?
145                var tmplts = _TemplatedMixin._templateCache;
146                var key = templateString;
147                var cached = tmplts[key];
148                if(cached){
149                        try{
150                                // if the cached value is an innerHTML string (no ownerDocument) or a DOM tree created within the
151                                // current document, then use the current cached value
152                                if(!cached.ownerDocument || cached.ownerDocument == (doc || document)){
153                                        // string or node of the same document
154                                        return cached;
155                                }
156                        }catch(e){ /* squelch */ } // IE can throw an exception if cached.ownerDocument was reloaded
157                        domConstruct.destroy(cached);
158                }
159
160                templateString = string.trim(templateString);
161
162                if(alwaysUseString || templateString.match(/\$\{([^\}]+)\}/g)){
163                        // there are variables in the template so all we can do is cache the string
164                        return (tmplts[key] = templateString); //String
165                }else{
166                        // there are no variables in the template so we can cache the DOM tree
167                        var node = domConstruct.toDom(templateString, doc);
168                        if(node.nodeType != 1){
169                                throw new Error("Invalid template: " + templateString);
170                        }
171                        return (tmplts[key] = node); //Node
172                }
173        };
174
175        if(has("ie")){
176                on(window, "unload", function(){
177                        var cache = _TemplatedMixin._templateCache;
178                        for(var key in cache){
179                                var value = cache[key];
180                                if(typeof value == "object"){ // value is either a string or a DOM node template
181                                        domConstruct.destroy(value);
182                                }
183                                delete cache[key];
184                        }
185                });
186        }
187
188        return _TemplatedMixin;
189});
Note: See TracBrowser for help on using the repository browser.