[483] | 1 | define([ |
---|
| 2 | "require", "./_base/kernel", "./_base/lang", "./_base/array", "./_base/config", "./dom", "./_base/window", |
---|
| 3 | "./_base/url", "./aspect", "./promise/all", "./date/stamp", "./Deferred", "./has", "./query", "./on", "./ready" |
---|
| 4 | ], function(require, dojo, dlang, darray, config, dom, dwindow, _Url, aspect, all, dates, Deferred, has, query, don, ready){ |
---|
| 5 | |
---|
| 6 | // module: |
---|
| 7 | // dojo/parser |
---|
| 8 | |
---|
| 9 | new Date("X"); // workaround for #11279, new Date("") == NaN |
---|
| 10 | |
---|
| 11 | // data-dojo-props etc. is not restricted to JSON, it can be any javascript |
---|
| 12 | function myEval(text){ |
---|
| 13 | return eval("(" + text + ")"); |
---|
| 14 | } |
---|
| 15 | |
---|
| 16 | // Widgets like BorderContainer add properties to _Widget via dojo.extend(). |
---|
| 17 | // If BorderContainer is loaded after _Widget's parameter list has been cached, |
---|
| 18 | // we need to refresh that parameter list (for _Widget and all widgets that extend _Widget). |
---|
| 19 | var extendCnt = 0; |
---|
| 20 | aspect.after(dlang, "extend", function(){ |
---|
| 21 | extendCnt++; |
---|
| 22 | }, true); |
---|
| 23 | |
---|
| 24 | function getNameMap(ctor){ |
---|
| 25 | // summary: |
---|
| 26 | // Returns map from lowercase name to attribute name in class, ex: {onclick: "onClick"} |
---|
| 27 | var map = ctor._nameCaseMap, proto = ctor.prototype; |
---|
| 28 | |
---|
| 29 | // Create the map if it's undefined. |
---|
| 30 | // Refresh the map if a superclass was possibly extended with new methods since the map was created. |
---|
| 31 | if(!map || map._extendCnt < extendCnt){ |
---|
| 32 | map = ctor._nameCaseMap = {}; |
---|
| 33 | for(var name in proto){ |
---|
| 34 | if(name.charAt(0) === "_"){ |
---|
| 35 | continue; |
---|
| 36 | } // skip internal properties |
---|
| 37 | map[name.toLowerCase()] = name; |
---|
| 38 | } |
---|
| 39 | map._extendCnt = extendCnt; |
---|
| 40 | } |
---|
| 41 | return map; |
---|
| 42 | } |
---|
| 43 | |
---|
| 44 | // Map from widget name or list of widget names(ex: "dijit/form/Button,acme/MyMixin") to a constructor. |
---|
| 45 | var _ctorMap = {}; |
---|
| 46 | |
---|
| 47 | function getCtor(/*String[]*/ types, /*Function?*/ contextRequire){ |
---|
| 48 | // summary: |
---|
| 49 | // Retrieves a constructor. If the types array contains more than one class/MID then the |
---|
| 50 | // subsequent classes will be mixed into the first class and a unique constructor will be |
---|
| 51 | // returned for that array. |
---|
| 52 | |
---|
| 53 | var ts = types.join(); |
---|
| 54 | if(!_ctorMap[ts]){ |
---|
| 55 | var mixins = []; |
---|
| 56 | for(var i = 0, l = types.length; i < l; i++){ |
---|
| 57 | var t = types[i]; |
---|
| 58 | // TODO: Consider swapping getObject and require in the future |
---|
| 59 | mixins[mixins.length] = (_ctorMap[t] = _ctorMap[t] || (dlang.getObject(t) || (~t.indexOf('/') && |
---|
| 60 | (contextRequire ? contextRequire(t) : require(t))))); |
---|
| 61 | } |
---|
| 62 | var ctor = mixins.shift(); |
---|
| 63 | _ctorMap[ts] = mixins.length ? (ctor.createSubclass ? ctor.createSubclass(mixins) : ctor.extend.apply(ctor, mixins)) : ctor; |
---|
| 64 | } |
---|
| 65 | |
---|
| 66 | return _ctorMap[ts]; |
---|
| 67 | } |
---|
| 68 | |
---|
| 69 | var parser = { |
---|
| 70 | // summary: |
---|
| 71 | // The Dom/Widget parsing package |
---|
| 72 | |
---|
| 73 | _clearCache: function(){ |
---|
| 74 | // summary: |
---|
| 75 | // Clear cached data. Used mainly for benchmarking. |
---|
| 76 | extendCnt++; |
---|
| 77 | _ctorMap = {}; |
---|
| 78 | }, |
---|
| 79 | |
---|
| 80 | _functionFromScript: function(script, attrData){ |
---|
| 81 | // summary: |
---|
| 82 | // Convert a `<script type="dojo/method" args="a, b, c"> ... </script>` |
---|
| 83 | // into a function |
---|
| 84 | // script: DOMNode |
---|
| 85 | // The `<script>` DOMNode |
---|
| 86 | // attrData: String |
---|
| 87 | // For HTML5 compliance, searches for attrData + "args" (typically |
---|
| 88 | // "data-dojo-args") instead of "args" |
---|
| 89 | var preamble = "", |
---|
| 90 | suffix = "", |
---|
| 91 | argsStr = (script.getAttribute(attrData + "args") || script.getAttribute("args")), |
---|
| 92 | withStr = script.getAttribute("with"); |
---|
| 93 | |
---|
| 94 | // Convert any arguments supplied in script tag into an array to be passed to the |
---|
| 95 | var fnArgs = (argsStr || "").split(/\s*,\s*/); |
---|
| 96 | |
---|
| 97 | if(withStr && withStr.length){ |
---|
| 98 | darray.forEach(withStr.split(/\s*,\s*/), function(part){ |
---|
| 99 | preamble += "with(" + part + "){"; |
---|
| 100 | suffix += "}"; |
---|
| 101 | }); |
---|
| 102 | } |
---|
| 103 | |
---|
| 104 | return new Function(fnArgs, preamble + script.innerHTML + suffix); |
---|
| 105 | }, |
---|
| 106 | |
---|
| 107 | instantiate: function(nodes, mixin, options){ |
---|
| 108 | // summary: |
---|
| 109 | // Takes array of nodes, and turns them into class instances and |
---|
| 110 | // potentially calls a startup method to allow them to connect with |
---|
| 111 | // any children. |
---|
| 112 | // nodes: Array |
---|
| 113 | // Array of DOM nodes |
---|
| 114 | // mixin: Object? |
---|
| 115 | // An object that will be mixed in with each node in the array. |
---|
| 116 | // Values in the mixin will override values in the node, if they |
---|
| 117 | // exist. |
---|
| 118 | // options: Object? |
---|
| 119 | // An object used to hold kwArgs for instantiation. |
---|
| 120 | // See parse.options argument for details. |
---|
| 121 | // returns: |
---|
| 122 | // Array of instances. |
---|
| 123 | |
---|
| 124 | mixin = mixin || {}; |
---|
| 125 | options = options || {}; |
---|
| 126 | |
---|
| 127 | var dojoType = (options.scope || dojo._scopeName) + "Type", // typically "dojoType" |
---|
| 128 | attrData = "data-" + (options.scope || dojo._scopeName) + "-", // typically "data-dojo-" |
---|
| 129 | dataDojoType = attrData + "type", // typically "data-dojo-type" |
---|
| 130 | dataDojoMixins = attrData + "mixins"; // typically "data-dojo-mixins" |
---|
| 131 | |
---|
| 132 | var list = []; |
---|
| 133 | darray.forEach(nodes, function(node){ |
---|
| 134 | var type = dojoType in mixin ? mixin[dojoType] : node.getAttribute(dataDojoType) || node.getAttribute(dojoType); |
---|
| 135 | if(type){ |
---|
| 136 | var mixinsValue = node.getAttribute(dataDojoMixins), |
---|
| 137 | types = mixinsValue ? [type].concat(mixinsValue.split(/\s*,\s*/)) : [type]; |
---|
| 138 | |
---|
| 139 | list.push({ |
---|
| 140 | node: node, |
---|
| 141 | types: types |
---|
| 142 | }); |
---|
| 143 | } |
---|
| 144 | }); |
---|
| 145 | |
---|
| 146 | // Instantiate the nodes and return the list of instances. |
---|
| 147 | return this._instantiate(list, mixin, options); |
---|
| 148 | }, |
---|
| 149 | |
---|
| 150 | _instantiate: function(nodes, mixin, options, returnPromise){ |
---|
| 151 | // summary: |
---|
| 152 | // Takes array of objects representing nodes, and turns them into class instances and |
---|
| 153 | // potentially calls a startup method to allow them to connect with |
---|
| 154 | // any children. |
---|
| 155 | // nodes: Array |
---|
| 156 | // Array of objects like |
---|
| 157 | // | { |
---|
| 158 | // | ctor: Function (may be null) |
---|
| 159 | // | types: ["dijit/form/Button", "acme/MyMixin"] (used if ctor not specified) |
---|
| 160 | // | node: DOMNode, |
---|
| 161 | // | scripts: [ ... ], // array of <script type="dojo/..."> children of node |
---|
| 162 | // | inherited: { ... } // settings inherited from ancestors like dir, theme, etc. |
---|
| 163 | // | } |
---|
| 164 | // mixin: Object |
---|
| 165 | // An object that will be mixed in with each node in the array. |
---|
| 166 | // Values in the mixin will override values in the node, if they |
---|
| 167 | // exist. |
---|
| 168 | // options: Object |
---|
| 169 | // An options object used to hold kwArgs for instantiation. |
---|
| 170 | // See parse.options argument for details. |
---|
| 171 | // returnPromise: Boolean |
---|
| 172 | // Return a Promise rather than the instance; supports asynchronous widget creation. |
---|
| 173 | // returns: |
---|
| 174 | // Array of instances, or if returnPromise is true, a promise for array of instances |
---|
| 175 | // that resolves when instances have finished initializing. |
---|
| 176 | |
---|
| 177 | // Call widget constructors. Some may be asynchronous and return promises. |
---|
| 178 | var thelist = darray.map(nodes, function(obj){ |
---|
| 179 | var ctor = obj.ctor || getCtor(obj.types, options.contextRequire); |
---|
| 180 | // If we still haven't resolved a ctor, it is fatal now |
---|
| 181 | if(!ctor){ |
---|
| 182 | throw new Error("Unable to resolve constructor for: '" + obj.types.join() + "'"); |
---|
| 183 | } |
---|
| 184 | return this.construct(ctor, obj.node, mixin, options, obj.scripts, obj.inherited); |
---|
| 185 | }, this); |
---|
| 186 | |
---|
| 187 | // After all widget construction finishes, call startup on each top level instance if it makes sense (as for |
---|
| 188 | // widgets). Parent widgets will recursively call startup on their (non-top level) children |
---|
| 189 | function onConstruct(thelist){ |
---|
| 190 | if(!mixin._started && !options.noStart){ |
---|
| 191 | darray.forEach(thelist, function(instance){ |
---|
| 192 | if(typeof instance.startup === "function" && !instance._started){ |
---|
| 193 | instance.startup(); |
---|
| 194 | } |
---|
| 195 | }); |
---|
| 196 | } |
---|
| 197 | |
---|
| 198 | return thelist; |
---|
| 199 | } |
---|
| 200 | |
---|
| 201 | if(returnPromise){ |
---|
| 202 | return all(thelist).then(onConstruct); |
---|
| 203 | }else{ |
---|
| 204 | // Back-compat path, remove for 2.0 |
---|
| 205 | return onConstruct(thelist); |
---|
| 206 | } |
---|
| 207 | }, |
---|
| 208 | |
---|
| 209 | construct: function(ctor, node, mixin, options, scripts, inherited){ |
---|
| 210 | // summary: |
---|
| 211 | // Calls new ctor(params, node), where params is the hash of parameters specified on the node, |
---|
| 212 | // excluding data-dojo-type and data-dojo-mixins. Does not call startup(). |
---|
| 213 | // ctor: Function |
---|
| 214 | // Widget constructor. |
---|
| 215 | // node: DOMNode |
---|
| 216 | // This node will be replaced/attached to by the widget. It also specifies the arguments to pass to ctor. |
---|
| 217 | // mixin: Object? |
---|
| 218 | // Attributes in this object will be passed as parameters to ctor, |
---|
| 219 | // overriding attributes specified on the node. |
---|
| 220 | // options: Object? |
---|
| 221 | // An options object used to hold kwArgs for instantiation. See parse.options argument for details. |
---|
| 222 | // scripts: DomNode[]? |
---|
| 223 | // Array of `<script type="dojo/*">` DOMNodes. If not specified, will search for `<script>` tags inside node. |
---|
| 224 | // inherited: Object? |
---|
| 225 | // Settings from dir=rtl or lang=... on a node above this node. Overrides options.inherited. |
---|
| 226 | // returns: |
---|
| 227 | // Instance or Promise for the instance, if markupFactory() itself returned a promise |
---|
| 228 | |
---|
| 229 | var proto = ctor && ctor.prototype; |
---|
| 230 | options = options || {}; |
---|
| 231 | |
---|
| 232 | // Setup hash to hold parameter settings for this widget. Start with the parameter |
---|
| 233 | // settings inherited from ancestors ("dir" and "lang"). |
---|
| 234 | // Inherited setting may later be overridden by explicit settings on node itself. |
---|
| 235 | var params = {}; |
---|
| 236 | |
---|
| 237 | if(options.defaults){ |
---|
| 238 | // settings for the document itself (or whatever subtree is being parsed) |
---|
| 239 | dlang.mixin(params, options.defaults); |
---|
| 240 | } |
---|
| 241 | if(inherited){ |
---|
| 242 | // settings from dir=rtl or lang=... on a node above this node |
---|
| 243 | dlang.mixin(params, inherited); |
---|
| 244 | } |
---|
| 245 | |
---|
| 246 | // Get list of attributes explicitly listed in the markup |
---|
| 247 | var attributes; |
---|
| 248 | if(has("dom-attributes-explicit")){ |
---|
| 249 | // Standard path to get list of user specified attributes |
---|
| 250 | attributes = node.attributes; |
---|
| 251 | }else if(has("dom-attributes-specified-flag")){ |
---|
| 252 | // Special processing needed for IE8, to skip a few faux values in attributes[] |
---|
| 253 | attributes = darray.filter(node.attributes, function(a){ |
---|
| 254 | return a.specified; |
---|
| 255 | }); |
---|
| 256 | }else{ |
---|
| 257 | // Special path for IE6-7, avoid (sometimes >100) bogus entries in node.attributes |
---|
| 258 | var clone = /^input$|^img$/i.test(node.nodeName) ? node : node.cloneNode(false), |
---|
| 259 | attrs = clone.outerHTML.replace(/=[^\s"']+|="[^"]*"|='[^']*'/g, "").replace(/^\s*<[a-zA-Z0-9]*\s*/, "").replace(/\s*>.*$/, ""); |
---|
| 260 | |
---|
| 261 | attributes = darray.map(attrs.split(/\s+/), function(name){ |
---|
| 262 | var lcName = name.toLowerCase(); |
---|
| 263 | return { |
---|
| 264 | name: name, |
---|
| 265 | // getAttribute() doesn't work for button.value, returns innerHTML of button. |
---|
| 266 | // but getAttributeNode().value doesn't work for the form.encType or li.value |
---|
| 267 | value: (node.nodeName == "LI" && name == "value") || lcName == "enctype" ? |
---|
| 268 | node.getAttribute(lcName) : node.getAttributeNode(lcName).value |
---|
| 269 | }; |
---|
| 270 | }); |
---|
| 271 | } |
---|
| 272 | |
---|
| 273 | // Hash to convert scoped attribute name (ex: data-dojo17-params) to something friendly (ex: data-dojo-params) |
---|
| 274 | // TODO: remove scope for 2.0 |
---|
| 275 | var scope = options.scope || dojo._scopeName, |
---|
| 276 | attrData = "data-" + scope + "-", // typically "data-dojo-" |
---|
| 277 | hash = {}; |
---|
| 278 | if(scope !== "dojo"){ |
---|
| 279 | hash[attrData + "props"] = "data-dojo-props"; |
---|
| 280 | hash[attrData + "type"] = "data-dojo-type"; |
---|
| 281 | hash[attrData + "mixins"] = "data-dojo-mixins"; |
---|
| 282 | hash[scope + "type"] = "dojoType"; |
---|
| 283 | hash[attrData + "id"] = "data-dojo-id"; |
---|
| 284 | } |
---|
| 285 | |
---|
| 286 | // Read in attributes and process them, including data-dojo-props, data-dojo-type, |
---|
| 287 | // dojoAttachPoint, etc., as well as normal foo=bar attributes. |
---|
| 288 | var i = 0, item, funcAttrs = [], jsname, extra; |
---|
| 289 | while(item = attributes[i++]){ |
---|
| 290 | var name = item.name, |
---|
| 291 | lcName = name.toLowerCase(), |
---|
| 292 | value = item.value; |
---|
| 293 | |
---|
| 294 | switch(hash[lcName] || lcName){ |
---|
| 295 | // Already processed, just ignore |
---|
| 296 | case "data-dojo-type": |
---|
| 297 | case "dojotype": |
---|
| 298 | case "data-dojo-mixins": |
---|
| 299 | break; |
---|
| 300 | |
---|
| 301 | // Data-dojo-props. Save for later to make sure it overrides direct foo=bar settings |
---|
| 302 | case "data-dojo-props": |
---|
| 303 | extra = value; |
---|
| 304 | break; |
---|
| 305 | |
---|
| 306 | // data-dojo-id or jsId. TODO: drop jsId in 2.0 |
---|
| 307 | case "data-dojo-id": |
---|
| 308 | case "jsid": |
---|
| 309 | jsname = value; |
---|
| 310 | break; |
---|
| 311 | |
---|
| 312 | // For the benefit of _Templated |
---|
| 313 | case "data-dojo-attach-point": |
---|
| 314 | case "dojoattachpoint": |
---|
| 315 | params.dojoAttachPoint = value; |
---|
| 316 | break; |
---|
| 317 | case "data-dojo-attach-event": |
---|
| 318 | case "dojoattachevent": |
---|
| 319 | params.dojoAttachEvent = value; |
---|
| 320 | break; |
---|
| 321 | |
---|
| 322 | // Special parameter handling needed for IE |
---|
| 323 | case "class": |
---|
| 324 | params["class"] = node.className; |
---|
| 325 | break; |
---|
| 326 | case "style": |
---|
| 327 | params["style"] = node.style && node.style.cssText; |
---|
| 328 | break; |
---|
| 329 | default: |
---|
| 330 | // Normal attribute, ex: value="123" |
---|
| 331 | |
---|
| 332 | // Find attribute in widget corresponding to specified name. |
---|
| 333 | // May involve case conversion, ex: onclick --> onClick |
---|
| 334 | if(!(name in proto)){ |
---|
| 335 | var map = getNameMap(ctor); |
---|
| 336 | name = map[lcName] || name; |
---|
| 337 | } |
---|
| 338 | |
---|
| 339 | // Set params[name] to value, doing type conversion |
---|
| 340 | if(name in proto){ |
---|
| 341 | switch(typeof proto[name]){ |
---|
| 342 | case "string": |
---|
| 343 | params[name] = value; |
---|
| 344 | break; |
---|
| 345 | case "number": |
---|
| 346 | params[name] = value.length ? Number(value) : NaN; |
---|
| 347 | break; |
---|
| 348 | case "boolean": |
---|
| 349 | // for checked/disabled value might be "" or "checked". interpret as true. |
---|
| 350 | params[name] = value.toLowerCase() != "false"; |
---|
| 351 | break; |
---|
| 352 | case "function": |
---|
| 353 | if(value === "" || value.search(/[^\w\.]+/i) != -1){ |
---|
| 354 | // The user has specified some text for a function like "return x+5" |
---|
| 355 | params[name] = new Function(value); |
---|
| 356 | }else{ |
---|
| 357 | // The user has specified the name of a global function like "myOnClick" |
---|
| 358 | // or a single word function "return" |
---|
| 359 | params[name] = dlang.getObject(value, false) || new Function(value); |
---|
| 360 | } |
---|
| 361 | funcAttrs.push(name); // prevent "double connect", see #15026 |
---|
| 362 | break; |
---|
| 363 | default: |
---|
| 364 | var pVal = proto[name]; |
---|
| 365 | params[name] = |
---|
| 366 | (pVal && "length" in pVal) ? (value ? value.split(/\s*,\s*/) : []) : // array |
---|
| 367 | (pVal instanceof Date) ? |
---|
| 368 | (value == "" ? new Date("") : // the NaN of dates |
---|
| 369 | value == "now" ? new Date() : // current date |
---|
| 370 | dates.fromISOString(value)) : |
---|
| 371 | (pVal instanceof _Url) ? (dojo.baseUrl + value) : |
---|
| 372 | myEval(value); |
---|
| 373 | } |
---|
| 374 | }else{ |
---|
| 375 | params[name] = value; |
---|
| 376 | } |
---|
| 377 | } |
---|
| 378 | } |
---|
| 379 | |
---|
| 380 | // Remove function attributes from DOMNode to prevent "double connect" problem, see #15026. |
---|
| 381 | // Do this as a separate loop since attributes[] is often a live collection (depends on the browser though). |
---|
| 382 | for(var j = 0; j < funcAttrs.length; j++){ |
---|
| 383 | var lcfname = funcAttrs[j].toLowerCase(); |
---|
| 384 | node.removeAttribute(lcfname); |
---|
| 385 | node[lcfname] = null; |
---|
| 386 | } |
---|
| 387 | |
---|
| 388 | // Mix things found in data-dojo-props into the params, overriding any direct settings |
---|
| 389 | if(extra){ |
---|
| 390 | try{ |
---|
| 391 | extra = myEval.call(options.propsThis, "{" + extra + "}"); |
---|
| 392 | dlang.mixin(params, extra); |
---|
| 393 | }catch(e){ |
---|
| 394 | // give the user a pointer to their invalid parameters. FIXME: can we kill this in production? |
---|
| 395 | throw new Error(e.toString() + " in data-dojo-props='" + extra + "'"); |
---|
| 396 | } |
---|
| 397 | } |
---|
| 398 | |
---|
| 399 | // Any parameters specified in "mixin" override everything else. |
---|
| 400 | dlang.mixin(params, mixin); |
---|
| 401 | |
---|
| 402 | // Get <script> nodes associated with this widget, if they weren't specified explicitly |
---|
| 403 | if(!scripts){ |
---|
| 404 | scripts = (ctor && (ctor._noScript || proto._noScript) ? [] : query("> script[type^='dojo/']", node)); |
---|
| 405 | } |
---|
| 406 | |
---|
| 407 | // Process <script type="dojo/*"> script tags |
---|
| 408 | // <script type="dojo/method" data-dojo-event="foo"> tags are added to params, and passed to |
---|
| 409 | // the widget on instantiation. |
---|
| 410 | // <script type="dojo/method"> tags (with no event) are executed after instantiation |
---|
| 411 | // <script type="dojo/connect" data-dojo-event="foo"> tags are dojo.connected after instantiation, |
---|
| 412 | // and likewise with <script type="dojo/aspect" data-dojo-method="foo"> |
---|
| 413 | // <script type="dojo/watch" data-dojo-prop="foo"> tags are dojo.watch after instantiation |
---|
| 414 | // <script type="dojo/on" data-dojo-event="foo"> tags are dojo.on after instantiation |
---|
| 415 | // note: dojo/* script tags cannot exist in self closing widgets, like <input /> |
---|
| 416 | var aspects = [], // aspects to connect after instantiation |
---|
| 417 | calls = [], // functions to call after instantiation |
---|
| 418 | watches = [], // functions to watch after instantiation |
---|
| 419 | ons = []; // functions to on after instantiation |
---|
| 420 | |
---|
| 421 | if(scripts){ |
---|
| 422 | for(i = 0; i < scripts.length; i++){ |
---|
| 423 | var script = scripts[i]; |
---|
| 424 | node.removeChild(script); |
---|
| 425 | // FIXME: drop event="" support in 2.0. use data-dojo-event="" instead |
---|
| 426 | var event = (script.getAttribute(attrData + "event") || script.getAttribute("event")), |
---|
| 427 | prop = script.getAttribute(attrData + "prop"), |
---|
| 428 | method = script.getAttribute(attrData + "method"), |
---|
| 429 | advice = script.getAttribute(attrData + "advice"), |
---|
| 430 | scriptType = script.getAttribute("type"), |
---|
| 431 | nf = this._functionFromScript(script, attrData); |
---|
| 432 | if(event){ |
---|
| 433 | if(scriptType == "dojo/connect"){ |
---|
| 434 | aspects.push({ method: event, func: nf }); |
---|
| 435 | }else if(scriptType == "dojo/on"){ |
---|
| 436 | ons.push({ event: event, func: nf }); |
---|
| 437 | }else{ |
---|
| 438 | // <script type="dojo/method" data-dojo-event="foo"> |
---|
| 439 | // TODO for 2.0: use data-dojo-method="foo" instead (also affects dijit/Declaration) |
---|
| 440 | params[event] = nf; |
---|
| 441 | } |
---|
| 442 | }else if(scriptType == "dojo/aspect"){ |
---|
| 443 | aspects.push({ method: method, advice: advice, func: nf }); |
---|
| 444 | }else if(scriptType == "dojo/watch"){ |
---|
| 445 | watches.push({ prop: prop, func: nf }); |
---|
| 446 | }else{ |
---|
| 447 | calls.push(nf); |
---|
| 448 | } |
---|
| 449 | } |
---|
| 450 | } |
---|
| 451 | |
---|
| 452 | // create the instance |
---|
| 453 | var markupFactory = ctor.markupFactory || proto.markupFactory; |
---|
| 454 | var instance = markupFactory ? markupFactory(params, node, ctor) : new ctor(params, node); |
---|
| 455 | |
---|
| 456 | function onInstantiate(instance){ |
---|
| 457 | // map it to the JS namespace if that makes sense |
---|
| 458 | if(jsname){ |
---|
| 459 | dlang.setObject(jsname, instance); |
---|
| 460 | } |
---|
| 461 | |
---|
| 462 | // process connections and startup functions |
---|
| 463 | for(i = 0; i < aspects.length; i++){ |
---|
| 464 | aspect[aspects[i].advice || "after"](instance, aspects[i].method, dlang.hitch(instance, aspects[i].func), true); |
---|
| 465 | } |
---|
| 466 | for(i = 0; i < calls.length; i++){ |
---|
| 467 | calls[i].call(instance); |
---|
| 468 | } |
---|
| 469 | for(i = 0; i < watches.length; i++){ |
---|
| 470 | instance.watch(watches[i].prop, watches[i].func); |
---|
| 471 | } |
---|
| 472 | for(i = 0; i < ons.length; i++){ |
---|
| 473 | don(instance, ons[i].event, ons[i].func); |
---|
| 474 | } |
---|
| 475 | |
---|
| 476 | return instance; |
---|
| 477 | } |
---|
| 478 | |
---|
| 479 | if(instance.then){ |
---|
| 480 | return instance.then(onInstantiate); |
---|
| 481 | }else{ |
---|
| 482 | return onInstantiate(instance); |
---|
| 483 | } |
---|
| 484 | }, |
---|
| 485 | |
---|
| 486 | scan: function(root, options){ |
---|
| 487 | // summary: |
---|
| 488 | // Scan a DOM tree and return an array of objects representing the DOMNodes |
---|
| 489 | // that need to be turned into widgets. |
---|
| 490 | // description: |
---|
| 491 | // Search specified node (or document root node) recursively for class instances |
---|
| 492 | // and return an array of objects that represent potential widgets to be |
---|
| 493 | // instantiated. Searches for either data-dojo-type="MID" or dojoType="MID" where |
---|
| 494 | // "MID" is a module ID like "dijit/form/Button" or a fully qualified Class name |
---|
| 495 | // like "dijit/form/Button". If the MID is not currently available, scan will |
---|
| 496 | // attempt to require() in the module. |
---|
| 497 | // |
---|
| 498 | // See parser.parse() for details of markup. |
---|
| 499 | // root: DomNode? |
---|
| 500 | // A default starting root node from which to start the parsing. Can be |
---|
| 501 | // omitted, defaulting to the entire document. If omitted, the `options` |
---|
| 502 | // object can be passed in this place. If the `options` object has a |
---|
| 503 | // `rootNode` member, that is used. |
---|
| 504 | // options: Object |
---|
| 505 | // a kwArgs options object, see parse() for details |
---|
| 506 | // |
---|
| 507 | // returns: Promise |
---|
| 508 | // A promise that is resolved with the nodes that have been parsed. |
---|
| 509 | |
---|
| 510 | var list = [], // Output List |
---|
| 511 | mids = [], // An array of modules that are not yet loaded |
---|
| 512 | midsHash = {}; // Used to keep the mids array unique |
---|
| 513 | |
---|
| 514 | var dojoType = (options.scope || dojo._scopeName) + "Type", // typically "dojoType" |
---|
| 515 | attrData = "data-" + (options.scope || dojo._scopeName) + "-", // typically "data-dojo-" |
---|
| 516 | dataDojoType = attrData + "type", // typically "data-dojo-type" |
---|
| 517 | dataDojoTextDir = attrData + "textdir", // typically "data-dojo-textdir" |
---|
| 518 | dataDojoMixins = attrData + "mixins"; // typically "data-dojo-mixins" |
---|
| 519 | |
---|
| 520 | // Info on DOMNode currently being processed |
---|
| 521 | var node = root.firstChild; |
---|
| 522 | |
---|
| 523 | // Info on parent of DOMNode currently being processed |
---|
| 524 | // - inherited: dir, lang, and textDir setting of parent, or inherited by parent |
---|
| 525 | // - parent: pointer to identical structure for my parent (or null if no parent) |
---|
| 526 | // - scripts: if specified, collects <script type="dojo/..."> type nodes from children |
---|
| 527 | var inherited = options.inherited; |
---|
| 528 | if(!inherited){ |
---|
| 529 | function findAncestorAttr(node, attr){ |
---|
| 530 | return (node.getAttribute && node.getAttribute(attr)) || |
---|
| 531 | (node.parentNode && findAncestorAttr(node.parentNode, attr)); |
---|
| 532 | } |
---|
| 533 | |
---|
| 534 | inherited = { |
---|
| 535 | dir: findAncestorAttr(root, "dir"), |
---|
| 536 | lang: findAncestorAttr(root, "lang"), |
---|
| 537 | textDir: findAncestorAttr(root, dataDojoTextDir) |
---|
| 538 | }; |
---|
| 539 | for(var key in inherited){ |
---|
| 540 | if(!inherited[key]){ |
---|
| 541 | delete inherited[key]; |
---|
| 542 | } |
---|
| 543 | } |
---|
| 544 | } |
---|
| 545 | |
---|
| 546 | // Metadata about parent node |
---|
| 547 | var parent = { |
---|
| 548 | inherited: inherited |
---|
| 549 | }; |
---|
| 550 | |
---|
| 551 | // For collecting <script type="dojo/..."> type nodes (when null, we don't need to collect) |
---|
| 552 | var scripts; |
---|
| 553 | |
---|
| 554 | // when true, only look for <script type="dojo/..."> tags, and don't recurse to children |
---|
| 555 | var scriptsOnly; |
---|
| 556 | |
---|
| 557 | function getEffective(parent){ |
---|
| 558 | // summary: |
---|
| 559 | // Get effective dir, lang, textDir settings for specified obj |
---|
| 560 | // (matching "parent" object structure above), and do caching. |
---|
| 561 | // Take care not to return null entries. |
---|
| 562 | if(!parent.inherited){ |
---|
| 563 | parent.inherited = {}; |
---|
| 564 | var node = parent.node, |
---|
| 565 | grandparent = getEffective(parent.parent); |
---|
| 566 | var inherited = { |
---|
| 567 | dir: node.getAttribute("dir") || grandparent.dir, |
---|
| 568 | lang: node.getAttribute("lang") || grandparent.lang, |
---|
| 569 | textDir: node.getAttribute(dataDojoTextDir) || grandparent.textDir |
---|
| 570 | }; |
---|
| 571 | for(var key in inherited){ |
---|
| 572 | if(inherited[key]){ |
---|
| 573 | parent.inherited[key] = inherited[key]; |
---|
| 574 | } |
---|
| 575 | } |
---|
| 576 | } |
---|
| 577 | return parent.inherited; |
---|
| 578 | } |
---|
| 579 | |
---|
| 580 | // DFS on DOM tree, collecting nodes with data-dojo-type specified. |
---|
| 581 | while(true){ |
---|
| 582 | if(!node){ |
---|
| 583 | // Finished this level, continue to parent's next sibling |
---|
| 584 | if(!parent || !parent.node){ |
---|
| 585 | break; |
---|
| 586 | } |
---|
| 587 | node = parent.node.nextSibling; |
---|
| 588 | scriptsOnly = false; |
---|
| 589 | parent = parent.parent; |
---|
| 590 | scripts = parent.scripts; |
---|
| 591 | continue; |
---|
| 592 | } |
---|
| 593 | |
---|
| 594 | if(node.nodeType != 1){ |
---|
| 595 | // Text or comment node, skip to next sibling |
---|
| 596 | node = node.nextSibling; |
---|
| 597 | continue; |
---|
| 598 | } |
---|
| 599 | |
---|
| 600 | if(scripts && node.nodeName.toLowerCase() == "script"){ |
---|
| 601 | // Save <script type="dojo/..."> for parent, then continue to next sibling |
---|
| 602 | type = node.getAttribute("type"); |
---|
| 603 | if(type && /^dojo\/\w/i.test(type)){ |
---|
| 604 | scripts.push(node); |
---|
| 605 | } |
---|
| 606 | node = node.nextSibling; |
---|
| 607 | continue; |
---|
| 608 | } |
---|
| 609 | if(scriptsOnly){ |
---|
| 610 | // scriptsOnly flag is set, we have already collected scripts if the parent wants them, so now we shouldn't |
---|
| 611 | // continue further analysis of the node and will continue to the next sibling |
---|
| 612 | node = node.nextSibling; |
---|
| 613 | continue; |
---|
| 614 | } |
---|
| 615 | |
---|
| 616 | // Check for data-dojo-type attribute, fallback to backward compatible dojoType |
---|
| 617 | // TODO: Remove dojoType in 2.0 |
---|
| 618 | var type = node.getAttribute(dataDojoType) || node.getAttribute(dojoType); |
---|
| 619 | |
---|
| 620 | // Short circuit for leaf nodes containing nothing [but text] |
---|
| 621 | var firstChild = node.firstChild; |
---|
| 622 | if(!type && (!firstChild || (firstChild.nodeType == 3 && !firstChild.nextSibling))){ |
---|
| 623 | node = node.nextSibling; |
---|
| 624 | continue; |
---|
| 625 | } |
---|
| 626 | |
---|
| 627 | // Meta data about current node |
---|
| 628 | var current; |
---|
| 629 | |
---|
| 630 | var ctor = null; |
---|
| 631 | if(type){ |
---|
| 632 | // If dojoType/data-dojo-type specified, add to output array of nodes to instantiate. |
---|
| 633 | var mixinsValue = node.getAttribute(dataDojoMixins), |
---|
| 634 | types = mixinsValue ? [type].concat(mixinsValue.split(/\s*,\s*/)) : [type]; |
---|
| 635 | |
---|
| 636 | // Note: won't find classes declared via dojo/Declaration or any modules that haven't been |
---|
| 637 | // loaded yet so use try/catch to avoid throw from require() |
---|
| 638 | try{ |
---|
| 639 | ctor = getCtor(types, options.contextRequire); |
---|
| 640 | }catch(e){} |
---|
| 641 | |
---|
| 642 | // If the constructor was not found, check to see if it has modules that can be loaded |
---|
| 643 | if(!ctor){ |
---|
| 644 | darray.forEach(types, function(t){ |
---|
| 645 | if(~t.indexOf('/') && !midsHash[t]){ |
---|
| 646 | // If the type looks like a MID and it currently isn't in the array of MIDs to load, add it. |
---|
| 647 | midsHash[t] = true; |
---|
| 648 | mids[mids.length] = t; |
---|
| 649 | } |
---|
| 650 | }); |
---|
| 651 | } |
---|
| 652 | |
---|
| 653 | var childScripts = ctor && !ctor.prototype._noScript ? [] : null; // <script> nodes that are parent's children |
---|
| 654 | |
---|
| 655 | // Setup meta data about this widget node, and save it to list of nodes to instantiate |
---|
| 656 | current = { |
---|
| 657 | types: types, |
---|
| 658 | ctor: ctor, |
---|
| 659 | parent: parent, |
---|
| 660 | node: node, |
---|
| 661 | scripts: childScripts |
---|
| 662 | }; |
---|
| 663 | current.inherited = getEffective(current); // dir & lang settings for current node, explicit or inherited |
---|
| 664 | list.push(current); |
---|
| 665 | }else{ |
---|
| 666 | // Meta data about this non-widget node |
---|
| 667 | current = { |
---|
| 668 | node: node, |
---|
| 669 | scripts: scripts, |
---|
| 670 | parent: parent |
---|
| 671 | }; |
---|
| 672 | } |
---|
| 673 | |
---|
| 674 | // Recurse, collecting <script type="dojo/..."> children, and also looking for |
---|
| 675 | // descendant nodes with dojoType specified (unless the widget has the stopParser flag). |
---|
| 676 | // When finished with children, go to my next sibling. |
---|
| 677 | scripts = childScripts; |
---|
| 678 | scriptsOnly = node.stopParser || (ctor && ctor.prototype.stopParser && !(options.template)); |
---|
| 679 | parent = current; |
---|
| 680 | node = firstChild; |
---|
| 681 | } |
---|
| 682 | |
---|
| 683 | var d = new Deferred(); |
---|
| 684 | |
---|
| 685 | // If there are modules to load then require them in |
---|
| 686 | if(mids.length){ |
---|
| 687 | // Warn that there are modules being auto-required |
---|
| 688 | if(has("dojo-debug-messages")){ |
---|
| 689 | console.warn("WARNING: Modules being Auto-Required: " + mids.join(", ")); |
---|
| 690 | } |
---|
| 691 | var r = options.contextRequire || require; |
---|
| 692 | r(mids, function(){ |
---|
| 693 | // Go through list of widget nodes, filling in missing constructors, and filtering out nodes that shouldn't |
---|
| 694 | // be instantiated due to a stopParser flag on an ancestor that we belatedly learned about due to |
---|
| 695 | // auto-require of a module like ContentPane. Assumes list is in DFS order. |
---|
| 696 | d.resolve(darray.filter(list, function(widget){ |
---|
| 697 | if(!widget.ctor){ |
---|
| 698 | // Attempt to find the constructor again. Still won't find classes defined via |
---|
| 699 | // dijit/Declaration so need to try/catch. |
---|
| 700 | try{ |
---|
| 701 | widget.ctor = getCtor(widget.types, options.contextRequire); |
---|
| 702 | }catch(e){} |
---|
| 703 | } |
---|
| 704 | |
---|
| 705 | // Get the parent widget |
---|
| 706 | var parent = widget.parent; |
---|
| 707 | while(parent && !parent.types){ |
---|
| 708 | parent = parent.parent; |
---|
| 709 | } |
---|
| 710 | |
---|
| 711 | // Return false if this node should be skipped due to stopParser on an ancestor. |
---|
| 712 | // Since list[] is in DFS order, this loop will always set parent.instantiateChildren before |
---|
| 713 | // trying to compute widget.instantiate. |
---|
| 714 | var proto = widget.ctor && widget.ctor.prototype; |
---|
| 715 | widget.instantiateChildren = !(proto && proto.stopParser && !(options.template)); |
---|
| 716 | widget.instantiate = !parent || (parent.instantiate && parent.instantiateChildren); |
---|
| 717 | return widget.instantiate; |
---|
| 718 | })); |
---|
| 719 | }); |
---|
| 720 | }else{ |
---|
| 721 | // There were no modules to load, so just resolve with the parsed nodes. This separate code path is for |
---|
| 722 | // efficiency, to avoid running the require() and the callback code above. |
---|
| 723 | d.resolve(list); |
---|
| 724 | } |
---|
| 725 | |
---|
| 726 | // Return the promise |
---|
| 727 | return d.promise; |
---|
| 728 | }, |
---|
| 729 | |
---|
| 730 | _require: function(/*DOMNode*/ script, /*Object?*/ options){ |
---|
| 731 | // summary: |
---|
| 732 | // Helper for _scanAMD(). Takes a `<script type=dojo/require>bar: "acme/bar", ...</script>` node, |
---|
| 733 | // calls require() to load the specified modules and (asynchronously) assign them to the specified global |
---|
| 734 | // variables, and returns a Promise for when that operation completes. |
---|
| 735 | // |
---|
| 736 | // In the example above, it is effectively doing a require(["acme/bar", ...], function(a){ bar = a; }). |
---|
| 737 | |
---|
| 738 | var hash = myEval("{" + script.innerHTML + "}"), // can't use dojo/json::parse() because maybe no quotes |
---|
| 739 | vars = [], |
---|
| 740 | mids = [], |
---|
| 741 | d = new Deferred(); |
---|
| 742 | |
---|
| 743 | var contextRequire = (options && options.contextRequire) || require; |
---|
| 744 | |
---|
| 745 | for(var name in hash){ |
---|
| 746 | vars.push(name); |
---|
| 747 | mids.push(hash[name]); |
---|
| 748 | } |
---|
| 749 | |
---|
| 750 | contextRequire(mids, function(){ |
---|
| 751 | for(var i = 0; i < vars.length; i++){ |
---|
| 752 | dlang.setObject(vars[i], arguments[i]); |
---|
| 753 | } |
---|
| 754 | d.resolve(arguments); |
---|
| 755 | }); |
---|
| 756 | |
---|
| 757 | return d.promise; |
---|
| 758 | }, |
---|
| 759 | |
---|
| 760 | _scanAmd: function(root, options){ |
---|
| 761 | // summary: |
---|
| 762 | // Scans the DOM for any declarative requires and returns their values. |
---|
| 763 | // description: |
---|
| 764 | // Looks for `<script type=dojo/require>bar: "acme/bar", ...</script>` node, calls require() to load the |
---|
| 765 | // specified modules and (asynchronously) assign them to the specified global variables, |
---|
| 766 | // and returns a Promise for when those operations complete. |
---|
| 767 | // root: DomNode |
---|
| 768 | // The node to base the scan from. |
---|
| 769 | // options: Object? |
---|
| 770 | // a kwArgs options object, see parse() for details |
---|
| 771 | |
---|
| 772 | // Promise that resolves when all the <script type=dojo/require> nodes have finished loading. |
---|
| 773 | var deferred = new Deferred(), |
---|
| 774 | promise = deferred.promise; |
---|
| 775 | deferred.resolve(true); |
---|
| 776 | |
---|
| 777 | var self = this; |
---|
| 778 | query("script[type='dojo/require']", root).forEach(function(node){ |
---|
| 779 | // Fire off require() call for specified modules. Chain this require to fire after |
---|
| 780 | // any previous requires complete, so that layers can be loaded before individual module require()'s fire. |
---|
| 781 | promise = promise.then(function(){ |
---|
| 782 | return self._require(node, options); |
---|
| 783 | }); |
---|
| 784 | |
---|
| 785 | // Remove from DOM so it isn't seen again |
---|
| 786 | node.parentNode.removeChild(node); |
---|
| 787 | }); |
---|
| 788 | |
---|
| 789 | return promise; |
---|
| 790 | }, |
---|
| 791 | |
---|
| 792 | parse: function(rootNode, options){ |
---|
| 793 | // summary: |
---|
| 794 | // Scan the DOM for class instances, and instantiate them. |
---|
| 795 | // description: |
---|
| 796 | // Search specified node (or root node) recursively for class instances, |
---|
| 797 | // and instantiate them. Searches for either data-dojo-type="Class" or |
---|
| 798 | // dojoType="Class" where "Class" is a a fully qualified class name, |
---|
| 799 | // like `dijit/form/Button` |
---|
| 800 | // |
---|
| 801 | // Using `data-dojo-type`: |
---|
| 802 | // Attributes using can be mixed into the parameters used to instantiate the |
---|
| 803 | // Class by using a `data-dojo-props` attribute on the node being converted. |
---|
| 804 | // `data-dojo-props` should be a string attribute to be converted from JSON. |
---|
| 805 | // |
---|
| 806 | // Using `dojoType`: |
---|
| 807 | // Attributes are read from the original domNode and converted to appropriate |
---|
| 808 | // types by looking up the Class prototype values. This is the default behavior |
---|
| 809 | // from Dojo 1.0 to Dojo 1.5. `dojoType` support is deprecated, and will |
---|
| 810 | // go away in Dojo 2.0. |
---|
| 811 | // rootNode: DomNode? |
---|
| 812 | // A default starting root node from which to start the parsing. Can be |
---|
| 813 | // omitted, defaulting to the entire document. If omitted, the `options` |
---|
| 814 | // object can be passed in this place. If the `options` object has a |
---|
| 815 | // `rootNode` member, that is used. |
---|
| 816 | // options: Object? |
---|
| 817 | // A hash of options. |
---|
| 818 | // |
---|
| 819 | // - noStart: Boolean?: |
---|
| 820 | // when set will prevent the parser from calling .startup() |
---|
| 821 | // when locating the nodes. |
---|
| 822 | // - rootNode: DomNode?: |
---|
| 823 | // identical to the function's `rootNode` argument, though |
---|
| 824 | // allowed to be passed in via this `options object. |
---|
| 825 | // - template: Boolean: |
---|
| 826 | // If true, ignores ContentPane's stopParser flag and parses contents inside of |
---|
| 827 | // a ContentPane inside of a template. This allows dojoAttachPoint on widgets/nodes |
---|
| 828 | // nested inside the ContentPane to work. |
---|
| 829 | // - inherited: Object: |
---|
| 830 | // Hash possibly containing dir and lang settings to be applied to |
---|
| 831 | // parsed widgets, unless there's another setting on a sub-node that overrides |
---|
| 832 | // - scope: String: |
---|
| 833 | // Root for attribute names to search for. If scopeName is dojo, |
---|
| 834 | // will search for data-dojo-type (or dojoType). For backwards compatibility |
---|
| 835 | // reasons defaults to dojo._scopeName (which is "dojo" except when |
---|
| 836 | // multi-version support is used, when it will be something like dojo16, dojo20, etc.) |
---|
| 837 | // - propsThis: Object: |
---|
| 838 | // If specified, "this" referenced from data-dojo-props will refer to propsThis. |
---|
| 839 | // Intended for use from the widgets-in-template feature of `dijit._WidgetsInTemplateMixin` |
---|
| 840 | // - contextRequire: Function: |
---|
| 841 | // If specified, this require is utilised for looking resolving modules instead of the |
---|
| 842 | // `dojo/parser` context `require()`. Intended for use from the widgets-in-template feature of |
---|
| 843 | // `dijit._WidgetsInTemplateMixin`. |
---|
| 844 | // returns: Mixed |
---|
| 845 | // Returns a blended object that is an array of the instantiated objects, but also can include |
---|
| 846 | // a promise that is resolved with the instantiated objects. This is done for backwards |
---|
| 847 | // compatibility. If the parser auto-requires modules, it will always behave in a promise |
---|
| 848 | // fashion and `parser.parse().then(function(instances){...})` should be used. |
---|
| 849 | // example: |
---|
| 850 | // Parse all widgets on a page: |
---|
| 851 | // | parser.parse(); |
---|
| 852 | // example: |
---|
| 853 | // Parse all classes within the node with id="foo" |
---|
| 854 | // | parser.parse(dojo.byId('foo')); |
---|
| 855 | // example: |
---|
| 856 | // Parse all classes in a page, but do not call .startup() on any |
---|
| 857 | // child |
---|
| 858 | // | parser.parse({ noStart: true }) |
---|
| 859 | // example: |
---|
| 860 | // Parse all classes in a node, but do not call .startup() |
---|
| 861 | // | parser.parse(someNode, { noStart:true }); |
---|
| 862 | // | // or |
---|
| 863 | // | parser.parse({ noStart:true, rootNode: someNode }); |
---|
| 864 | |
---|
| 865 | // determine the root node and options based on the passed arguments. |
---|
| 866 | var root; |
---|
| 867 | if(!options && rootNode && rootNode.rootNode){ |
---|
| 868 | options = rootNode; |
---|
| 869 | root = options.rootNode; |
---|
| 870 | }else if(rootNode && dlang.isObject(rootNode) && !("nodeType" in rootNode)){ |
---|
| 871 | options = rootNode; |
---|
| 872 | }else{ |
---|
| 873 | root = rootNode; |
---|
| 874 | } |
---|
| 875 | root = root ? dom.byId(root) : dwindow.body(); |
---|
| 876 | |
---|
| 877 | options = options || {}; |
---|
| 878 | |
---|
| 879 | var mixin = options.template ? { template: true } : {}, |
---|
| 880 | instances = [], |
---|
| 881 | self = this; |
---|
| 882 | |
---|
| 883 | // First scan for any <script type=dojo/require> nodes, and execute. |
---|
| 884 | // Then scan for all nodes with data-dojo-type, and load any unloaded modules. |
---|
| 885 | // Then build the object instances. Add instances to already existing (but empty) instances[] array, |
---|
| 886 | // which may already have been returned to caller. Also, use otherwise to collect and throw any errors |
---|
| 887 | // that occur during the parse(). |
---|
| 888 | var p = |
---|
| 889 | this._scanAmd(root, options).then(function(){ |
---|
| 890 | return self.scan(root, options); |
---|
| 891 | }).then(function(parsedNodes){ |
---|
| 892 | return self._instantiate(parsedNodes, mixin, options, true); |
---|
| 893 | }).then(function(_instances){ |
---|
| 894 | // Copy the instances into the instances[] array we declared above, and are accessing as |
---|
| 895 | // our return value. |
---|
| 896 | return instances = instances.concat(_instances); |
---|
| 897 | }).otherwise(function(e){ |
---|
| 898 | // TODO Modify to follow better pattern for promise error management when available |
---|
| 899 | console.error("dojo/parser::parse() error", e); |
---|
| 900 | throw e; |
---|
| 901 | }); |
---|
| 902 | |
---|
| 903 | // Blend the array with the promise |
---|
| 904 | dlang.mixin(instances, p); |
---|
| 905 | return instances; |
---|
| 906 | } |
---|
| 907 | }; |
---|
| 908 | |
---|
| 909 | if(has("extend-dojo")){ |
---|
| 910 | dojo.parser = parser; |
---|
| 911 | } |
---|
| 912 | |
---|
| 913 | // Register the parser callback. It should be the first callback |
---|
| 914 | // after the a11y test. |
---|
| 915 | if(config.parseOnLoad){ |
---|
| 916 | ready(100, parser, "parse"); |
---|
| 917 | } |
---|
| 918 | |
---|
| 919 | return parser; |
---|
| 920 | }); |
---|