1 | define( |
---|
2 | ["./_base/kernel", "./_base/lang", "./_base/array", "./_base/html", "./_base/window", "./_base/url", |
---|
3 | "./_base/json", "./aspect", "./date/stamp", "./query", "./on", "./ready"], |
---|
4 | function(dojo, dlang, darray, dhtml, dwindow, _Url, djson, aspect, dates, query, don){ |
---|
5 | |
---|
6 | // module: |
---|
7 | // dojo/parser |
---|
8 | // summary: |
---|
9 | // The Dom/Widget parsing package |
---|
10 | |
---|
11 | new Date("X"); // workaround for #11279, new Date("") == NaN |
---|
12 | |
---|
13 | var features = { |
---|
14 | // Feature detection for when node.attributes only lists the attributes specified in the markup |
---|
15 | // rather than old IE/quirks behavior where it lists every default value too |
---|
16 | "dom-attributes-explicit": document.createElement("div").attributes.length < 40 |
---|
17 | }; |
---|
18 | function has(feature){ |
---|
19 | return features[feature]; |
---|
20 | } |
---|
21 | |
---|
22 | |
---|
23 | dojo.parser = new function(){ |
---|
24 | // summary: |
---|
25 | // The Dom/Widget parsing package |
---|
26 | |
---|
27 | var _nameMap = { |
---|
28 | // Map from widget name (ex: "dijit.form.Button") to structure mapping |
---|
29 | // lowercase version of attribute names to the version in the widget ex: |
---|
30 | // { |
---|
31 | // label: "label", |
---|
32 | // onclick: "onClick" |
---|
33 | // } |
---|
34 | }; |
---|
35 | function getNameMap(proto){ |
---|
36 | // summary: |
---|
37 | // Returns map from lowercase name to attribute name in class, ex: {onclick: "onClick"} |
---|
38 | var map = {}; |
---|
39 | for(var name in proto){ |
---|
40 | if(name.charAt(0)=="_"){ continue; } // skip internal properties |
---|
41 | map[name.toLowerCase()] = name; |
---|
42 | } |
---|
43 | return map; |
---|
44 | } |
---|
45 | // Widgets like BorderContainer add properties to _Widget via dojo.extend(). |
---|
46 | // If BorderContainer is loaded after _Widget's parameter list has been cached, |
---|
47 | // we need to refresh that parameter list (for _Widget and all widgets that extend _Widget). |
---|
48 | aspect.after(dlang, "extend", function(){ |
---|
49 | _nameMap = {}; |
---|
50 | }, true); |
---|
51 | |
---|
52 | // Map from widget name (ex: "dijit.form.Button") to constructor |
---|
53 | var _ctorMap = {}; |
---|
54 | |
---|
55 | this._functionFromScript = function(script, attrData){ |
---|
56 | // summary: |
---|
57 | // Convert a <script type="dojo/method" args="a, b, c"> ... </script> |
---|
58 | // into a function |
---|
59 | // script: DOMNode |
---|
60 | // The <script> DOMNode |
---|
61 | // attrData: String |
---|
62 | // For HTML5 compliance, searches for attrData + "args" (typically |
---|
63 | // "data-dojo-args") instead of "args" |
---|
64 | var preamble = ""; |
---|
65 | var suffix = ""; |
---|
66 | var argsStr = (script.getAttribute(attrData + "args") || script.getAttribute("args")); |
---|
67 | if(argsStr){ |
---|
68 | darray.forEach(argsStr.split(/\s*,\s*/), function(part, idx){ |
---|
69 | preamble += "var "+part+" = arguments["+idx+"]; "; |
---|
70 | }); |
---|
71 | } |
---|
72 | var withStr = script.getAttribute("with"); |
---|
73 | if(withStr && withStr.length){ |
---|
74 | darray.forEach(withStr.split(/\s*,\s*/), function(part){ |
---|
75 | preamble += "with("+part+"){"; |
---|
76 | suffix += "}"; |
---|
77 | }); |
---|
78 | } |
---|
79 | return new Function(preamble+script.innerHTML+suffix); |
---|
80 | }; |
---|
81 | |
---|
82 | this.instantiate = /*====== dojo.parser.instantiate= ======*/function(nodes, mixin, args){ |
---|
83 | // summary: |
---|
84 | // Takes array of nodes, and turns them into class instances and |
---|
85 | // potentially calls a startup method to allow them to connect with |
---|
86 | // any children. |
---|
87 | // nodes: Array |
---|
88 | // Array of nodes or objects like |
---|
89 | // | { |
---|
90 | // | type: "dijit.form.Button", |
---|
91 | // | node: DOMNode, |
---|
92 | // | scripts: [ ... ], // array of <script type="dojo/..."> children of node |
---|
93 | // | inherited: { ... } // settings inherited from ancestors like dir, theme, etc. |
---|
94 | // | } |
---|
95 | // mixin: Object? |
---|
96 | // An object that will be mixed in with each node in the array. |
---|
97 | // Values in the mixin will override values in the node, if they |
---|
98 | // exist. |
---|
99 | // args: Object? |
---|
100 | // An object used to hold kwArgs for instantiation. |
---|
101 | // See parse.args argument for details. |
---|
102 | |
---|
103 | var thelist = [], |
---|
104 | mixin = mixin||{}; |
---|
105 | args = args||{}; |
---|
106 | |
---|
107 | // Precompute names of special attributes we are looking for |
---|
108 | // TODO: for 2.0 default to data-dojo- regardless of scopeName (or maybe scopeName won't exist in 2.0) |
---|
109 | var dojoType = (args.scope || dojo._scopeName) + "Type", // typically "dojoType" |
---|
110 | attrData = "data-" + (args.scope || dojo._scopeName) + "-",// typically "data-dojo-" |
---|
111 | dataDojoType = attrData + "type", // typically "data-dojo-type" |
---|
112 | dataDojoProps = attrData + "props", // typically "data-dojo-props" |
---|
113 | dataDojoAttachPoint = attrData + "attach-point", |
---|
114 | dataDojoAttachEvent = attrData + "attach-event", |
---|
115 | dataDojoId = attrData + "id"; |
---|
116 | |
---|
117 | // And make hash to quickly check if a given attribute is special, and to map the name to something friendly |
---|
118 | var specialAttrs = {}; |
---|
119 | darray.forEach([dataDojoProps, dataDojoType, dojoType, dataDojoId, "jsId", dataDojoAttachPoint, |
---|
120 | dataDojoAttachEvent, "dojoAttachPoint", "dojoAttachEvent", "class", "style"], function(name){ |
---|
121 | specialAttrs[name.toLowerCase()] = name.replace(args.scope, "dojo"); |
---|
122 | }); |
---|
123 | |
---|
124 | darray.forEach(nodes, function(obj){ |
---|
125 | if(!obj){ return; } |
---|
126 | |
---|
127 | var node = obj.node || obj, |
---|
128 | type = dojoType in mixin ? mixin[dojoType] : obj.node ? obj.type : (node.getAttribute(dataDojoType) || node.getAttribute(dojoType)), |
---|
129 | ctor = _ctorMap[type] || (_ctorMap[type] = dlang.getObject(type)), |
---|
130 | proto = ctor && ctor.prototype; |
---|
131 | if(!ctor){ |
---|
132 | throw new Error("Could not load class '" + type); |
---|
133 | } |
---|
134 | |
---|
135 | // Setup hash to hold parameter settings for this widget. Start with the parameter |
---|
136 | // settings inherited from ancestors ("dir" and "lang"). |
---|
137 | // Inherited setting may later be overridden by explicit settings on node itself. |
---|
138 | var params = {}; |
---|
139 | |
---|
140 | if(args.defaults){ |
---|
141 | // settings for the document itself (or whatever subtree is being parsed) |
---|
142 | dlang.mixin(params, args.defaults); |
---|
143 | } |
---|
144 | if(obj.inherited){ |
---|
145 | // settings from dir=rtl or lang=... on a node above this node |
---|
146 | dlang.mixin(params, obj.inherited); |
---|
147 | } |
---|
148 | |
---|
149 | // Get list of attributes explicitly listed in the markup |
---|
150 | var attributes; |
---|
151 | if(has("dom-attributes-explicit")){ |
---|
152 | // Standard path to get list of user specified attributes |
---|
153 | attributes = node.attributes; |
---|
154 | }else{ |
---|
155 | // Special path for IE, avoid (sometimes >100) bogus entries in node.attributes |
---|
156 | var clone = /^input$|^img$/i.test(node.nodeName) ? node : node.cloneNode(false), |
---|
157 | attrs = clone.outerHTML.replace(/=[^\s"']+|="[^"]*"|='[^']*'/g, "").replace(/^\s*<[a-zA-Z0-9]*/, "").replace(/>.*$/, ""); |
---|
158 | |
---|
159 | attributes = darray.map(attrs.split(/\s+/), function(name){ |
---|
160 | var lcName = name.toLowerCase(); |
---|
161 | return { |
---|
162 | name: name, |
---|
163 | // getAttribute() doesn't work for button.value, returns innerHTML of button. |
---|
164 | // but getAttributeNode().value doesn't work for the form.encType or li.value |
---|
165 | value: (node.nodeName == "LI" && name == "value") || lcName == "enctype" ? |
---|
166 | node.getAttribute(lcName) : node.getAttributeNode(lcName).value, |
---|
167 | specified: true |
---|
168 | }; |
---|
169 | }); |
---|
170 | } |
---|
171 | |
---|
172 | // Read in attributes and process them, including data-dojo-props, data-dojo-type, |
---|
173 | // dojoAttachPoint, etc., as well as normal foo=bar attributes. |
---|
174 | var i=0, item; |
---|
175 | while(item = attributes[i++]){ |
---|
176 | if(!item || !item.specified){ |
---|
177 | continue; |
---|
178 | } |
---|
179 | |
---|
180 | var name = item.name, |
---|
181 | lcName = name.toLowerCase(), |
---|
182 | value = item.value; |
---|
183 | |
---|
184 | if(lcName in specialAttrs){ |
---|
185 | switch(specialAttrs[lcName]){ |
---|
186 | |
---|
187 | // Data-dojo-props. Save for later to make sure it overrides direct foo=bar settings |
---|
188 | case "data-dojo-props": |
---|
189 | var extra = value; |
---|
190 | break; |
---|
191 | |
---|
192 | // data-dojo-id or jsId. TODO: drop jsId in 2.0 |
---|
193 | case "data-dojo-id": |
---|
194 | case "jsId": |
---|
195 | var jsname = value; |
---|
196 | break; |
---|
197 | |
---|
198 | // For the benefit of _Templated |
---|
199 | case "data-dojo-attach-point": |
---|
200 | case "dojoAttachPoint": |
---|
201 | params.dojoAttachPoint = value; |
---|
202 | break; |
---|
203 | case "data-dojo-attach-event": |
---|
204 | case "dojoAttachEvent": |
---|
205 | params.dojoAttachEvent = value; |
---|
206 | break; |
---|
207 | |
---|
208 | // Special parameter handling needed for IE |
---|
209 | case "class": |
---|
210 | params["class"] = node.className; |
---|
211 | break; |
---|
212 | case "style": |
---|
213 | params["style"] = node.style && node.style.cssText; |
---|
214 | break; |
---|
215 | } |
---|
216 | }else{ |
---|
217 | // Normal attribute, ex: value="123" |
---|
218 | |
---|
219 | // Find attribute in widget corresponding to specified name. |
---|
220 | // May involve case conversion, ex: onclick --> onClick |
---|
221 | if(!(name in proto)){ |
---|
222 | var map = (_nameMap[type] || (_nameMap[type] = getNameMap(proto))); |
---|
223 | name = map[lcName] || name; |
---|
224 | } |
---|
225 | |
---|
226 | // Set params[name] to value, doing type conversion |
---|
227 | if(name in proto){ |
---|
228 | switch(typeof proto[name]){ |
---|
229 | case "string": |
---|
230 | params[name] = value; |
---|
231 | break; |
---|
232 | case "number": |
---|
233 | params[name] = value.length ? Number(value) : NaN; |
---|
234 | break; |
---|
235 | case "boolean": |
---|
236 | // for checked/disabled value might be "" or "checked". interpret as true. |
---|
237 | params[name] = value.toLowerCase() != "false"; |
---|
238 | break; |
---|
239 | case "function": |
---|
240 | if(value === "" || value.search(/[^\w\.]+/i) != -1){ |
---|
241 | // The user has specified some text for a function like "return x+5" |
---|
242 | params[name] = new Function(value); |
---|
243 | }else{ |
---|
244 | // The user has specified the name of a function like "myOnClick" |
---|
245 | // or a single word function "return" |
---|
246 | params[name] = dlang.getObject(value, false) || new Function(value); |
---|
247 | } |
---|
248 | break; |
---|
249 | default: |
---|
250 | var pVal = proto[name]; |
---|
251 | params[name] = |
---|
252 | (pVal && "length" in pVal) ? (value ? value.split(/\s*,\s*/) : []) : // array |
---|
253 | (pVal instanceof Date) ? |
---|
254 | (value == "" ? new Date("") : // the NaN of dates |
---|
255 | value == "now" ? new Date() : // current date |
---|
256 | dates.fromISOString(value)) : |
---|
257 | (pVal instanceof dojo._Url) ? (dojo.baseUrl + value) : |
---|
258 | djson.fromJson(value); |
---|
259 | } |
---|
260 | }else{ |
---|
261 | params[name] = value; |
---|
262 | } |
---|
263 | } |
---|
264 | } |
---|
265 | |
---|
266 | // Mix things found in data-dojo-props into the params, overriding any direct settings |
---|
267 | if(extra){ |
---|
268 | try{ |
---|
269 | extra = djson.fromJson.call(args.propsThis, "{" + extra + "}"); |
---|
270 | dlang.mixin(params, extra); |
---|
271 | }catch(e){ |
---|
272 | // give the user a pointer to their invalid parameters. FIXME: can we kill this in production? |
---|
273 | throw new Error(e.toString() + " in data-dojo-props='" + extra + "'"); |
---|
274 | } |
---|
275 | } |
---|
276 | |
---|
277 | // Any parameters specified in "mixin" override everything else. |
---|
278 | dlang.mixin(params, mixin); |
---|
279 | |
---|
280 | var scripts = obj.node ? obj.scripts : (ctor && (ctor._noScript || proto._noScript) ? [] : |
---|
281 | query("> script[type^='dojo/']", node)); |
---|
282 | |
---|
283 | // Process <script type="dojo/*"> script tags |
---|
284 | // <script type="dojo/method" event="foo"> tags are added to params, and passed to |
---|
285 | // the widget on instantiation. |
---|
286 | // <script type="dojo/method"> tags (with no event) are executed after instantiation |
---|
287 | // <script type="dojo/connect" data-dojo-event="foo"> tags are dojo.connected after instantiation |
---|
288 | // <script type="dojo/watch" data-dojo-prop="foo"> tags are dojo.watch after instantiation |
---|
289 | // <script type="dojo/on" data-dojo-event="foo"> tags are dojo.on after instantiation |
---|
290 | // note: dojo/* script tags cannot exist in self closing widgets, like <input /> |
---|
291 | var connects = [], // functions to connect after instantiation |
---|
292 | calls = [], // functions to call after instantiation |
---|
293 | watch = [], //functions to watch after instantiation |
---|
294 | on = []; //functions to on after instantiation |
---|
295 | |
---|
296 | if(scripts){ |
---|
297 | for(i=0; i<scripts.length; i++){ |
---|
298 | var script = scripts[i]; |
---|
299 | node.removeChild(script); |
---|
300 | // FIXME: drop event="" support in 2.0. use data-dojo-event="" instead |
---|
301 | var event = (script.getAttribute(attrData + "event") || script.getAttribute("event")), |
---|
302 | prop = script.getAttribute(attrData + "prop"), |
---|
303 | type = script.getAttribute("type"), |
---|
304 | nf = this._functionFromScript(script, attrData); |
---|
305 | if(event){ |
---|
306 | if(type == "dojo/connect"){ |
---|
307 | connects.push({event: event, func: nf}); |
---|
308 | }else if(type == "dojo/on"){ |
---|
309 | on.push({event: event, func: nf}); |
---|
310 | }else{ |
---|
311 | params[event] = nf; |
---|
312 | } |
---|
313 | }else if(type == "dojo/watch"){ |
---|
314 | watch.push({prop: prop, func: nf}); |
---|
315 | }else{ |
---|
316 | calls.push(nf); |
---|
317 | } |
---|
318 | } |
---|
319 | } |
---|
320 | |
---|
321 | // create the instance |
---|
322 | var markupFactory = ctor.markupFactory || proto.markupFactory; |
---|
323 | var instance = markupFactory ? markupFactory(params, node, ctor) : new ctor(params, node); |
---|
324 | thelist.push(instance); |
---|
325 | |
---|
326 | // map it to the JS namespace if that makes sense |
---|
327 | if(jsname){ |
---|
328 | dlang.setObject(jsname, instance); |
---|
329 | } |
---|
330 | |
---|
331 | // process connections and startup functions |
---|
332 | for(i=0; i<connects.length; i++){ |
---|
333 | aspect.after(instance, connects[i].event, dojo.hitch(instance, connects[i].func), true); |
---|
334 | } |
---|
335 | for(i=0; i<calls.length; i++){ |
---|
336 | calls[i].call(instance); |
---|
337 | } |
---|
338 | for(i=0; i<watch.length; i++){ |
---|
339 | instance.watch(watch[i].prop, watch[i].func); |
---|
340 | } |
---|
341 | for(i=0; i<on.length; i++){ |
---|
342 | don(instance, on[i].event, on[i].func); |
---|
343 | } |
---|
344 | }, this); |
---|
345 | |
---|
346 | // Call startup on each top level instance if it makes sense (as for |
---|
347 | // widgets). Parent widgets will recursively call startup on their |
---|
348 | // (non-top level) children |
---|
349 | if(!mixin._started){ |
---|
350 | darray.forEach(thelist, function(instance){ |
---|
351 | if( !args.noStart && instance && |
---|
352 | dlang.isFunction(instance.startup) && |
---|
353 | !instance._started |
---|
354 | ){ |
---|
355 | instance.startup(); |
---|
356 | } |
---|
357 | }); |
---|
358 | } |
---|
359 | return thelist; |
---|
360 | }; |
---|
361 | |
---|
362 | this.parse = /*====== dojo.parser.parse= ======*/ function(rootNode, args){ |
---|
363 | // summary: |
---|
364 | // Scan the DOM for class instances, and instantiate them. |
---|
365 | // |
---|
366 | // description: |
---|
367 | // Search specified node (or root node) recursively for class instances, |
---|
368 | // and instantiate them. Searches for either data-dojo-type="Class" or |
---|
369 | // dojoType="Class" where "Class" is a a fully qualified class name, |
---|
370 | // like `dijit.form.Button` |
---|
371 | // |
---|
372 | // Using `data-dojo-type`: |
---|
373 | // Attributes using can be mixed into the parameters used to instantiate the |
---|
374 | // Class by using a `data-dojo-props` attribute on the node being converted. |
---|
375 | // `data-dojo-props` should be a string attribute to be converted from JSON. |
---|
376 | // |
---|
377 | // Using `dojoType`: |
---|
378 | // Attributes are read from the original domNode and converted to appropriate |
---|
379 | // types by looking up the Class prototype values. This is the default behavior |
---|
380 | // from Dojo 1.0 to Dojo 1.5. `dojoType` support is deprecated, and will |
---|
381 | // go away in Dojo 2.0. |
---|
382 | // |
---|
383 | // rootNode: DomNode? |
---|
384 | // A default starting root node from which to start the parsing. Can be |
---|
385 | // omitted, defaulting to the entire document. If omitted, the `args` |
---|
386 | // object can be passed in this place. If the `args` object has a |
---|
387 | // `rootNode` member, that is used. |
---|
388 | // |
---|
389 | // args: Object |
---|
390 | // a kwArgs object passed along to instantiate() |
---|
391 | // |
---|
392 | // * noStart: Boolean? |
---|
393 | // when set will prevent the parser from calling .startup() |
---|
394 | // when locating the nodes. |
---|
395 | // * rootNode: DomNode? |
---|
396 | // identical to the function's `rootNode` argument, though |
---|
397 | // allowed to be passed in via this `args object. |
---|
398 | // * template: Boolean |
---|
399 | // If true, ignores ContentPane's stopParser flag and parses contents inside of |
---|
400 | // a ContentPane inside of a template. This allows dojoAttachPoint on widgets/nodes |
---|
401 | // nested inside the ContentPane to work. |
---|
402 | // * inherited: Object |
---|
403 | // Hash possibly containing dir and lang settings to be applied to |
---|
404 | // parsed widgets, unless there's another setting on a sub-node that overrides |
---|
405 | // * scope: String |
---|
406 | // Root for attribute names to search for. If scopeName is dojo, |
---|
407 | // will search for data-dojo-type (or dojoType). For backwards compatibility |
---|
408 | // reasons defaults to dojo._scopeName (which is "dojo" except when |
---|
409 | // multi-version support is used, when it will be something like dojo16, dojo20, etc.) |
---|
410 | // * propsThis: Object |
---|
411 | // If specified, "this" referenced from data-dojo-props will refer to propsThis. |
---|
412 | // Intended for use from the widgets-in-template feature of `dijit._WidgetsInTemplateMixin` |
---|
413 | // |
---|
414 | // example: |
---|
415 | // Parse all widgets on a page: |
---|
416 | // | dojo.parser.parse(); |
---|
417 | // |
---|
418 | // example: |
---|
419 | // Parse all classes within the node with id="foo" |
---|
420 | // | dojo.parser.parse(dojo.byId('foo')); |
---|
421 | // |
---|
422 | // example: |
---|
423 | // Parse all classes in a page, but do not call .startup() on any |
---|
424 | // child |
---|
425 | // | dojo.parser.parse({ noStart: true }) |
---|
426 | // |
---|
427 | // example: |
---|
428 | // Parse all classes in a node, but do not call .startup() |
---|
429 | // | dojo.parser.parse(someNode, { noStart:true }); |
---|
430 | // | // or |
---|
431 | // | dojo.parser.parse({ noStart:true, rootNode: someNode }); |
---|
432 | |
---|
433 | // determine the root node based on the passed arguments. |
---|
434 | var root; |
---|
435 | if(!args && rootNode && rootNode.rootNode){ |
---|
436 | args = rootNode; |
---|
437 | root = args.rootNode; |
---|
438 | }else{ |
---|
439 | root = rootNode; |
---|
440 | } |
---|
441 | root = root ? dhtml.byId(root) : dwindow.body(); |
---|
442 | args = args || {}; |
---|
443 | |
---|
444 | var dojoType = (args.scope || dojo._scopeName) + "Type", // typically "dojoType" |
---|
445 | attrData = "data-" + (args.scope || dojo._scopeName) + "-", // typically "data-dojo-" |
---|
446 | dataDojoType = attrData + "type", // typically "data-dojo-type" |
---|
447 | dataDojoTextDir = attrData + "textdir"; // typically "data-dojo-textdir" |
---|
448 | |
---|
449 | // List of all nodes on page w/dojoType specified |
---|
450 | var list = []; |
---|
451 | |
---|
452 | // Info on DOMNode currently being processed |
---|
453 | var node = root.firstChild; |
---|
454 | |
---|
455 | // Info on parent of DOMNode currently being processed |
---|
456 | // - inherited: dir, lang, and textDir setting of parent, or inherited by parent |
---|
457 | // - parent: pointer to identical structure for my parent (or null if no parent) |
---|
458 | // - scripts: if specified, collects <script type="dojo/..."> type nodes from children |
---|
459 | var inherited = args && args.inherited; |
---|
460 | if(!inherited){ |
---|
461 | function findAncestorAttr(node, attr){ |
---|
462 | return (node.getAttribute && node.getAttribute(attr)) || |
---|
463 | (node !== dwindow.doc && node !== dwindow.doc.documentElement && node.parentNode ? findAncestorAttr(node.parentNode, attr) : null); |
---|
464 | } |
---|
465 | inherited = { |
---|
466 | dir: findAncestorAttr(root, "dir"), |
---|
467 | lang: findAncestorAttr(root, "lang"), |
---|
468 | textDir: findAncestorAttr(root, dataDojoTextDir) |
---|
469 | }; |
---|
470 | for(var key in inherited){ |
---|
471 | if(!inherited[key]){ delete inherited[key]; } |
---|
472 | } |
---|
473 | } |
---|
474 | var parent = { |
---|
475 | inherited: inherited |
---|
476 | }; |
---|
477 | |
---|
478 | // For collecting <script type="dojo/..."> type nodes (when null, we don't need to collect) |
---|
479 | var scripts; |
---|
480 | |
---|
481 | // when true, only look for <script type="dojo/..."> tags, and don't recurse to children |
---|
482 | var scriptsOnly; |
---|
483 | |
---|
484 | function getEffective(parent){ |
---|
485 | // summary: |
---|
486 | // Get effective dir, lang, textDir settings for specified obj |
---|
487 | // (matching "parent" object structure above), and do caching. |
---|
488 | // Take care not to return null entries. |
---|
489 | if(!parent.inherited){ |
---|
490 | parent.inherited = {}; |
---|
491 | var node = parent.node, |
---|
492 | grandparent = getEffective(parent.parent); |
---|
493 | var inherited = { |
---|
494 | dir: node.getAttribute("dir") || grandparent.dir, |
---|
495 | lang: node.getAttribute("lang") || grandparent.lang, |
---|
496 | textDir: node.getAttribute(dataDojoTextDir) || grandparent.textDir |
---|
497 | }; |
---|
498 | for(var key in inherited){ |
---|
499 | if(inherited[key]){ |
---|
500 | parent.inherited[key] = inherited[key]; |
---|
501 | } |
---|
502 | } |
---|
503 | } |
---|
504 | return parent.inherited; |
---|
505 | } |
---|
506 | |
---|
507 | // DFS on DOM tree, collecting nodes with data-dojo-type specified. |
---|
508 | while(true){ |
---|
509 | if(!node){ |
---|
510 | // Finished this level, continue to parent's next sibling |
---|
511 | if(!parent || !parent.node){ |
---|
512 | break; |
---|
513 | } |
---|
514 | node = parent.node.nextSibling; |
---|
515 | scripts = parent.scripts; |
---|
516 | scriptsOnly = false; |
---|
517 | parent = parent.parent; |
---|
518 | continue; |
---|
519 | } |
---|
520 | |
---|
521 | if(node.nodeType != 1){ |
---|
522 | // Text or comment node, skip to next sibling |
---|
523 | node = node.nextSibling; |
---|
524 | continue; |
---|
525 | } |
---|
526 | |
---|
527 | if(scripts && node.nodeName.toLowerCase() == "script"){ |
---|
528 | // Save <script type="dojo/..."> for parent, then continue to next sibling |
---|
529 | type = node.getAttribute("type"); |
---|
530 | if(type && /^dojo\/\w/i.test(type)){ |
---|
531 | scripts.push(node); |
---|
532 | } |
---|
533 | node = node.nextSibling; |
---|
534 | continue; |
---|
535 | } |
---|
536 | if(scriptsOnly){ |
---|
537 | node = node.nextSibling; |
---|
538 | continue; |
---|
539 | } |
---|
540 | |
---|
541 | // Check for data-dojo-type attribute, fallback to backward compatible dojoType |
---|
542 | var type = node.getAttribute(dataDojoType) || node.getAttribute(dojoType); |
---|
543 | |
---|
544 | // Short circuit for leaf nodes containing nothing [but text] |
---|
545 | var firstChild = node.firstChild; |
---|
546 | if(!type && (!firstChild || (firstChild.nodeType == 3 && !firstChild.nextSibling))){ |
---|
547 | node = node.nextSibling; |
---|
548 | continue; |
---|
549 | } |
---|
550 | |
---|
551 | // Setup data structure to save info on current node for when we return from processing descendant nodes |
---|
552 | var current = { |
---|
553 | node: node, |
---|
554 | scripts: scripts, |
---|
555 | parent: parent |
---|
556 | }; |
---|
557 | |
---|
558 | // If dojoType/data-dojo-type specified, add to output array of nodes to instantiate |
---|
559 | var ctor = type && (_ctorMap[type] || (_ctorMap[type] = dlang.getObject(type))), // note: won't find classes declared via dojo.Declaration |
---|
560 | childScripts = ctor && !ctor.prototype._noScript ? [] : null; // <script> nodes that are parent's children |
---|
561 | if(type){ |
---|
562 | list.push({ |
---|
563 | "type": type, |
---|
564 | node: node, |
---|
565 | scripts: childScripts, |
---|
566 | inherited: getEffective(current) // dir & lang settings for current node, explicit or inherited |
---|
567 | }); |
---|
568 | } |
---|
569 | |
---|
570 | // Recurse, collecting <script type="dojo/..."> children, and also looking for |
---|
571 | // descendant nodes with dojoType specified (unless the widget has the stopParser flag). |
---|
572 | // When finished with children, go to my next sibling. |
---|
573 | node = firstChild; |
---|
574 | scripts = childScripts; |
---|
575 | scriptsOnly = ctor && ctor.prototype.stopParser && !(args && args.template); |
---|
576 | parent = current; |
---|
577 | |
---|
578 | } |
---|
579 | |
---|
580 | // go build the object instances |
---|
581 | var mixin = args && args.template ? {template: true} : null; |
---|
582 | return this.instantiate(list, mixin, args); // Array |
---|
583 | }; |
---|
584 | }(); |
---|
585 | |
---|
586 | |
---|
587 | //Register the parser callback. It should be the first callback |
---|
588 | //after the a11y test. |
---|
589 | if(dojo.config.parseOnLoad){ |
---|
590 | dojo.ready(100, dojo.parser, "parse"); |
---|
591 | } |
---|
592 | |
---|
593 | return dojo.parser; |
---|
594 | }); |
---|