]>
git.wh0rd.org - tt-rss.git/blob - lib/dojo/parser.js.uncompressed.js
2 "dojo/parser", ["./_base/kernel", "./_base/lang", "./_base/array", "./_base/html", "./_base/window", "./_base/url",
3 "./_base/json", "./aspect", "./date/stamp", "./query", "./on", "./ready"],
4 function(dojo
, dlang
, darray
, dhtml
, dwindow
, _Url
, djson
, aspect
, dates
, query
, don
){
9 // The Dom/Widget parsing package
11 new Date("X"); // workaround for #11279, new Date("") == NaN
14 // Feature detection for when node.attributes only lists the attributes specified in the markup
15 // rather than old IE/quirks behavior where it lists every default value too
16 "dom-attributes-explicit": document
.createElement("div").attributes
.length
< 40
18 function has(feature
){
19 return features
[feature
];
23 dojo
.parser
= new function(){
25 // The Dom/Widget parsing package
28 // Map from widget name (ex: "dijit.form.Button") to structure mapping
29 // lowercase version of attribute names to the version in the widget ex:
35 function getNameMap(proto
){
37 // Returns map from lowercase name to attribute name in class, ex: {onclick: "onClick"}
39 for(var name
in proto
){
40 if(name
.charAt(0)=="_"){ continue; } // skip internal properties
41 map
[name
.toLowerCase()] = name
;
45 // Widgets like BorderContainer add properties to _Widget via dojo.extend().
46 // If BorderContainer is loaded after _Widget's parameter list has been cached,
47 // we need to refresh that parameter list (for _Widget and all widgets that extend _Widget).
48 aspect
.after(dlang
, "extend", function(){
52 // Map from widget name (ex: "dijit.form.Button") to constructor
55 this._functionFromScript = function(script
, attrData
){
57 // Convert a <script type="dojo/method" args="a, b, c"> ... </script>
60 // The <script> DOMNode
62 // For HTML5 compliance, searches for attrData + "args" (typically
63 // "data-dojo-args") instead of "args"
66 var argsStr
= (script
.getAttribute(attrData
+ "args") || script
.getAttribute("args"));
68 darray
.forEach(argsStr
.split(/\s*,\s*/), function(part
, idx
){
69 preamble
+= "var "+part
+" = arguments["+idx
+"]; ";
72 var withStr
= script
.getAttribute("with");
73 if(withStr
&& withStr
.length
){
74 darray
.forEach(withStr
.split(/\s*,\s*/), function(part
){
75 preamble
+= "with("+part
+"){";
79 return new Function(preamble
+script
.innerHTML
+suffix
);
82 this.instantiate
= /*====== dojo.parser.instantiate= ======*/function(nodes
, mixin
, args
){
84 // Takes array of nodes, and turns them into class instances and
85 // potentially calls a startup method to allow them to connect with
88 // Array of nodes or objects like
90 // | type: "dijit.form.Button",
92 // | scripts: [ ... ], // array of <script type="dojo/..."> children of node
93 // | inherited: { ... } // settings inherited from ancestors like dir, theme, etc.
96 // An object that will be mixed in with each node in the array.
97 // Values in the mixin will override values in the node, if they
100 // An object used to hold kwArgs for instantiation.
101 // See parse.args argument for details.
107 // Precompute names of special attributes we are looking for
108 // TODO: for 2.0 default to data-dojo- regardless of scopeName (or maybe scopeName won't exist in 2.0)
109 var dojoType
= (args
.scope
|| dojo
._scopeName
) + "Type", // typically "dojoType"
110 attrData
= "data-" + (args
.scope
|| dojo
._scopeName
) + "-",// typically "data-dojo-"
111 dataDojoType
= attrData
+ "type", // typically "data-dojo-type"
112 dataDojoProps
= attrData
+ "props", // typically "data-dojo-props"
113 dataDojoAttachPoint
= attrData
+ "attach-point",
114 dataDojoAttachEvent
= attrData
+ "attach-event",
115 dataDojoId
= attrData
+ "id";
117 // And make hash to quickly check if a given attribute is special, and to map the name to something friendly
118 var specialAttrs
= {};
119 darray
.forEach([dataDojoProps
, dataDojoType
, dojoType
, dataDojoId
, "jsId", dataDojoAttachPoint
,
120 dataDojoAttachEvent
, "dojoAttachPoint", "dojoAttachEvent", "class", "style"], function(name
){
121 specialAttrs
[name
.toLowerCase()] = name
.replace(args
.scope
, "dojo");
124 darray
.forEach(nodes
, function(obj
){
127 var node
= obj
.node
|| obj
,
128 type
= dojoType
in mixin
? mixin
[dojoType
] : obj
.node
? obj
.type
: (node
.getAttribute(dataDojoType
) || node
.getAttribute(dojoType
)),
129 ctor
= _ctorMap
[type
] || (_ctorMap
[type
] = dlang
.getObject(type
)),
130 proto
= ctor
&& ctor
.prototype;
132 throw new Error("Could not load class '" + type
);
135 // Setup hash to hold parameter settings for this widget. Start with the parameter
136 // settings inherited from ancestors ("dir" and "lang").
137 // Inherited setting may later be overridden by explicit settings on node itself.
141 // settings for the document itself (or whatever subtree is being parsed)
142 dlang
.mixin(params
, args
.defaults
);
145 // settings from dir=rtl or lang=... on a node above this node
146 dlang
.mixin(params
, obj
.inherited
);
149 // Get list of attributes explicitly listed in the markup
151 if(has("dom-attributes-explicit")){
152 // Standard path to get list of user specified attributes
153 attributes
= node
.attributes
;
155 // Special path for IE, avoid (sometimes >100) bogus entries in node.attributes
156 var clone
= /^input$|^img$/i.test(node
.nodeName
) ? node
: node
.cloneNode(false),
157 attrs
= clone
.outerHTML
.replace(/=[^\s"']+|="[^"]*"|='[^']*'/g, "").replace(/^\s*<[a-zA-Z0-9]*/, "").replace(/>.*$/, "");
159 attributes
= darray
.map(attrs
.split(/\s+/), function(name
){
160 var lcName
= name
.toLowerCase();
163 // getAttribute() doesn't work for button.value, returns innerHTML of button.
164 // but getAttributeNode().value doesn't work for the form.encType or li.value
165 value
: (node
.nodeName
== "LI" && name
== "value") || lcName
== "enctype" ?
166 node
.getAttribute(lcName
) : node
.getAttributeNode(lcName
).value
,
172 // Read in attributes and process them, including data-dojo-props, data-dojo-type,
173 // dojoAttachPoint, etc., as well as normal foo=bar attributes.
175 while(item
= attributes
[i
++]){
176 if(!item
|| !item
.specified
){
180 var name
= item
.name
,
181 lcName
= name
.toLowerCase(),
184 if(lcName
in specialAttrs
){
185 switch(specialAttrs
[lcName
]){
187 // Data-dojo-props. Save for later to make sure it overrides direct foo=bar settings
188 case "data-dojo-props":
192 // data-dojo-id or jsId. TODO: drop jsId in 2.0
198 // For the benefit of _Templated
199 case "data-dojo-attach-point":
200 case "dojoAttachPoint":
201 params
.dojoAttachPoint
= value
;
203 case "data-dojo-attach-event":
204 case "dojoAttachEvent":
205 params
.dojoAttachEvent
= value
;
208 // Special parameter handling needed for IE
210 params
["class"] = node
.className
;
213 params
["style"] = node
.style
&& node
.style
.cssText
;
217 // Normal attribute, ex: value="123"
219 // Find attribute in widget corresponding to specified name.
220 // May involve case conversion, ex: onclick --> onClick
221 if(!(name
in proto
)){
222 var map
= (_nameMap
[type
] || (_nameMap
[type
] = getNameMap(proto
)));
223 name
= map
[lcName
] || name
;
226 // Set params[name] to value, doing type conversion
228 switch(typeof proto
[name
]){
230 params
[name
] = value
;
233 params
[name
] = value
.length
? Number(value
) : NaN
;
236 // for checked/disabled value might be "" or "checked". interpret as true.
237 params
[name
] = value
.toLowerCase() != "false";
240 if(value
=== "" || value
.search(/[^\w\.]+/i) != -1){
241 // The user has specified some text for a function like "return x+5"
242 params
[name
] = new Function(value
);
244 // The user has specified the name of a function like "myOnClick"
245 // or a single word function "return"
246 params
[name
] = dlang
.getObject(value
, false) || new Function(value
);
250 var pVal
= proto
[name
];
252 (pVal
&& "length" in pVal
) ? (value
? value
.split(/\s*,\s*/) : []) : // array
253 (pVal
instanceof Date
) ?
254 (value
== "" ? new Date("") : // the NaN of dates
255 value
== "now" ? new Date() : // current date
256 dates
.fromISOString(value
)) :
257 (pVal
instanceof dojo
._Url
) ? (dojo
.baseUrl
+ value
) :
258 djson
.fromJson(value
);
261 params
[name
] = value
;
266 // Mix things found in data-dojo-props into the params, overriding any direct settings
269 extra
= djson
.fromJson
.call(args
.propsThis
, "{" + extra
+ "}");
270 dlang
.mixin(params
, extra
);
272 // give the user a pointer to their invalid parameters. FIXME: can we kill this in production?
273 throw new Error(e
.toString() + " in data-dojo-props='" + extra
+ "'");
277 // Any parameters specified in "mixin" override everything else.
278 dlang
.mixin(params
, mixin
);
280 var scripts
= obj
.node
? obj
.scripts
: (ctor
&& (ctor
._noScript
|| proto
._noScript
) ? [] :
281 query("> script[type^='dojo/']", node
));
283 // Process <script type="dojo/*"> script tags
284 // <script type="dojo/method" event="foo"> tags are added to params, and passed to
285 // the widget on instantiation.
286 // <script type="dojo/method"> tags (with no event) are executed after instantiation
287 // <script type="dojo/connect" data-dojo-event="foo"> tags are dojo.connected after instantiation
288 // <script type="dojo/watch" data-dojo-prop="foo"> tags are dojo.watch after instantiation
289 // <script type="dojo/on" data-dojo-event="foo"> tags are dojo.on after instantiation
290 // note: dojo/* script tags cannot exist in self closing widgets, like <input />
291 var connects
= [], // functions to connect after instantiation
292 calls
= [], // functions to call after instantiation
293 watch
= [], //functions to watch after instantiation
294 on
= []; //functions to on after instantiation
297 for(i
=0; i
<scripts
.length
; i
++){
298 var script
= scripts
[i
];
299 node
.removeChild(script
);
300 // FIXME: drop event="" support in 2.0. use data-dojo-event="" instead
301 var event
= (script
.getAttribute(attrData
+ "event") || script
.getAttribute("event")),
302 prop
= script
.getAttribute(attrData
+ "prop"),
303 type
= script
.getAttribute("type"),
304 nf
= this._functionFromScript(script
, attrData
);
306 if(type
== "dojo/connect"){
307 connects
.push({event
: event
, func
: nf
});
308 }else if(type
== "dojo/on"){
309 on
.push({event
: event
, func
: nf
});
313 }else if(type
== "dojo/watch"){
314 watch
.push({prop
: prop
, func
: nf
});
321 // create the instance
322 var markupFactory
= ctor
.markupFactory
|| proto
.markupFactory
;
323 var instance
= markupFactory
? markupFactory(params
, node
, ctor
) : new ctor(params
, node
);
324 thelist
.push(instance
);
326 // map it to the JS namespace if that makes sense
328 dlang
.setObject(jsname
, instance
);
331 // process connections and startup functions
332 for(i
=0; i
<connects
.length
; i
++){
333 aspect
.after(instance
, connects
[i
].event
, dojo
.hitch(instance
, connects
[i
].func
), true);
335 for(i
=0; i
<calls
.length
; i
++){
336 calls
[i
].call(instance
);
338 for(i
=0; i
<watch
.length
; i
++){
339 instance
.watch(watch
[i
].prop
, watch
[i
].func
);
341 for(i
=0; i
<on
.length
; i
++){
342 don(instance
, on
[i
].event
, on
[i
].func
);
346 // Call startup on each top level instance if it makes sense (as for
347 // widgets). Parent widgets will recursively call startup on their
348 // (non-top level) children
350 darray
.forEach(thelist
, function(instance
){
351 if( !args
.noStart
&& instance
&&
352 dlang
.isFunction(instance
.startup
) &&
362 this.parse
= /*====== dojo.parser.parse= ======*/ function(rootNode
, args
){
364 // Scan the DOM for class instances, and instantiate them.
367 // Search specified node (or root node) recursively for class instances,
368 // and instantiate them. Searches for either data-dojo-type="Class" or
369 // dojoType="Class" where "Class" is a a fully qualified class name,
370 // like `dijit.form.Button`
372 // Using `data-dojo-type`:
373 // Attributes using can be mixed into the parameters used to instantiate the
374 // Class by using a `data-dojo-props` attribute on the node being converted.
375 // `data-dojo-props` should be a string attribute to be converted from JSON.
378 // Attributes are read from the original domNode and converted to appropriate
379 // types by looking up the Class prototype values. This is the default behavior
380 // from Dojo 1.0 to Dojo 1.5. `dojoType` support is deprecated, and will
381 // go away in Dojo 2.0.
383 // rootNode: DomNode?
384 // A default starting root node from which to start the parsing. Can be
385 // omitted, defaulting to the entire document. If omitted, the `args`
386 // object can be passed in this place. If the `args` object has a
387 // `rootNode` member, that is used.
390 // a kwArgs object passed along to instantiate()
392 // * noStart: Boolean?
393 // when set will prevent the parser from calling .startup()
394 // when locating the nodes.
395 // * rootNode: DomNode?
396 // identical to the function's `rootNode` argument, though
397 // allowed to be passed in via this `args object.
398 // * template: Boolean
399 // If true, ignores ContentPane's stopParser flag and parses contents inside of
400 // a ContentPane inside of a template. This allows dojoAttachPoint on widgets/nodes
401 // nested inside the ContentPane to work.
402 // * inherited: Object
403 // Hash possibly containing dir and lang settings to be applied to
404 // parsed widgets, unless there's another setting on a sub-node that overrides
406 // Root for attribute names to search for. If scopeName is dojo,
407 // will search for data-dojo-type (or dojoType). For backwards compatibility
408 // reasons defaults to dojo._scopeName (which is "dojo" except when
409 // multi-version support is used, when it will be something like dojo16, dojo20, etc.)
410 // * propsThis: Object
411 // If specified, "this" referenced from data-dojo-props will refer to propsThis.
412 // Intended for use from the widgets-in-template feature of `dijit._WidgetsInTemplateMixin`
415 // Parse all widgets on a page:
416 // | dojo.parser.parse();
419 // Parse all classes within the node with id="foo"
420 // | dojo.parser.parse(dojo.byId('foo'));
423 // Parse all classes in a page, but do not call .startup() on any
425 // | dojo.parser.parse({ noStart: true })
428 // Parse all classes in a node, but do not call .startup()
429 // | dojo.parser.parse(someNode, { noStart:true });
431 // | dojo.parser.parse({ noStart:true, rootNode: someNode });
433 // determine the root node based on the passed arguments.
435 if(!args
&& rootNode
&& rootNode
.rootNode
){
437 root
= args
.rootNode
;
441 root
= root
? dhtml
.byId(root
) : dwindow
.body();
444 var dojoType
= (args
.scope
|| dojo
._scopeName
) + "Type", // typically "dojoType"
445 attrData
= "data-" + (args
.scope
|| dojo
._scopeName
) + "-", // typically "data-dojo-"
446 dataDojoType
= attrData
+ "type", // typically "data-dojo-type"
447 dataDojoTextDir
= attrData
+ "textdir"; // typically "data-dojo-textdir"
449 // List of all nodes on page w/dojoType specified
452 // Info on DOMNode currently being processed
453 var node
= root
.firstChild
;
455 // Info on parent of DOMNode currently being processed
456 // - inherited: dir, lang, and textDir setting of parent, or inherited by parent
457 // - parent: pointer to identical structure for my parent (or null if no parent)
458 // - scripts: if specified, collects <script type="dojo/..."> type nodes from children
459 var inherited
= args
&& args
.inherited
;
461 function findAncestorAttr(node
, attr
){
462 return (node
.getAttribute
&& node
.getAttribute(attr
)) ||
463 (node
!== dwindow
.doc
&& node
!== dwindow
.doc
.documentElement
&& node
.parentNode
? findAncestorAttr(node
.parentNode
, attr
) : null);
466 dir
: findAncestorAttr(root
, "dir"),
467 lang
: findAncestorAttr(root
, "lang"),
468 textDir
: findAncestorAttr(root
, dataDojoTextDir
)
470 for(var key
in inherited
){
471 if(!inherited
[key
]){ delete inherited
[key
]; }
478 // For collecting <script type="dojo/..."> type nodes (when null, we don't need to collect)
481 // when true, only look for <script type="dojo/..."> tags, and don't recurse to children
484 function getEffective(parent
){
486 // Get effective dir, lang, textDir settings for specified obj
487 // (matching "parent" object structure above), and do caching.
488 // Take care not to return null entries.
489 if(!parent
.inherited
){
490 parent
.inherited
= {};
491 var node
= parent
.node
,
492 grandparent
= getEffective(parent
.parent
);
494 dir
: node
.getAttribute("dir") || grandparent
.dir
,
495 lang
: node
.getAttribute("lang") || grandparent
.lang
,
496 textDir
: node
.getAttribute(dataDojoTextDir
) || grandparent
.textDir
498 for(var key
in inherited
){
500 parent
.inherited
[key
] = inherited
[key
];
504 return parent
.inherited
;
507 // DFS on DOM tree, collecting nodes with data-dojo-type specified.
510 // Finished this level, continue to parent's next sibling
511 if(!parent
|| !parent
.node
){
514 node
= parent
.node
.nextSibling
;
515 scripts
= parent
.scripts
;
517 parent
= parent
.parent
;
521 if(node
.nodeType
!= 1){
522 // Text or comment node, skip to next sibling
523 node
= node
.nextSibling
;
527 if(scripts
&& node
.nodeName
.toLowerCase() == "script"){
528 // Save <script type="dojo/..."> for parent, then continue to next sibling
529 type
= node
.getAttribute("type");
530 if(type
&& /^dojo\/\w/i.test(type
)){
533 node
= node
.nextSibling
;
537 node
= node
.nextSibling
;
541 // Check for data-dojo-type attribute, fallback to backward compatible dojoType
542 var type
= node
.getAttribute(dataDojoType
) || node
.getAttribute(dojoType
);
544 // Short circuit for leaf nodes containing nothing [but text]
545 var firstChild
= node
.firstChild
;
546 if(!type
&& (!firstChild
|| (firstChild
.nodeType
== 3 && !firstChild
.nextSibling
))){
547 node
= node
.nextSibling
;
551 // Setup data structure to save info on current node for when we return from processing descendant nodes
558 // If dojoType/data-dojo-type specified, add to output array of nodes to instantiate
559 var ctor
= type
&& (_ctorMap
[type
] || (_ctorMap
[type
] = dlang
.getObject(type
))), // note: won't find classes declared via dojo.Declaration
560 childScripts
= ctor
&& !ctor
.prototype._noScript
? [] : null; // <script> nodes that are parent's children
565 scripts
: childScripts
,
566 inherited
: getEffective(current
) // dir & lang settings for current node, explicit or inherited
570 // Recurse, collecting <script type="dojo/..."> children, and also looking for
571 // descendant nodes with dojoType specified (unless the widget has the stopParser flag).
572 // When finished with children, go to my next sibling.
574 scripts
= childScripts
;
575 scriptsOnly
= ctor
&& ctor
.prototype.stopParser
&& !(args
&& args
.template
);
580 // go build the object instances
581 var mixin
= args
&& args
.template
? {template
: true} : null;
582 return this.instantiate(list
, mixin
, args
); // Array
587 //Register the parser callback. It should be the first callback
588 //after the a11y test.
589 if(dojo
.config
.parseOnLoad
){
590 dojo
.ready(100, dojo
.parser
, "parse");