1 if(!dojo._hasResource["dijit._Templated"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
2 dojo._hasResource["dijit._Templated"] = true;
3 dojo.provide("dijit._Templated");
5 dojo.require("dijit._Widget");
6 dojo.require("dojo.string");
7 dojo.require("dojo.parser");
9 dojo.declare("dijit._Templated",
13 // Mixin for widgets that are instantiated from a template
15 // templateNode: DomNode
16 // a node that represents the widget template. Pre-empts both templateString and templatePath.
19 // templateString: String
20 // a string that represents the widget template. Pre-empts the
21 // templatePath. In builds that have their strings "interned", the
22 // templatePath is converted to an inline templateString, thereby
23 // preventing a synchronous network call.
26 // templatePath: String
27 // Path to template (HTML file) for this widget relative to dojo.baseUrl
30 // widgetsInTemplate: Boolean
31 // should we parse the template to find widgets that might be
32 // declared in markup inside it? false by default.
33 widgetsInTemplate: false,
35 // containerNode: DomNode
36 // holds child elements. "containerNode" is generally set via a
37 // dojoAttachPoint assignment and it designates where children of
38 // the src dom node will be placed
41 // skipNodeCache: Boolean
42 // if using a cached widget template node poses issues for a
43 // particular widget class, it can set this property to ensure
44 // that its template is always re-built from a string
45 _skipNodeCache: false,
47 _stringRepl: function(tmpl){
48 var className = this.declaredClass, _this = this;
49 // Cache contains a string because we need to do property replacement
50 // do the property replacement
51 return dojo.string.substitute(tmpl, this, function(value, key){
52 if(key.charAt(0) == '!'){ value = _this[key.substr(1)]; }
53 if(typeof value == "undefined"){ throw new Error(className+" template:"+key); } // a debugging aide
54 if(!value){ return ""; }
56 // Substitution keys beginning with ! will skip the transform step,
57 // in case a user wishes to insert unescaped markup, e.g. ${!foo}
58 return key.charAt(0) == "!" ? value :
59 // Safer substitution, see heading "Attribute values" in
60 // http://www.w3.org/TR/REC-html40/appendix/notes.html#h-B.3.2
61 value.toString().replace(/"/g,"""); //TODO: add &? use encodeXML method?
66 buildRendering: function(){
68 // Construct the UI for this widget from a template, setting this.domNode.
70 // Lookup cached version of template, and download to cache if it
71 // isn't there already. Returns either a DomNode or a string, depending on
72 // whether or not the template contains ${foo} replacement parameters.
73 var cached = dijit._Templated.getCachedTemplate(this.templatePath, this.templateString, this._skipNodeCache);
76 if(dojo.isString(cached)){
77 node = dijit._Templated._createNodesFromText(this._stringRepl(cached))[0];
79 // if it's a node, all we have to do is clone it
80 node = cached.cloneNode(true);
83 // recurse through the node, looking for, and attaching to, our
84 // attachment points which should be defined on the template node.
85 this._attachTemplateNodes(node);
87 var source = this.srcNodeRef;
88 if(source && source.parentNode){
89 source.parentNode.replaceChild(node, source);
93 if(this.widgetsInTemplate){
94 var cw = this._supportingWidgets = dojo.parser.parse(node);
95 this._attachTemplateNodes(cw, function(n,p){
100 this._fillContent(source);
103 _fillContent: function(/*DomNode*/ source){
105 // relocate source contents to templated container node
106 // this.containerNode must be able to receive children, or exceptions will be thrown
107 var dest = this.containerNode;
109 while(source.hasChildNodes()){
110 dest.appendChild(source.firstChild);
115 _attachTemplateNodes: function(rootNode, getAttrFunc){
116 // summary: Iterate through the template and attach functions and nodes accordingly.
118 // Map widget properties and functions to the handlers specified in
119 // the dom node and it's descendants. This function iterates over all
120 // nodes and looks for these properties:
125 // rootNode: DomNode|Array[Widgets]
126 // the node to search for properties. All children will be searched.
127 // getAttrFunc: function?
128 // a function which will be used to obtain property for a given
131 getAttrFunc = getAttrFunc || function(n,p){ return n.getAttribute(p); };
133 var nodes = dojo.isArray(rootNode) ? rootNode : (rootNode.all || rootNode.getElementsByTagName("*"));
134 var x=dojo.isArray(rootNode)?0:-1;
135 for(; x<nodes.length; x++){
136 var baseNode = (x == -1) ? rootNode : nodes[x];
137 if(this.widgetsInTemplate && getAttrFunc(baseNode,'dojoType')){
140 // Process dojoAttachPoint
141 var attachPoint = getAttrFunc(baseNode, "dojoAttachPoint");
143 var point, points = attachPoint.split(/\s*,\s*/);
144 while((point = points.shift())){
145 if(dojo.isArray(this[point])){
146 this[point].push(baseNode);
148 this[point]=baseNode;
153 // Process dojoAttachEvent
154 var attachEvent = getAttrFunc(baseNode, "dojoAttachEvent");
156 // NOTE: we want to support attributes that have the form
157 // "domEvent: nativeEvent; ..."
158 var event, events = attachEvent.split(/\s*,\s*/);
159 var trim = dojo.trim;
160 while((event = events.shift())){
163 if(event.indexOf(":") != -1){
164 // oh, if only JS had tuple assignment
165 var funcNameArr = event.split(":");
166 event = trim(funcNameArr[0]);
167 thisFunc = trim(funcNameArr[1]);
174 this.connect(baseNode, event, thisFunc);
180 var role = getAttrFunc(baseNode, "waiRole");
182 dijit.setWaiRole(baseNode, role);
184 var values = getAttrFunc(baseNode, "waiState");
186 dojo.forEach(values.split(/\s*,\s*/), function(stateValue){
187 if(stateValue.indexOf('-') != -1){
188 var pair = stateValue.split('-');
189 dijit.setWaiState(baseNode, pair[0], pair[1]);
199 // key is either templatePath or templateString; object is either string or DOM tree
200 dijit._Templated._templateCache = {};
202 dijit._Templated.getCachedTemplate = function(templatePath, templateString, alwaysUseString){
204 // Static method to get a template based on the templatePath or
205 // templateString key
206 // templatePath: String
207 // The URL to get the template from. dojo.uri.Uri is often passed as well.
208 // templateString: String?
209 // a string to use in lieu of fetching the template from a URL. Takes precedence
212 // Either string (if there are ${} variables that need to be replaced) or just
213 // a DOM tree (if the node can be cloned directly)
215 // is it already cached?
216 var tmplts = dijit._Templated._templateCache;
217 var key = templateString || templatePath;
218 var cached = tmplts[key];
223 // If necessary, load template string from template path
225 templateString = dijit._Templated._sanitizeTemplateString(dojo._getText(templatePath));
228 templateString = dojo.string.trim(templateString);
230 if(alwaysUseString || templateString.match(/\$\{([^\}]+)\}/g)){
231 // there are variables in the template so all we can do is cache the string
232 return (tmplts[key] = templateString); //String
234 // there are no variables in the template so we can cache the DOM tree
235 return (tmplts[key] = dijit._Templated._createNodesFromText(templateString)[0]); //Node
239 dijit._Templated._sanitizeTemplateString = function(/*String*/tString){
241 // Strips <?xml ...?> declarations so that external SVG and XML
242 // documents can be added to a document without worry. Also, if the string
243 // is an HTML document, only the part inside the body tag is returned.
245 tString = tString.replace(/^\s*<\?xml(\s)+version=[\'\"](\d)*.(\d)*[\'\"](\s)*\?>/im, "");
246 var matches = tString.match(/<body[^>]*>\s*([\s\S]+)\s*<\/body>/im);
248 tString = matches[1];
253 return tString; //String
258 dojo.addOnUnload(function(){
259 var cache = dijit._Templated._templateCache;
260 for(var key in cache){
261 var value = cache[key];
262 if(!isNaN(value.nodeType)){ // isNode equivalent
263 dojo._destroyElement(value);
272 cell: {re: /^<t[dh][\s\r\n>]/i, pre: "<table><tbody><tr>", post: "</tr></tbody></table>"},
273 row: {re: /^<tr[\s\r\n>]/i, pre: "<table><tbody>", post: "</tbody></table>"},
274 section: {re: /^<(thead|tbody|tfoot)[\s\r\n>]/i, pre: "<table>", post: "</table>"}
277 // dummy container node used temporarily to hold nodes being created
280 dijit._Templated._createNodesFromText = function(/*String*/text){
282 // Attempts to create a set of nodes based on the structure of the passed text.
285 tn = dojo.doc.createElement("div");
286 tn.style.display="none";
287 dojo.body().appendChild(tn);
289 var tableType = "none";
290 var rtext = text.replace(/^\s+/, "");
291 for(var type in tagMap){
292 var map = tagMap[type];
293 if(map.re.test(rtext)){
295 text = map.pre + text + map.post;
305 var tag = { cell: "tr", row: "tbody", section: "table" }[tableType];
306 var _parent = (typeof tag != "undefined") ?
307 tn.getElementsByTagName(tag)[0] :
311 while(_parent.firstChild){
312 nodes.push(_parent.removeChild(_parent.firstChild));
315 return nodes; // Array
319 // These arguments can be specified for widgets which are used in templates.
320 // Since any widget can be specified as sub widgets in template, mix it
321 // into the base widget class. (This is a hack, but it's effective.)
322 dojo.extend(dijit._Widget,{