diff options
author | Andrew Dolgov <[email protected]> | 2012-08-14 18:59:10 +0400 |
---|---|---|
committer | Andrew Dolgov <[email protected]> | 2012-08-14 18:59:18 +0400 |
commit | 1354d17270961fff662d40f90521223f8fd0d73b (patch) | |
tree | e9266be71587e47c800303446e968a6d3565e2cf /lib/dojo/parser.js.uncompressed.js | |
parent | d04f8c826f5283765f52cf6b98b42a1ed8f2d6bc (diff) |
update dojo to 1.7.3
Diffstat (limited to 'lib/dojo/parser.js.uncompressed.js')
-rw-r--r-- | lib/dojo/parser.js.uncompressed.js | 594 |
1 files changed, 594 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..6d340fc24 --- /dev/null +++ b/lib/dojo/parser.js.uncompressed.js @@ -0,0 +1,594 @@ +define( + "dojo/parser", ["./_base/kernel", "./_base/lang", "./_base/array", "./_base/html", "./_base/window", "./_base/url", + "./_base/json", "./aspect", "./date/stamp", "./query", "./on", "./ready"], + function(dojo, dlang, darray, dhtml, dwindow, _Url, djson, aspect, dates, query, don){ + +// module: +// dojo/parser +// summary: +// The Dom/Widget parsing package + +new Date("X"); // workaround for #11279, new Date("") == NaN + +var features = { + // Feature detection for when node.attributes only lists the attributes specified in the markup + // rather than old IE/quirks behavior where it lists every default value too + "dom-attributes-explicit": document.createElement("div").attributes.length < 40 +}; +function has(feature){ + return features[feature]; +} + + +dojo.parser = new function(){ + // summary: + // The Dom/Widget parsing package + + var _nameMap = { + // Map from widget name (ex: "dijit.form.Button") to structure mapping + // lowercase version of attribute names to the version in the widget ex: + // { + // label: "label", + // onclick: "onClick" + // } + }; + function getNameMap(proto){ + // summary: + // Returns map from lowercase name to attribute name in class, ex: {onclick: "onClick"} + var map = {}; + for(var name in proto){ + if(name.charAt(0)=="_"){ continue; } // skip internal properties + map[name.toLowerCase()] = name; + } + return map; + } + // 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). + aspect.after(dlang, "extend", function(){ + _nameMap = {}; + }, true); + + // Map from widget name (ex: "dijit.form.Button") to constructor + var _ctorMap = {}; + + this._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 = ""; + var suffix = ""; + var argsStr = (script.getAttribute(attrData + "args") || script.getAttribute("args")); + if(argsStr){ + darray.forEach(argsStr.split(/\s*,\s*/), function(part, idx){ + preamble += "var "+part+" = arguments["+idx+"]; "; + }); + } + var withStr = script.getAttribute("with"); + if(withStr && withStr.length){ + darray.forEach(withStr.split(/\s*,\s*/), function(part){ + preamble += "with("+part+"){"; + suffix += "}"; + }); + } + return new Function(preamble+script.innerHTML+suffix); + }; + + this.instantiate = /*====== dojo.parser.instantiate= ======*/function(nodes, mixin, args){ + // 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 nodes or objects like + // | { + // | type: "dijit.form.Button", + // | 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. + // args: Object? + // An object used to hold kwArgs for instantiation. + // See parse.args argument for details. + + var thelist = [], + mixin = mixin||{}; + args = args||{}; + + // Precompute names of special attributes we are looking for + // TODO: for 2.0 default to data-dojo- regardless of scopeName (or maybe scopeName won't exist in 2.0) + var dojoType = (args.scope || dojo._scopeName) + "Type", // typically "dojoType" + attrData = "data-" + (args.scope || dojo._scopeName) + "-",// typically "data-dojo-" + dataDojoType = attrData + "type", // typically "data-dojo-type" + dataDojoProps = attrData + "props", // typically "data-dojo-props" + dataDojoAttachPoint = attrData + "attach-point", + dataDojoAttachEvent = attrData + "attach-event", + dataDojoId = attrData + "id"; + + // And make hash to quickly check if a given attribute is special, and to map the name to something friendly + var specialAttrs = {}; + darray.forEach([dataDojoProps, dataDojoType, dojoType, dataDojoId, "jsId", dataDojoAttachPoint, + dataDojoAttachEvent, "dojoAttachPoint", "dojoAttachEvent", "class", "style"], function(name){ + specialAttrs[name.toLowerCase()] = name.replace(args.scope, "dojo"); + }); + + darray.forEach(nodes, function(obj){ + if(!obj){ return; } + + var node = obj.node || obj, + type = dojoType in mixin ? mixin[dojoType] : obj.node ? obj.type : (node.getAttribute(dataDojoType) || node.getAttribute(dojoType)), + ctor = _ctorMap[type] || (_ctorMap[type] = dlang.getObject(type)), + proto = ctor && ctor.prototype; + if(!ctor){ + throw new Error("Could not load class '" + type); + } + + // 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(args.defaults){ + // settings for the document itself (or whatever subtree is being parsed) + dlang.mixin(params, args.defaults); + } + if(obj.inherited){ + // settings from dir=rtl or lang=... on a node above this node + dlang.mixin(params, obj.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{ + // Special path for IE, 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]*/, "").replace(/>.*$/, ""); + + 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, + specified: true + }; + }); + } + + // 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; + while(item = attributes[i++]){ + if(!item || !item.specified){ + continue; + } + + var name = item.name, + lcName = name.toLowerCase(), + value = item.value; + + if(lcName in specialAttrs){ + switch(specialAttrs[lcName]){ + + // Data-dojo-props. Save for later to make sure it overrides direct foo=bar settings + case "data-dojo-props": + var extra = value; + break; + + // data-dojo-id or jsId. TODO: drop jsId in 2.0 + case "data-dojo-id": + case "jsId": + var 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; + } + }else{ + // 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 = (_nameMap[type] || (_nameMap[type] = getNameMap(proto))); + 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 function like "myOnClick" + // or a single word function "return" + params[name] = dlang.getObject(value, false) || new Function(value); + } + 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 dojo._Url) ? (dojo.baseUrl + value) : + djson.fromJson(value); + } + }else{ + params[name] = value; + } + } + } + + // Mix things found in data-dojo-props into the params, overriding any direct settings + if(extra){ + try{ + extra = djson.fromJson.call(args.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); + + var scripts = obj.node ? obj.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 connects = [], // functions to connect after instantiation + calls = [], // functions to call after instantiation + watch = [], //functions to watch after instantiation + on = []; //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"), + type = script.getAttribute("type"), + nf = this._functionFromScript(script, attrData); + if(event){ + if(type == "dojo/connect"){ + connects.push({event: event, func: nf}); + }else if(type == "dojo/on"){ + on.push({event: event, func: nf}); + }else{ + params[event] = nf; + } + }else if(type == "dojo/watch"){ + watch.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); + thelist.push(instance); + + // 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<connects.length; i++){ + aspect.after(instance, connects[i].event, dojo.hitch(instance, connects[i].func), true); + } + for(i=0; i<calls.length; i++){ + calls[i].call(instance); + } + for(i=0; i<watch.length; i++){ + instance.watch(watch[i].prop, watch[i].func); + } + for(i=0; i<on.length; i++){ + don(instance, on[i].event, on[i].func); + } + }, 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){ + darray.forEach(thelist, function(instance){ + if( !args.noStart && instance && + dlang.isFunction(instance.startup) && + !instance._started + ){ + instance.startup(); + } + }); + } + return thelist; + }; + + this.parse = /*====== dojo.parser.parse= ======*/ function(rootNode, args){ + // 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 `args` + // object can be passed in this place. If the `args` object has a + // `rootNode` member, that is used. + // + // args: Object + // a kwArgs object passed along to instantiate() + // + // * 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 `args 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` + // + // example: + // Parse all widgets on a page: + // | dojo.parser.parse(); + // + // example: + // Parse all classes within the node with id="foo" + // | dojo.parser.parse(dojo.byId('foo')); + // + // example: + // Parse all classes in a page, but do not call .startup() on any + // child + // | dojo.parser.parse({ noStart: true }) + // + // example: + // Parse all classes in a node, but do not call .startup() + // | dojo.parser.parse(someNode, { noStart:true }); + // | // or + // | dojo.parser.parse({ noStart:true, rootNode: someNode }); + + // determine the root node based on the passed arguments. + var root; + if(!args && rootNode && rootNode.rootNode){ + args = rootNode; + root = args.rootNode; + }else{ + root = rootNode; + } + root = root ? dhtml.byId(root) : dwindow.body(); + args = args || {}; + + var dojoType = (args.scope || dojo._scopeName) + "Type", // typically "dojoType" + attrData = "data-" + (args.scope || dojo._scopeName) + "-", // typically "data-dojo-" + dataDojoType = attrData + "type", // typically "data-dojo-type" + dataDojoTextDir = attrData + "textdir"; // typically "data-dojo-textdir" + + // List of all nodes on page w/dojoType specified + var list = []; + + // 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 = args && args.inherited; + if(!inherited){ + function findAncestorAttr(node, attr){ + return (node.getAttribute && node.getAttribute(attr)) || + (node !== dwindow.doc && node !== dwindow.doc.documentElement && node.parentNode ? findAncestorAttr(node.parentNode, attr) : null); + } + inherited = { + dir: findAncestorAttr(root, "dir"), + lang: findAncestorAttr(root, "lang"), + textDir: findAncestorAttr(root, dataDojoTextDir) + }; + for(var key in inherited){ + if(!inherited[key]){ delete inherited[key]; } + } + } + 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; + scripts = parent.scripts; + scriptsOnly = false; + parent = parent.parent; + 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){ + node = node.nextSibling; + continue; + } + + // Check for data-dojo-type attribute, fallback to backward compatible dojoType + 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; + } + + // Setup data structure to save info on current node for when we return from processing descendant nodes + var current = { + node: node, + scripts: scripts, + parent: parent + }; + + // If dojoType/data-dojo-type specified, add to output array of nodes to instantiate + var ctor = type && (_ctorMap[type] || (_ctorMap[type] = dlang.getObject(type))), // note: won't find classes declared via dojo.Declaration + childScripts = ctor && !ctor.prototype._noScript ? [] : null; // <script> nodes that are parent's children + if(type){ + list.push({ + "type": type, + node: node, + scripts: childScripts, + inherited: getEffective(current) // dir & lang settings for current node, explicit or inherited + }); + } + + // 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 && !(args && args.template); + parent = current; + + } + + // go build the object instances + var mixin = args && args.template ? {template: true} : null; + return this.instantiate(list, mixin, args); // Array + }; +}(); + + +//Register the parser callback. It should be the first callback +//after the a11y test. +if(dojo.config.parseOnLoad){ + dojo.ready(100, dojo.parser, "parse"); +} + +return dojo.parser; +}); |