]> git.pond.sub.org Git - eow/blob - static/dojo-release-1.1.1/dijit/_Templated.js
add Dojo 1.1.1
[eow] / static / dojo-release-1.1.1 / dijit / _Templated.js
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");
4
5 dojo.require("dijit._Widget");
6 dojo.require("dojo.string");
7 dojo.require("dojo.parser");
8
9 dojo.declare("dijit._Templated",
10         null,
11         {
12                 //      summary:
13                 //              Mixin for widgets that are instantiated from a template
14                 // 
15                 // templateNode: DomNode
16                 //              a node that represents the widget template. Pre-empts both templateString and templatePath.
17                 templateNode: null,
18
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.
24                 templateString: null,
25
26                 // templatePath: String
27                 //      Path to template (HTML file) for this widget relative to dojo.baseUrl
28                 templatePath: null,
29
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,
34
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
39                 containerNode: null,
40
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,
46
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 ""; }
55
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 &amp? use encodeXML method?
62                         }, this);
63                 },
64
65                 // method over-ride
66                 buildRendering: function(){
67                         // summary:
68                         //              Construct the UI for this widget from a template, setting this.domNode.
69
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);
74
75                         var node;
76                         if(dojo.isString(cached)){
77                                 node = dijit._Templated._createNodesFromText(this._stringRepl(cached))[0];
78                         }else{
79                                 // if it's a node, all we have to do is clone it
80                                 node = cached.cloneNode(true);
81                         }
82
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);
86
87                         var source = this.srcNodeRef;
88                         if(source && source.parentNode){
89                                 source.parentNode.replaceChild(node, source);
90                         }
91
92                         this.domNode = node;
93                         if(this.widgetsInTemplate){
94                                 var cw = this._supportingWidgets  = dojo.parser.parse(node);
95                                 this._attachTemplateNodes(cw, function(n,p){
96                                         return n[p];
97                                 });
98                         }
99
100                         this._fillContent(source);
101                 },
102
103                 _fillContent: function(/*DomNode*/ source){
104                         // summary:
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;
108                         if(source && dest){
109                                 while(source.hasChildNodes()){
110                                         dest.appendChild(source.firstChild);
111                                 }
112                         }
113                 },
114
115                 _attachTemplateNodes: function(rootNode, getAttrFunc){
116                         // summary: Iterate through the template and attach functions and nodes accordingly.    
117                         // description:         
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:
121                         //                      * dojoAttachPoint
122                         //                      * dojoAttachEvent       
123                         //                      * waiRole
124                         //                      * waiState
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
129                         //              DomNode/Widget
130
131                         getAttrFunc = getAttrFunc || function(n,p){ return n.getAttribute(p); };
132
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')){
138                                         continue;
139                                 }
140                                 // Process dojoAttachPoint
141                                 var attachPoint = getAttrFunc(baseNode, "dojoAttachPoint");
142                                 if(attachPoint){
143                                         var point, points = attachPoint.split(/\s*,\s*/);
144                                         while((point = points.shift())){
145                                                 if(dojo.isArray(this[point])){
146                                                         this[point].push(baseNode);
147                                                 }else{
148                                                         this[point]=baseNode;
149                                                 }
150                                         }
151                                 }
152
153                                 // Process dojoAttachEvent
154                                 var attachEvent = getAttrFunc(baseNode, "dojoAttachEvent");
155                                 if(attachEvent){
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())){
161                                                 if(event){
162                                                         var thisFunc = null;
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]);
168                                                         }else{
169                                                                 event = trim(event);
170                                                         }
171                                                         if(!thisFunc){
172                                                                 thisFunc = event;
173                                                         }
174                                                         this.connect(baseNode, event, thisFunc);
175                                                 }
176                                         }
177                                 }
178
179                                 // waiRole, waiState
180                                 var role = getAttrFunc(baseNode, "waiRole");
181                                 if(role){
182                                         dijit.setWaiRole(baseNode, role);
183                                 }
184                                 var values = getAttrFunc(baseNode, "waiState");
185                                 if(values){
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]);
190                                                 }
191                                         });
192                                 }
193
194                         }
195                 }
196         }
197 );
198
199 // key is either templatePath or templateString; object is either string or DOM tree
200 dijit._Templated._templateCache = {};
201
202 dijit._Templated.getCachedTemplate = function(templatePath, templateString, alwaysUseString){
203         // summary:
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
210         //              over templatePath
211         // Returns: Mixed
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)
214
215         // is it already cached?
216         var tmplts = dijit._Templated._templateCache;
217         var key = templateString || templatePath;
218         var cached = tmplts[key];
219         if(cached){
220                 return cached;
221         }
222
223         // If necessary, load template string from template path
224         if(!templateString){
225                 templateString = dijit._Templated._sanitizeTemplateString(dojo._getText(templatePath));
226         }
227
228         templateString = dojo.string.trim(templateString);
229
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
233         }else{
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
236         }
237 };
238
239 dijit._Templated._sanitizeTemplateString = function(/*String*/tString){
240         // summary: 
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.
244         if(tString){
245                 tString = tString.replace(/^\s*<\?xml(\s)+version=[\'\"](\d)*.(\d)*[\'\"](\s)*\?>/im, "");
246                 var matches = tString.match(/<body[^>]*>\s*([\s\S]+)\s*<\/body>/im);
247                 if(matches){
248                         tString = matches[1];
249                 }
250         }else{
251                 tString = "";
252         }
253         return tString; //String
254 };
255
256
257 if(dojo.isIE){
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);
264                         }
265                         delete cache[key];
266                 }
267         });
268 }
269
270 (function(){
271         var tagMap = {
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>"}
275         };
276
277         // dummy container node used temporarily to hold nodes being created
278         var tn;
279
280         dijit._Templated._createNodesFromText = function(/*String*/text){
281                 // summary:
282                 //      Attempts to create a set of nodes based on the structure of the passed text.
283
284                 if(!tn){
285                         tn = dojo.doc.createElement("div");
286                         tn.style.display="none";
287                         dojo.body().appendChild(tn);
288                 }
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)){
294                                 tableType = type;
295                                 text = map.pre + text + map.post;
296                                 break;
297                         }
298                 }
299
300                 tn.innerHTML = text;
301                 if(tn.normalize){
302                         tn.normalize();
303                 }
304
305                 var tag = { cell: "tr", row: "tbody", section: "table" }[tableType];
306                 var _parent = (typeof tag != "undefined") ?
307                                                 tn.getElementsByTagName(tag)[0] :
308                                                 tn;
309
310                 var nodes = [];
311                 while(_parent.firstChild){
312                         nodes.push(_parent.removeChild(_parent.firstChild));
313                 }
314                 tn.innerHTML="";
315                 return nodes;   //      Array
316         }
317 })();
318
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,{
323         dojoAttachEvent: "",
324         dojoAttachPoint: "",
325         waiRole: "",
326         waiState:""
327 })
328
329 }