2150 |
mathias |
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 |
|
|
|
7 |
dojo.require("dojo.string");
|
|
|
8 |
dojo.require("dojo.parser");
|
|
|
9 |
|
|
|
10 |
dojo.declare("dijit._Templated",
|
|
|
11 |
null,
|
|
|
12 |
{
|
|
|
13 |
// summary:
|
|
|
14 |
// mixin for widgets that are instantiated from a template
|
|
|
15 |
|
|
|
16 |
// templateNode: DomNode
|
|
|
17 |
// a node that represents the widget template. Pre-empts both templateString and templatePath.
|
|
|
18 |
templateNode: null,
|
|
|
19 |
|
|
|
20 |
// templateString String:
|
|
|
21 |
// a string that represents the widget template. Pre-empts the
|
|
|
22 |
// templatePath. In builds that have their strings "interned", the
|
|
|
23 |
// templatePath is converted to an inline templateString, thereby
|
|
|
24 |
// preventing a synchronous network call.
|
|
|
25 |
templateString: null,
|
|
|
26 |
|
|
|
27 |
// templatePath: String
|
|
|
28 |
// Path to template (HTML file) for this widget
|
|
|
29 |
templatePath: null,
|
|
|
30 |
|
|
|
31 |
// widgetsInTemplate Boolean:
|
|
|
32 |
// should we parse the template to find widgets that might be
|
|
|
33 |
// declared in markup inside it? false by default.
|
|
|
34 |
widgetsInTemplate: false,
|
|
|
35 |
|
|
|
36 |
// containerNode DomNode:
|
|
|
37 |
// holds child elements. "containerNode" is generally set via a
|
|
|
38 |
// dojoAttachPoint assignment and it designates where children of
|
|
|
39 |
// the src dom node will be placed
|
|
|
40 |
containerNode: null,
|
|
|
41 |
|
|
|
42 |
// skipNodeCache Boolean:
|
|
|
43 |
// if using a cached widget template node poses issues for a
|
|
|
44 |
// particular widget class, it can set this property to ensure
|
|
|
45 |
// that its template is always re-built from a string
|
|
|
46 |
_skipNodeCache: false,
|
|
|
47 |
|
|
|
48 |
// method over-ride
|
|
|
49 |
buildRendering: function(){
|
|
|
50 |
// summary:
|
|
|
51 |
// Construct the UI for this widget from a template, setting this.domNode.
|
|
|
52 |
|
|
|
53 |
// Lookup cached version of template, and download to cache if it
|
|
|
54 |
// isn't there already. Returns either a DomNode or a string, depending on
|
|
|
55 |
// whether or not the template contains ${foo} replacement parameters.
|
|
|
56 |
var cached = dijit._Templated.getCachedTemplate(this.templatePath, this.templateString, this._skipNodeCache);
|
|
|
57 |
|
|
|
58 |
var node;
|
|
|
59 |
if(dojo.isString(cached)){
|
|
|
60 |
var className = this.declaredClass, _this = this;
|
|
|
61 |
// Cache contains a string because we need to do property replacement
|
|
|
62 |
// do the property replacement
|
|
|
63 |
var tstr = dojo.string.substitute(cached, this, function(value, key){
|
|
|
64 |
if(key.charAt(0) == '!'){ value = _this[key.substr(1)]; }
|
|
|
65 |
if(typeof value == "undefined"){ throw new Error(className+" template:"+key); } // a debugging aide
|
|
|
66 |
if(!value){ return ""; }
|
|
|
67 |
|
|
|
68 |
// Substitution keys beginning with ! will skip the transform step,
|
|
|
69 |
// in case a user wishes to insert unescaped markup, e.g. ${!foo}
|
|
|
70 |
return key.charAt(0) == "!" ? value :
|
|
|
71 |
// Safer substitution, see heading "Attribute values" in
|
|
|
72 |
// http://www.w3.org/TR/REC-html40/appendix/notes.html#h-B.3.2
|
|
|
73 |
value.toString().replace(/"/g,"""); //TODO: add &? use encodeXML method?
|
|
|
74 |
}, this);
|
|
|
75 |
|
|
|
76 |
node = dijit._Templated._createNodesFromText(tstr)[0];
|
|
|
77 |
}else{
|
|
|
78 |
// if it's a node, all we have to do is clone it
|
|
|
79 |
node = cached.cloneNode(true);
|
|
|
80 |
}
|
|
|
81 |
|
|
|
82 |
// recurse through the node, looking for, and attaching to, our
|
|
|
83 |
// attachment points which should be defined on the template node.
|
|
|
84 |
this._attachTemplateNodes(node);
|
|
|
85 |
|
|
|
86 |
var source = this.srcNodeRef;
|
|
|
87 |
if(source && source.parentNode){
|
|
|
88 |
source.parentNode.replaceChild(node, source);
|
|
|
89 |
}
|
|
|
90 |
|
|
|
91 |
this.domNode = node;
|
|
|
92 |
if(this.widgetsInTemplate){
|
|
|
93 |
var childWidgets = dojo.parser.parse(node);
|
|
|
94 |
this._attachTemplateNodes(childWidgets, function(n,p){
|
|
|
95 |
return n[p];
|
|
|
96 |
});
|
|
|
97 |
}
|
|
|
98 |
|
|
|
99 |
this._fillContent(source);
|
|
|
100 |
},
|
|
|
101 |
|
|
|
102 |
_fillContent: function(/*DomNode*/ source){
|
|
|
103 |
// summary:
|
|
|
104 |
// relocate source contents to templated container node
|
|
|
105 |
// this.containerNode must be able to receive children, or exceptions will be thrown
|
|
|
106 |
var dest = this.containerNode;
|
|
|
107 |
if(source && dest){
|
|
|
108 |
while(source.hasChildNodes()){
|
|
|
109 |
dest.appendChild(source.firstChild);
|
|
|
110 |
}
|
|
|
111 |
}
|
|
|
112 |
},
|
|
|
113 |
|
|
|
114 |
_attachTemplateNodes: function(rootNode, getAttrFunc){
|
|
|
115 |
// summary:
|
|
|
116 |
// map widget properties and functions to the handlers specified in
|
|
|
117 |
// the dom node and it's descendants. This function iterates over all
|
|
|
118 |
// nodes and looks for these properties:
|
|
|
119 |
// * dojoAttachPoint
|
|
|
120 |
// * dojoAttachEvent
|
|
|
121 |
// * waiRole
|
|
|
122 |
// * waiState
|
|
|
123 |
// rootNode: DomNode|Array[Widgets]
|
|
|
124 |
// the node to search for properties. All children will be searched.
|
|
|
125 |
// getAttrFunc: function?
|
|
|
126 |
// a function which will be used to obtain property for a given
|
|
|
127 |
// DomNode/Widget
|
|
|
128 |
|
|
|
129 |
getAttrFunc = getAttrFunc || function(n,p){ return n.getAttribute(p); };
|
|
|
130 |
|
|
|
131 |
var nodes = dojo.isArray(rootNode) ? rootNode : (rootNode.all || rootNode.getElementsByTagName("*"));
|
|
|
132 |
var x=dojo.isArray(rootNode)?0:-1;
|
|
|
133 |
for(; x<nodes.length; x++){
|
|
|
134 |
var baseNode = (x == -1) ? rootNode : nodes[x];
|
|
|
135 |
if(this.widgetsInTemplate && getAttrFunc(baseNode,'dojoType')){
|
|
|
136 |
continue;
|
|
|
137 |
}
|
|
|
138 |
// Process dojoAttachPoint
|
|
|
139 |
var attachPoint = getAttrFunc(baseNode, "dojoAttachPoint");
|
|
|
140 |
if(attachPoint){
|
|
|
141 |
var point, points = attachPoint.split(/\s*,\s*/);
|
|
|
142 |
while(point=points.shift()){
|
|
|
143 |
if(dojo.isArray(this[point])){
|
|
|
144 |
this[point].push(baseNode);
|
|
|
145 |
}else{
|
|
|
146 |
this[point]=baseNode;
|
|
|
147 |
}
|
|
|
148 |
}
|
|
|
149 |
}
|
|
|
150 |
|
|
|
151 |
// Process dojoAttachEvent
|
|
|
152 |
var attachEvent = getAttrFunc(baseNode, "dojoAttachEvent");
|
|
|
153 |
if(attachEvent){
|
|
|
154 |
// NOTE: we want to support attributes that have the form
|
|
|
155 |
// "domEvent: nativeEvent; ..."
|
|
|
156 |
var event, events = attachEvent.split(/\s*,\s*/);
|
|
|
157 |
var trim = dojo.trim;
|
|
|
158 |
while(event=events.shift()){
|
|
|
159 |
if(event){
|
|
|
160 |
var thisFunc = null;
|
|
|
161 |
if(event.indexOf(":") != -1){
|
|
|
162 |
// oh, if only JS had tuple assignment
|
|
|
163 |
var funcNameArr = event.split(":");
|
|
|
164 |
event = trim(funcNameArr[0]);
|
|
|
165 |
thisFunc = trim(funcNameArr[1]);
|
|
|
166 |
}else{
|
|
|
167 |
event = trim(event);
|
|
|
168 |
}
|
|
|
169 |
if(!thisFunc){
|
|
|
170 |
thisFunc = event;
|
|
|
171 |
}
|
|
|
172 |
this.connect(baseNode, event, thisFunc);
|
|
|
173 |
}
|
|
|
174 |
}
|
|
|
175 |
}
|
|
|
176 |
|
|
|
177 |
// waiRole, waiState
|
|
|
178 |
var role = getAttrFunc(baseNode, "waiRole");
|
|
|
179 |
if(role){
|
|
|
180 |
dijit.setWaiRole(baseNode, role);
|
|
|
181 |
}
|
|
|
182 |
var values = getAttrFunc(baseNode, "waiState");
|
|
|
183 |
if(values){
|
|
|
184 |
dojo.forEach(values.split(/\s*,\s*/), function(stateValue){
|
|
|
185 |
if(stateValue.indexOf('-') != -1){
|
|
|
186 |
var pair = stateValue.split('-');
|
|
|
187 |
dijit.setWaiState(baseNode, pair[0], pair[1]);
|
|
|
188 |
}
|
|
|
189 |
});
|
|
|
190 |
}
|
|
|
191 |
|
|
|
192 |
}
|
|
|
193 |
}
|
|
|
194 |
}
|
|
|
195 |
);
|
|
|
196 |
|
|
|
197 |
// key is either templatePath or templateString; object is either string or DOM tree
|
|
|
198 |
dijit._Templated._templateCache = {};
|
|
|
199 |
|
|
|
200 |
dijit._Templated.getCachedTemplate = function(templatePath, templateString, alwaysUseString){
|
|
|
201 |
// summary:
|
|
|
202 |
// static method to get a template based on the templatePath or
|
|
|
203 |
// templateString key
|
|
|
204 |
// templatePath: String
|
|
|
205 |
// the URL to get the template from. dojo.uri.Uri is often passed as well.
|
|
|
206 |
// templateString: String?
|
|
|
207 |
// a string to use in lieu of fetching the template from a URL
|
|
|
208 |
// Returns:
|
|
|
209 |
// Either string (if there are ${} variables that need to be replaced) or just
|
|
|
210 |
// a DOM tree (if the node can be cloned directly)
|
|
|
211 |
|
|
|
212 |
// is it already cached?
|
|
|
213 |
var tmplts = dijit._Templated._templateCache;
|
|
|
214 |
var key = templateString || templatePath;
|
|
|
215 |
var cached = tmplts[key];
|
|
|
216 |
if(cached){
|
|
|
217 |
return cached;
|
|
|
218 |
}
|
|
|
219 |
|
|
|
220 |
// If necessary, load template string from template path
|
|
|
221 |
if(!templateString){
|
|
|
222 |
templateString = dijit._Templated._sanitizeTemplateString(dojo._getText(templatePath));
|
|
|
223 |
}
|
|
|
224 |
|
|
|
225 |
templateString = dojo.string.trim(templateString);
|
|
|
226 |
|
|
|
227 |
if(templateString.match(/\$\{([^\}]+)\}/g) || alwaysUseString){
|
|
|
228 |
// there are variables in the template so all we can do is cache the string
|
|
|
229 |
return (tmplts[key] = templateString); //String
|
|
|
230 |
}else{
|
|
|
231 |
// there are no variables in the template so we can cache the DOM tree
|
|
|
232 |
return (tmplts[key] = dijit._Templated._createNodesFromText(templateString)[0]); //Node
|
|
|
233 |
}
|
|
|
234 |
};
|
|
|
235 |
|
|
|
236 |
dijit._Templated._sanitizeTemplateString = function(/*String*/tString){
|
|
|
237 |
// summary:
|
|
|
238 |
// Strips <?xml ...?> declarations so that external SVG and XML
|
|
|
239 |
// documents can be added to a document without worry. Also, if the string
|
|
|
240 |
// is an HTML document, only the part inside the body tag is returned.
|
|
|
241 |
if(tString){
|
|
|
242 |
tString = tString.replace(/^\s*<\?xml(\s)+version=[\'\"](\d)*.(\d)*[\'\"](\s)*\?>/im, "");
|
|
|
243 |
var matches = tString.match(/<body[^>]*>\s*([\s\S]+)\s*<\/body>/im);
|
|
|
244 |
if(matches){
|
|
|
245 |
tString = matches[1];
|
|
|
246 |
}
|
|
|
247 |
}else{
|
|
|
248 |
tString = "";
|
|
|
249 |
}
|
|
|
250 |
return tString; //String
|
|
|
251 |
};
|
|
|
252 |
|
|
|
253 |
|
|
|
254 |
if(dojo.isIE){
|
|
|
255 |
dojo.addOnUnload(function(){
|
|
|
256 |
var cache = dijit._Templated._templateCache;
|
|
|
257 |
for(var key in cache){
|
|
|
258 |
var value = cache[key];
|
|
|
259 |
if(!isNaN(value.nodeType)){ // isNode equivalent
|
|
|
260 |
dojo._destroyElement(value);
|
|
|
261 |
}
|
|
|
262 |
delete cache[key];
|
|
|
263 |
}
|
|
|
264 |
});
|
|
|
265 |
}
|
|
|
266 |
|
|
|
267 |
(function(){
|
|
|
268 |
var tagMap = {
|
|
|
269 |
cell: {re: /^<t[dh][\s\r\n>]/i, pre: "<table><tbody><tr>", post: "</tr></tbody></table>"},
|
|
|
270 |
row: {re: /^<tr[\s\r\n>]/i, pre: "<table><tbody>", post: "</tbody></table>"},
|
|
|
271 |
section: {re: /^<(thead|tbody|tfoot)[\s\r\n>]/i, pre: "<table>", post: "</table>"}
|
|
|
272 |
};
|
|
|
273 |
|
|
|
274 |
// dummy container node used temporarily to hold nodes being created
|
|
|
275 |
var tn;
|
|
|
276 |
|
|
|
277 |
dijit._Templated._createNodesFromText = function(/*String*/text){
|
|
|
278 |
// summary:
|
|
|
279 |
// Attempts to create a set of nodes based on the structure of the passed text.
|
|
|
280 |
|
|
|
281 |
if(!tn){
|
|
|
282 |
tn = dojo.doc.createElement("div");
|
|
|
283 |
tn.style.display="none";
|
|
|
284 |
dojo.body().appendChild(tn);
|
|
|
285 |
}
|
|
|
286 |
var tableType = "none";
|
|
|
287 |
var rtext = text.replace(/^\s+/, "");
|
|
|
288 |
for(var type in tagMap){
|
|
|
289 |
var map = tagMap[type];
|
|
|
290 |
if(map.re.test(rtext)){
|
|
|
291 |
tableType = type;
|
|
|
292 |
text = map.pre + text + map.post;
|
|
|
293 |
break;
|
|
|
294 |
}
|
|
|
295 |
}
|
|
|
296 |
|
|
|
297 |
tn.innerHTML = text;
|
|
|
298 |
if(tn.normalize){
|
|
|
299 |
tn.normalize();
|
|
|
300 |
}
|
|
|
301 |
|
|
|
302 |
var tag = { cell: "tr", row: "tbody", section: "table" }[tableType];
|
|
|
303 |
var _parent = (typeof tag != "undefined") ?
|
|
|
304 |
tn.getElementsByTagName(tag)[0] :
|
|
|
305 |
tn;
|
|
|
306 |
|
|
|
307 |
var nodes = [];
|
|
|
308 |
while(_parent.firstChild){
|
|
|
309 |
nodes.push(_parent.removeChild(_parent.firstChild));
|
|
|
310 |
}
|
|
|
311 |
tn.innerHTML="";
|
|
|
312 |
return nodes; // Array
|
|
|
313 |
}
|
|
|
314 |
})();
|
|
|
315 |
|
|
|
316 |
// These arguments can be specified for widgets which are used in templates.
|
|
|
317 |
// Since any widget can be specified as sub widgets in template, mix it
|
|
|
318 |
// into the base widget class. (This is a hack, but it's effective.)
|
|
|
319 |
dojo.extend(dijit._Widget,{
|
|
|
320 |
dojoAttachEvent: "",
|
|
|
321 |
dojoAttachPoint: "",
|
|
|
322 |
waiRole: "",
|
|
|
323 |
waiState:""
|
|
|
324 |
})
|
|
|
325 |
|
|
|
326 |
}
|