[256] | 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 | }); |
---|