diff options
author | Andrew Dolgov <[email protected]> | 2013-03-18 10:26:24 +0400 |
---|---|---|
committer | Andrew Dolgov <[email protected]> | 2013-03-18 10:26:26 +0400 |
commit | f0cfe83e3725f9a3928da97a6e3085e79cb25309 (patch) | |
tree | 4b0af188defaa807c7bc6ff3a101b41c9166c463 /lib/dojo/parser.js.uncompressed.js | |
parent | 9a2885da170ffd64358b99194095851a2d09c1b6 (diff) |
upgrade dojo to 1.8.3 (refs #570)
Diffstat (limited to 'lib/dojo/parser.js.uncompressed.js')
-rw-r--r-- | lib/dojo/parser.js.uncompressed.js | 865 |
1 files changed, 865 insertions, 0 deletions
diff --git a/lib/dojo/parser.js.uncompressed.js b/lib/dojo/parser.js.uncompressed.js new file mode 100644 index 000000000..7ce8c2eb2 --- /dev/null +++ b/lib/dojo/parser.js.uncompressed.js @@ -0,0 +1,865 @@ +define( + "dojo/parser", ["require", "./_base/kernel", "./_base/lang", "./_base/array", "./_base/config", "./_base/html", "./_base/window", + "./_base/url", "./_base/json", "./aspect", "./date/stamp", "./Deferred", "./has", "./query", "./on", "./ready"], + function(require, dojo, dlang, darray, config, dhtml, dwindow, _Url, djson, aspect, dates, Deferred, has, query, don, ready){ + + // module: + // dojo/parser + + new Date("X"); // workaround for #11279, new Date("") == NaN + + + // Widgets like BorderContainer add properties to _Widget via dojo.extend(). + // If BorderContainer is loaded after _Widget's parameter list has been cached, + // we need to refresh that parameter list (for _Widget and all widgets that extend _Widget). + var extendCnt = 0; + aspect.after(dlang, "extend", function(){ + extendCnt++; + }, true); + + function getNameMap(ctor){ + // summary: + // Returns map from lowercase name to attribute name in class, ex: {onclick: "onClick"} + var map = ctor._nameCaseMap, proto = ctor.prototype; + + // Create the map if it's undefined. + // Refresh the map if a superclass was possibly extended with new methods since the map was created. + if(!map || map._extendCnt < extendCnt){ + map = ctor._nameCaseMap = {}; + for(var name in proto){ + if(name.charAt(0) === "_"){ continue; } // skip internal properties + map[name.toLowerCase()] = name; + } + map._extendCnt = extendCnt; + } + return map; + } + + // Map from widget name or list of widget names(ex: "dijit/form/Button,acme/MyMixin") to a constructor. + var _ctorMap = {}; + + function getCtor(/*String[]*/ types){ + // summary: + // Retrieves a constructor. If the types array contains more than one class/MID then the + // subsequent classes will be mixed into the first class and a unique constructor will be + // returned for that array. + + var ts = types.join(); + if(!_ctorMap[ts]){ + var mixins = []; + for(var i = 0, l = types.length; i < l; i++){ + var t = types[i]; + // TODO: Consider swapping getObject and require in the future + mixins[mixins.length] = (_ctorMap[t] = _ctorMap[t] || (dlang.getObject(t) || (~t.indexOf('/') && require(t)))); + } + var ctor = mixins.shift(); + _ctorMap[ts] = mixins.length ? (ctor.createSubclass ? ctor.createSubclass(mixins) : ctor.extend.apply(ctor, mixins)) : ctor; + } + + return _ctorMap[ts]; + } + + var parser = { + // summary: + // The Dom/Widget parsing package + + _clearCache: function(){ + // summary: + // Clear cached data. Used mainly for benchmarking. + extendCnt++; + _ctorMap = {}; + }, + + _functionFromScript: function(script, attrData){ + // summary: + // Convert a `<script type="dojo/method" args="a, b, c"> ... </script>` + // into a function + // script: DOMNode + // The `<script>` DOMNode + // attrData: String + // For HTML5 compliance, searches for attrData + "args" (typically + // "data-dojo-args") instead of "args" + var preamble = "", + suffix = "", + argsStr = (script.getAttribute(attrData + "args") || script.getAttribute("args")), + withStr = script.getAttribute("with"); + + // Convert any arguments supplied in script tag into an array to be passed to the + var fnArgs = (argsStr || "").split(/\s*,\s*/); + + if(withStr && withStr.length){ + darray.forEach(withStr.split(/\s*,\s*/), function(part){ + preamble += "with("+part+"){"; + suffix += "}"; + }); + } + + return new Function(fnArgs, preamble + script.innerHTML + suffix); + }, + + instantiate: function(nodes, mixin, options){ + // summary: + // Takes array of nodes, and turns them into class instances and + // potentially calls a startup method to allow them to connect with + // any children. + // nodes: Array + // Array of DOM nodes + // mixin: Object? + // An object that will be mixed in with each node in the array. + // Values in the mixin will override values in the node, if they + // exist. + // options: Object? + // An object used to hold kwArgs for instantiation. + // See parse.options argument for details. + + mixin = mixin || {}; + options = options || {}; + + var dojoType = (options.scope || dojo._scopeName) + "Type", // typically "dojoType" + attrData = "data-" + (options.scope || dojo._scopeName) + "-",// typically "data-dojo-" + dataDojoType = attrData + "type", // typically "data-dojo-type" + dataDojoMixins = attrData + "mixins"; // typically "data-dojo-mixins" + + var list = []; + darray.forEach(nodes, function(node){ + var type = dojoType in mixin ? mixin[dojoType] : node.getAttribute(dataDojoType) || node.getAttribute(dojoType); + if(type){ + var mixinsValue = node.getAttribute(dataDojoMixins), + types = mixinsValue ? [type].concat(mixinsValue.split(/\s*,\s*/)) : [type]; + + list.push({ + node: node, + types: types + }); + } + }); + + // Instantiate the nodes and return the objects + return this._instantiate(list, mixin, options); + }, + + _instantiate: function(nodes, mixin, options){ + // summary: + // Takes array of objects representing nodes, and turns them into class instances and + // potentially calls a startup method to allow them to connect with + // any children. + // nodes: Array + // Array of objects like + // | { + // | ctor: Function (may be null) + // | types: ["dijit/form/Button", "acme/MyMixin"] (used if ctor not specified) + // | node: DOMNode, + // | scripts: [ ... ], // array of <script type="dojo/..."> children of node + // | inherited: { ... } // settings inherited from ancestors like dir, theme, etc. + // | } + // mixin: Object + // An object that will be mixed in with each node in the array. + // Values in the mixin will override values in the node, if they + // exist. + // options: Object + // An options object used to hold kwArgs for instantiation. + // See parse.options argument for details. + + // Call widget constructors + var thelist = darray.map(nodes, function(obj){ + var ctor = obj.ctor || getCtor(obj.types); + // If we still haven't resolved a ctor, it is fatal now + if(!ctor){ + throw new Error("Unable to resolve constructor for: '" + obj.types.join() + "'"); + } + return this.construct(ctor, obj.node, mixin, options, obj.scripts, obj.inherited); + }, this); + + // Call startup on each top level instance if it makes sense (as for + // widgets). Parent widgets will recursively call startup on their + // (non-top level) children + if(!mixin._started && !options.noStart){ + darray.forEach(thelist, function(instance){ + if(typeof instance.startup === "function" && !instance._started){ + instance.startup(); + } + }); + } + + return thelist; + }, + + construct: function(ctor, node, mixin, options, scripts, inherited){ + // summary: + // Calls new ctor(params, node), where params is the hash of parameters specified on the node, + // excluding data-dojo-type and data-dojo-mixins. Does not call startup(). Returns the widget. + // ctor: Function + // Widget constructor. + // node: DOMNode + // This node will be replaced/attached to by the widget. It also specifies the arguments to pass to ctor. + // mixin: Object? + // Attributes in this object will be passed as parameters to ctor, + // overriding attributes specified on the node. + // options: Object? + // An options object used to hold kwArgs for instantiation. See parse.options argument for details. + // scripts: DomNode[]? + // Array of `<script type="dojo/*">` DOMNodes. If not specified, will search for `<script>` tags inside node. + // inherited: Object? + // Settings from dir=rtl or lang=... on a node above this node. Overrides options.inherited. + + var proto = ctor && ctor.prototype; + options = options || {}; + + // Setup hash to hold parameter settings for this widget. Start with the parameter + // settings inherited from ancestors ("dir" and "lang"). + // Inherited setting may later be overridden by explicit settings on node itself. + var params = {}; + + if(options.defaults){ + // settings for the document itself (or whatever subtree is being parsed) + dlang.mixin(params, options.defaults); + } + if(inherited){ + // settings from dir=rtl or lang=... on a node above this node + dlang.mixin(params, inherited); + } + + // Get list of attributes explicitly listed in the markup + var attributes; + if(has("dom-attributes-explicit")){ + // Standard path to get list of user specified attributes + attributes = node.attributes; + }else if(has("dom-attributes-specified-flag")){ + // Special processing needed for IE8, to skip a few faux values in attributes[] + attributes = darray.filter(node.attributes, function(a){ return a.specified;}); + }else{ + // Special path for IE6-7, avoid (sometimes >100) bogus entries in node.attributes + var clone = /^input$|^img$/i.test(node.nodeName) ? node : node.cloneNode(false), + attrs = clone.outerHTML.replace(/=[^\s"']+|="[^"]*"|='[^']*'/g, "").replace(/^\s*<[a-zA-Z0-9]*\s*/, "").replace(/\s*>.*$/, ""); + + attributes = darray.map(attrs.split(/\s+/), function(name){ + var lcName = name.toLowerCase(); + return { + name: name, + // getAttribute() doesn't work for button.value, returns innerHTML of button. + // but getAttributeNode().value doesn't work for the form.encType or li.value + value: (node.nodeName == "LI" && name == "value") || lcName == "enctype" ? + node.getAttribute(lcName) : node.getAttributeNode(lcName).value + }; + }); + } + + // Hash to convert scoped attribute name (ex: data-dojo17-params) to something friendly (ex: data-dojo-params) + // TODO: remove scope for 2.0 + var scope = options.scope || dojo._scopeName, + attrData = "data-" + scope + "-", // typically "data-dojo-" + hash = {}; + if(scope !== "dojo"){ + hash[attrData + "props"] = "data-dojo-props"; + hash[attrData + "type"] = "data-dojo-type"; + hash[attrData + "mixins"] = "data-dojo-mixins"; + hash[scope + "type"] = "dojoType"; + hash[attrData + "id"] = "data-dojo-id"; + } + + // Read in attributes and process them, including data-dojo-props, data-dojo-type, + // dojoAttachPoint, etc., as well as normal foo=bar attributes. + var i=0, item, funcAttrs=[], jsname, extra; + while(item = attributes[i++]){ + var name = item.name, + lcName = name.toLowerCase(), + value = item.value; + + switch(hash[lcName] || lcName){ + // Already processed, just ignore + case "data-dojo-type": + case "dojotype": + case "data-dojo-mixins": + break; + + // Data-dojo-props. Save for later to make sure it overrides direct foo=bar settings + case "data-dojo-props": + extra = value; + break; + + // data-dojo-id or jsId. TODO: drop jsId in 2.0 + case "data-dojo-id": + case "jsid": + jsname = value; + break; + + // For the benefit of _Templated + case "data-dojo-attach-point": + case "dojoattachpoint": + params.dojoAttachPoint = value; + break; + case "data-dojo-attach-event": + case "dojoattachevent": + params.dojoAttachEvent = value; + break; + + // Special parameter handling needed for IE + case "class": + params["class"] = node.className; + break; + case "style": + params["style"] = node.style && node.style.cssText; + break; + default: + // Normal attribute, ex: value="123" + + // Find attribute in widget corresponding to specified name. + // May involve case conversion, ex: onclick --> onClick + if(!(name in proto)){ + var map = getNameMap(ctor); + name = map[lcName] || name; + } + + // Set params[name] to value, doing type conversion + if(name in proto){ + switch(typeof proto[name]){ + case "string": + params[name] = value; + break; + case "number": + params[name] = value.length ? Number(value) : NaN; + break; + case "boolean": + // for checked/disabled value might be "" or "checked". interpret as true. + params[name] = value.toLowerCase() != "false"; + break; + case "function": + if(value === "" || value.search(/[^\w\.]+/i) != -1){ + // The user has specified some text for a function like "return x+5" + params[name] = new Function(value); + }else{ + // The user has specified the name of a global function like "myOnClick" + // or a single word function "return" + params[name] = dlang.getObject(value, false) || new Function(value); + } + funcAttrs.push(name); // prevent "double connect", see #15026 + break; + default: + var pVal = proto[name]; + params[name] = + (pVal && "length" in pVal) ? (value ? value.split(/\s*,\s*/) : []) : // array + (pVal instanceof Date) ? + (value == "" ? new Date("") : // the NaN of dates + value == "now" ? new Date() : // current date + dates.fromISOString(value)) : + (pVal instanceof _Url) ? (dojo.baseUrl + value) : + djson.fromJson(value); + } + }else{ + params[name] = value; + } + } + } + + // Remove function attributes from DOMNode to prevent "double connect" problem, see #15026. + // Do this as a separate loop since attributes[] is often a live collection (depends on the browser though). + for(var j=0; j<funcAttrs.length; j++){ + var lcfname = funcAttrs[j].toLowerCase(); + node.removeAttribute(lcfname); + node[lcfname] = null; + } + + // Mix things found in data-dojo-props into the params, overriding any direct settings + if(extra){ + try{ + extra = djson.fromJson.call(options.propsThis, "{" + extra + "}"); + dlang.mixin(params, extra); + }catch(e){ + // give the user a pointer to their invalid parameters. FIXME: can we kill this in production? + throw new Error(e.toString() + " in data-dojo-props='" + extra + "'"); + } + } + + // Any parameters specified in "mixin" override everything else. + dlang.mixin(params, mixin); + + // Get <script> nodes associated with this widget, if they weren't specified explicitly + if(!scripts){ + scripts = (ctor && (ctor._noScript || proto._noScript) ? [] : query("> script[type^='dojo/']", node)); + } + + // Process <script type="dojo/*"> script tags + // <script type="dojo/method" event="foo"> tags are added to params, and passed to + // the widget on instantiation. + // <script type="dojo/method"> tags (with no event) are executed after instantiation + // <script type="dojo/connect" data-dojo-event="foo"> tags are dojo.connected after instantiation + // <script type="dojo/watch" data-dojo-prop="foo"> tags are dojo.watch after instantiation + // <script type="dojo/on" data-dojo-event="foo"> tags are dojo.on after instantiation + // note: dojo/* script tags cannot exist in self closing widgets, like <input /> + var aspects = [], // aspects to connect after instantiation + calls = [], // functions to call after instantiation + watches = [], // functions to watch after instantiation + ons = []; // functions to on after instantiation + + if(scripts){ + for(i=0; i<scripts.length; i++){ + var script = scripts[i]; + node.removeChild(script); + // FIXME: drop event="" support in 2.0. use data-dojo-event="" instead + var event = (script.getAttribute(attrData + "event") || script.getAttribute("event")), + prop = script.getAttribute(attrData + "prop"), + method = script.getAttribute(attrData + "method"), + advice = script.getAttribute(attrData + "advice"), + scriptType = script.getAttribute("type"), + nf = this._functionFromScript(script, attrData); + if(event){ + if(scriptType == "dojo/connect"){ + aspects.push({ method: event, func: nf }); + }else if(scriptType == "dojo/on"){ + ons.push({ event: event, func: nf }); + }else{ + params[event] = nf; + } + }else if(scriptType == "dojo/aspect"){ + aspects.push({ method: method, advice: advice, func: nf }); + }else if(scriptType == "dojo/watch"){ + watches.push({ prop: prop, func: nf }); + }else{ + calls.push(nf); + } + } + } + + // create the instance + var markupFactory = ctor.markupFactory || proto.markupFactory; + var instance = markupFactory ? markupFactory(params, node, ctor) : new ctor(params, node); + + // map it to the JS namespace if that makes sense + if(jsname){ + dlang.setObject(jsname, instance); + } + + // process connections and startup functions + for(i=0; i<aspects.length; i++){ + aspect[aspects[i].advice || "after"](instance, aspects[i].method, dlang.hitch(instance, aspects[i].func), true); + } + for(i=0; i<calls.length; i++){ + calls[i].call(instance); + } + for(i=0; i<watches.length; i++){ + instance.watch(watches[i].prop, watches[i].func); + } + for(i=0; i<ons.length; i++){ + don(instance, ons[i].event, ons[i].func); + } + + return instance; + }, + + scan: function(root, options){ + // summary: + // Scan a DOM tree and return an array of objects representing the DOMNodes + // that need to be turned into widgets. + // description: + // Search specified node (or document root node) recursively for class instances + // and return an array of objects that represent potential widgets to be + // instantiated. Searches for either data-dojo-type="MID" or dojoType="MID" where + // "MID" is a module ID like "dijit/form/Button" or a fully qualified Class name + // like "dijit/form/Button". If the MID is not currently available, scan will + // attempt to require() in the module. + // + // See parser.parse() for details of markup. + // root: DomNode? + // A default starting root node from which to start the parsing. Can be + // omitted, defaulting to the entire document. If omitted, the `options` + // object can be passed in this place. If the `options` object has a + // `rootNode` member, that is used. + // options: Object + // a kwArgs options object, see parse() for details + // + // returns: Promise + // A promise that is resolved with the nodes that have been parsed. + + var list = [], // Output List + mids = [], // An array of modules that are not yet loaded + midsHash = {}; // Used to keep the mids array unique + + var dojoType = (options.scope || dojo._scopeName) + "Type", // typically "dojoType" + attrData = "data-" + (options.scope || dojo._scopeName) + "-", // typically "data-dojo-" + dataDojoType = attrData + "type", // typically "data-dojo-type" + dataDojoTextDir = attrData + "textdir", // typically "data-dojo-textdir" + dataDojoMixins = attrData + "mixins"; // typically "data-dojo-mixins" + + // Info on DOMNode currently being processed + var node = root.firstChild; + + // Info on parent of DOMNode currently being processed + // - inherited: dir, lang, and textDir setting of parent, or inherited by parent + // - parent: pointer to identical structure for my parent (or null if no parent) + // - scripts: if specified, collects <script type="dojo/..."> type nodes from children + var inherited = options.inherited; + if(!inherited){ + function findAncestorAttr(node, attr){ + return (node.getAttribute && node.getAttribute(attr)) || + (node.parentNode && findAncestorAttr(node.parentNode, attr)); + } + inherited = { + dir: findAncestorAttr(root, "dir"), + lang: findAncestorAttr(root, "lang"), + textDir: findAncestorAttr(root, dataDojoTextDir) + }; + for(var key in inherited){ + if(!inherited[key]){ delete inherited[key]; } + } + } + + // Metadata about parent node + var parent = { + inherited: inherited + }; + + // For collecting <script type="dojo/..."> type nodes (when null, we don't need to collect) + var scripts; + + // when true, only look for <script type="dojo/..."> tags, and don't recurse to children + var scriptsOnly; + + function getEffective(parent){ + // summary: + // Get effective dir, lang, textDir settings for specified obj + // (matching "parent" object structure above), and do caching. + // Take care not to return null entries. + if(!parent.inherited){ + parent.inherited = {}; + var node = parent.node, + grandparent = getEffective(parent.parent); + var inherited = { + dir: node.getAttribute("dir") || grandparent.dir, + lang: node.getAttribute("lang") || grandparent.lang, + textDir: node.getAttribute(dataDojoTextDir) || grandparent.textDir + }; + for(var key in inherited){ + if(inherited[key]){ + parent.inherited[key] = inherited[key]; + } + } + } + return parent.inherited; + } + + // DFS on DOM tree, collecting nodes with data-dojo-type specified. + while(true){ + if(!node){ + // Finished this level, continue to parent's next sibling + if(!parent || !parent.node){ + break; + } + node = parent.node.nextSibling; + scriptsOnly = false; + parent = parent.parent; + scripts = parent.scripts; + continue; + } + + if(node.nodeType != 1){ + // Text or comment node, skip to next sibling + node = node.nextSibling; + continue; + } + + if(scripts && node.nodeName.toLowerCase() == "script"){ + // Save <script type="dojo/..."> for parent, then continue to next sibling + type = node.getAttribute("type"); + if(type && /^dojo\/\w/i.test(type)){ + scripts.push(node); + } + node = node.nextSibling; + continue; + } + if(scriptsOnly){ + // scriptsOnly flag is set, we have already collected scripts if the parent wants them, so now we shouldn't + // continue further analysis of the node and will continue to the next sibling + node = node.nextSibling; + continue; + } + + // Check for data-dojo-type attribute, fallback to backward compatible dojoType + // TODO: Remove dojoType in 2.0 + var type = node.getAttribute(dataDojoType) || node.getAttribute(dojoType); + + // Short circuit for leaf nodes containing nothing [but text] + var firstChild = node.firstChild; + if(!type && (!firstChild || (firstChild.nodeType == 3 && !firstChild.nextSibling))){ + node = node.nextSibling; + continue; + } + + // Meta data about current node + var current; + + var ctor = null; + if(type){ + // If dojoType/data-dojo-type specified, add to output array of nodes to instantiate. + var mixinsValue = node.getAttribute(dataDojoMixins), + types = mixinsValue ? [type].concat(mixinsValue.split(/\s*,\s*/)) : [type]; + + // Note: won't find classes declared via dojo/Declaration or any modules that haven't been + // loaded yet so use try/catch to avoid throw from require() + try{ + ctor = getCtor(types); + }catch(e){} + + // If the constructor was not found, check to see if it has modules that can be loaded + if(!ctor){ + darray.forEach(types, function(t){ + if(~t.indexOf('/') && !midsHash[t]){ + // If the type looks like a MID and it currently isn't in the array of MIDs to load, add it. + midsHash[t] = true; + mids[mids.length] = t; + } + }); + } + + var childScripts = ctor && !ctor.prototype._noScript ? [] : null; // <script> nodes that are parent's children + + // Setup meta data about this widget node, and save it to list of nodes to instantiate + current = { + types: types, + ctor: ctor, + parent: parent, + node: node, + scripts: childScripts + }; + current.inherited = getEffective(current); // dir & lang settings for current node, explicit or inherited + list.push(current); + }else{ + // Meta data about this non-widget node + current = { + node: node, + scripts: scripts, + parent: parent + }; + } + + // Recurse, collecting <script type="dojo/..."> children, and also looking for + // descendant nodes with dojoType specified (unless the widget has the stopParser flag). + // When finished with children, go to my next sibling. + node = firstChild; + scripts = childScripts; + scriptsOnly = ctor && ctor.prototype.stopParser && !(options.template); + parent = current; + } + + var d = new Deferred(); + + // If there are modules to load then require them in + if(mids.length){ + // Warn that there are modules being auto-required + if(has("dojo-debug-messages")){ + console.warn("WARNING: Modules being Auto-Required: " + mids.join(", ")); + } + require(mids, function(){ + // Go through list of widget nodes, filling in missing constructors, and filtering out nodes that shouldn't + // be instantiated due to a stopParser flag on an ancestor that we belatedly learned about due to + // auto-require of a module like ContentPane. Assumes list is in DFS order. + d.resolve(darray.filter(list, function(widget){ + if(!widget.ctor){ + // Attempt to find the constructor again. Still won't find classes defined via + // dijit/Declaration so need to try/catch. + try{ + widget.ctor = getCtor(widget.types); + }catch(e){} + } + + // Get the parent widget + var parent = widget.parent; + while(parent && !parent.types){ + parent = parent.parent; + } + + // Return false if this node should be skipped due to stopParser on an ancestor. + // Since list[] is in DFS order, this loop will always set parent.instantiateChildren before + // trying to compute widget.instantiate. + var proto = widget.ctor && widget.ctor.prototype; + widget.instantiateChildren = !(proto && proto.stopParser && !(options.template)); + widget.instantiate = !parent || (parent.instantiate && parent.instantiateChildren); + return widget.instantiate; + })); + }); + }else{ + // There were no modules to load, so just resolve with the parsed nodes. This separate code path is for + // efficiency, to avoid running the require() and the callback code above. + d.resolve(list); + } + + // Return the promise + return d.promise; + }, + + _require: function(/*DOMNode*/ script){ + // summary: + // Helper for _scanAMD(). Takes a `<script type=dojo/require>bar: "acme/bar", ...</script>` node, + // calls require() to load the specified modules and (asynchronously) assign them to the specified global + // variables, and returns a Promise for when that operation completes. + // + // In the example above, it is effectively doing a require(["acme/bar", ...], function(a){ bar = a; }). + + var hash = djson.fromJson("{" + script.innerHTML + "}"), + vars = [], + mids = [], + d = new Deferred(); + + for(var name in hash){ + vars.push(name); + mids.push(hash[name]); + } + + require(mids, function(){ + for(var i=0; i<vars.length; i++){ + dlang.setObject(vars[i], arguments[i]); + } + d.resolve(arguments); + }); + + return d.promise; + }, + + _scanAmd: function(root){ + // summary: + // Scans the DOM for any declarative requires and returns their values. + // description: + // Looks for `<script type=dojo/require>bar: "acme/bar", ...</script>` node, calls require() to load the + // specified modules and (asynchronously) assign them to the specified global variables, + // and returns a Promise for when those operations complete. + // root: DomNode + // The node to base the scan from. + + // Promise that resolves when all the <script type=dojo/require> nodes have finished loading. + var deferred = new Deferred(), + promise = deferred.promise; + deferred.resolve(true); + + var self = this; + query("script[type='dojo/require']", root).forEach(function(node){ + // Fire off require() call for specified modules. Chain this require to fire after + // any previous requires complete, so that layers can be loaded before individual module require()'s fire. + promise = promise.then(function(){ return self._require(node); }); + + // Remove from DOM so it isn't seen again + node.parentNode.removeChild(node); + }); + + return promise; + }, + + parse: function(rootNode, options){ + // summary: + // Scan the DOM for class instances, and instantiate them. + // description: + // Search specified node (or root node) recursively for class instances, + // and instantiate them. Searches for either data-dojo-type="Class" or + // dojoType="Class" where "Class" is a a fully qualified class name, + // like `dijit/form/Button` + // + // Using `data-dojo-type`: + // Attributes using can be mixed into the parameters used to instantiate the + // Class by using a `data-dojo-props` attribute on the node being converted. + // `data-dojo-props` should be a string attribute to be converted from JSON. + // + // Using `dojoType`: + // Attributes are read from the original domNode and converted to appropriate + // types by looking up the Class prototype values. This is the default behavior + // from Dojo 1.0 to Dojo 1.5. `dojoType` support is deprecated, and will + // go away in Dojo 2.0. + // rootNode: DomNode? + // A default starting root node from which to start the parsing. Can be + // omitted, defaulting to the entire document. If omitted, the `options` + // object can be passed in this place. If the `options` object has a + // `rootNode` member, that is used. + // options: Object? + // A hash of options. + // + // - noStart: Boolean?: + // when set will prevent the parser from calling .startup() + // when locating the nodes. + // - rootNode: DomNode?: + // identical to the function's `rootNode` argument, though + // allowed to be passed in via this `options object. + // - template: Boolean: + // If true, ignores ContentPane's stopParser flag and parses contents inside of + // a ContentPane inside of a template. This allows dojoAttachPoint on widgets/nodes + // nested inside the ContentPane to work. + // - inherited: Object: + // Hash possibly containing dir and lang settings to be applied to + // parsed widgets, unless there's another setting on a sub-node that overrides + // - scope: String: + // Root for attribute names to search for. If scopeName is dojo, + // will search for data-dojo-type (or dojoType). For backwards compatibility + // reasons defaults to dojo._scopeName (which is "dojo" except when + // multi-version support is used, when it will be something like dojo16, dojo20, etc.) + // - propsThis: Object: + // If specified, "this" referenced from data-dojo-props will refer to propsThis. + // Intended for use from the widgets-in-template feature of `dijit._WidgetsInTemplateMixin` + // returns: Mixed + // Returns a blended object that is an array of the instantiated objects, but also can include + // a promise that is resolved with the instantiated objects. This is done for backwards + // compatibility. If the parser auto-requires modules, it will always behave in a promise + // fashion and `parser.parse().then(function(instances){...})` should be used. + // example: + // Parse all widgets on a page: + // | parser.parse(); + // example: + // Parse all classes within the node with id="foo" + // | parser.parse(dojo.byId('foo')); + // example: + // Parse all classes in a page, but do not call .startup() on any + // child + // | parser.parse({ noStart: true }) + // example: + // Parse all classes in a node, but do not call .startup() + // | parser.parse(someNode, { noStart:true }); + // | // or + // | parser.parse({ noStart:true, rootNode: someNode }); + + // determine the root node and options based on the passed arguments. + var root; + if(!options && rootNode && rootNode.rootNode){ + options = rootNode; + root = options.rootNode; + }else if(rootNode && dlang.isObject(rootNode) && !("nodeType" in rootNode)){ + options = rootNode; + }else{ + root = rootNode; + } + root = root ? dhtml.byId(root) : dwindow.body(); + + options = options || {}; + + var mixin = options.template ? { template: true } : {}, + instances = [], + self = this; + + // First scan for any <script type=dojo/require> nodes, and execute. + // Then scan for all nodes with data-dojo-type, and load any unloaded modules. + // Then build the object instances. Add instances to already existing (but empty) instances[] array, + // which may already have been returned to caller. Also, use otherwise to collect and throw any errors + // that occur during the parse(). + var p = + this._scanAmd(root, options).then(function(){ + return self.scan(root, options); + }).then(function(parsedNodes){ + return instances = instances.concat(self._instantiate(parsedNodes, mixin, options)); + }).otherwise(function(e){ + // TODO Modify to follow better pattern for promise error managment when available + console.error("dojo/parser::parse() error", e); + throw e; + }); + + // Blend the array with the promise + dlang.mixin(instances, p); + return instances; + } + }; + + if( 1 ){ + dojo.parser = parser; + } + + // Register the parser callback. It should be the first callback + // after the a11y test. + if(config.parseOnLoad){ + ready(100, parser, "parse"); + } + + return parser; +}); |