]>
git.wh0rd.org - tt-rss.git/blob - lib/dojo/parser.js
2 Copyright (c) 2004-2010, The Dojo Foundation All Rights Reserved.
3 Available via Academic Free License >= 2.1 OR the modified BSD license.
4 see: http://dojotoolkit.org/license for details
8 if(!dojo
._hasResource
["dojo.parser"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
9 dojo
._hasResource
["dojo.parser"] = true;
10 dojo
.provide("dojo.parser");
11 dojo
.require("dojo.date.stamp");
13 new Date("X"); // workaround for #11279, new Date("") == NaN
15 dojo
.parser
= new function(){
16 // summary: The Dom/Widget parsing package
19 this._attrName
= d
._scopeName
+ "Type";
20 this._query
= "[" + this._attrName
+ "]";
22 function val2type(/*Object*/ value
){
24 // Returns name of type of given value.
26 if(d
.isString(value
)){ return "string"; }
27 if(typeof value
== "number"){ return "number"; }
28 if(typeof value
== "boolean"){ return "boolean"; }
29 if(d
.isFunction(value
)){ return "function"; }
30 if(d
.isArray(value
)){ return "array"; } // typeof [] == "object"
31 if(value
instanceof Date
) { return "date"; } // assume timestamp
32 if(value
instanceof d
._Url
){ return "url"; }
36 function str2obj(/*String*/ value
, /*String*/ type
){
38 // Convert given string value to given type
43 return value
.length
? Number(value
) : NaN
;
45 // for checked/disabled value might be "" or "checked". interpret as true.
46 return typeof value
== "boolean" ? value
: !(value
.toLowerCase()=="false");
48 if(d
.isFunction(value
)){
49 // IE gives us a function, even when we say something like onClick="foo"
50 // (in which case it gives us an invalid function "function(){ foo }").
51 // Therefore, convert to string
52 value
=value
.toString();
53 value
=d
.trim(value
.substring(value
.indexOf('{')+1, value
.length
-1));
56 if(value
=== "" || value
.search(/[^\w\.]+/i) != -1){
57 // The user has specified some text for a function like "return x+5"
58 return new Function(value
);
60 // The user has specified the name of a function like "myOnClick"
61 // or a single word function "return"
62 return d
.getObject(value
, false) || new Function(value
);
64 }catch(e
){ return new Function(); }
66 return value
? value
.split(/\s*,\s*/) : [];
69 case "": return new Date(""); // the NaN of dates
70 case "now": return new Date(); // current date
71 default: return d
.date
.stamp
.fromISOString(value
);
74 return d
.baseUrl
+ value
;
76 return d
.fromJson(value
);
80 var instanceClasses
= {
81 // map from fully qualified name (like "dijit.Button") to structure like
82 // { cls: dijit.Button, params: {label: "string", disabled: "boolean"} }
85 // Widgets like BorderContainer add properties to _Widget via dojo.extend().
86 // If BorderContainer is loaded after _Widget's parameter list has been cached,
87 // we need to refresh that parameter list (for _Widget and all widgets that extend _Widget).
88 dojo
.connect(dojo
, "extend", function(){
92 function getClassInfo(/*String*/ className
){
94 // fully qualified name (like "dijit.form.Button")
99 // params: { label: "string", disabled: "boolean"}
102 if(!instanceClasses
[className
]){
103 // get pointer to widget class
104 var cls
= d
.getObject(className
);
105 if(!cls
){ return null; } // class not defined [yet]
107 var proto
= cls
.prototype;
109 // get table of parameter names & types
110 var params
= {}, dummyClass
= {};
111 for(var name
in proto
){
112 if(name
.charAt(0)=="_"){ continue; } // skip internal properties
113 if(name
in dummyClass
){ continue; } // skip "constructor" and "toString"
114 var defVal
= proto
[name
];
115 params
[name
]=val2type(defVal
);
118 instanceClasses
[className
] = { cls
: cls
, params
: params
};
120 return instanceClasses
[className
];
123 this._functionFromScript = function(script
){
126 var argsStr
= script
.getAttribute("args");
128 d
.forEach(argsStr
.split(/\s*,\s*/), function(part
, idx
){
129 preamble
+= "var "+part
+" = arguments["+idx
+"]; ";
132 var withStr
= script
.getAttribute("with");
133 if(withStr
&& withStr
.length
){
134 d
.forEach(withStr
.split(/\s*,\s*/), function(part
){
135 preamble
+= "with("+part
+"){";
139 return new Function(preamble
+script
.innerHTML
+suffix
);
142 this.instantiate = function(/* Array */nodes
, /* Object? */mixin
, /* Object? */args
){
144 // Takes array of nodes, and turns them into class instances and
145 // potentially calls a startup method to allow them to connect with
148 // Array of nodes or objects like
150 // | type: "dijit.form.Button",
152 // | scripts: [ ... ], // array of <script type="dojo/..."> children of node
153 // | inherited: { ... } // settings inherited from ancestors like dir, theme, etc.
156 // An object that will be mixed in with each node in the array.
157 // Values in the mixin will override values in the node, if they
160 // An object used to hold kwArgs for instantiation.
161 // Supports 'noStart' and inherited.
162 var thelist
= [], dp
= dojo
.parser
;
166 d
.forEach(nodes
, function(obj
){
169 // Get pointers to DOMNode, dojoType string, and clsInfo (metadata about the dojoType), etc.s
170 var node
, type
, clsInfo
, clazz
, scripts
;
172 // new format of nodes[] array, object w/lots of properties pre-computed for me
175 clsInfo
= obj
.clsInfo
|| (type
&& getClassInfo(type
));
176 clazz
= clsInfo
&& clsInfo
.cls
;
177 scripts
= obj
.scripts
;
179 // old (backwards compatible) format of nodes[] array, simple array of DOMNodes
181 type
= dp
._attrName
in mixin
? mixin
[dp
._attrName
] : node
.getAttribute(dp
._attrName
);
182 clsInfo
= type
&& getClassInfo(type
);
183 clazz
= clsInfo
&& clsInfo
.cls
;
184 scripts
= (clazz
&& (clazz
._noScript
|| clazz
.prototype._noScript
) ? [] :
185 d
.query("> script[type^='dojo/']", node
));
188 throw new Error("Could not load class '" + type
);
191 // Setup hash to hold parameter settings for this widget. Start with the parameter
192 // settings inherited from ancestors ("dir" and "lang").
193 // Inherited setting may later be overridden by explicit settings on node itself.
195 attributes
= node
.attributes
;
197 // settings for the document itself (or whatever subtree is being parsed)
198 dojo
.mixin(params
, args
.defaults
);
201 // settings from dir=rtl or lang=... on a node above this node
202 dojo
.mixin(params
, obj
.inherited
);
205 // read parameters (ie, attributes) specified on DOMNode
206 // clsInfo.params lists expected params like {"checked": "boolean", "n": "number"}
207 for(var name
in clsInfo
.params
){
208 var item
= name
in mixin
?{value
:mixin
[name
],specified
:true}:attributes
.getNamedItem(name
);
209 if(!item
|| (!item
.specified
&& (!dojo
.isIE
|| name
.toLowerCase()!="value"))){ continue; }
210 var value
= item
.value
;
211 // Deal with IE quirks for 'class' and 'style'
214 value
= "className" in mixin
?mixin
.className
:node
.className
;
217 value
= "style" in mixin
?mixin
.style
:(node
.style
&& node
.style
.cssText
); // FIXME: Opera?
219 var _type
= clsInfo
.params
[name
];
220 if(typeof value
== "string"){
221 params
[name
] = str2obj(value
, _type
);
223 params
[name
] = value
;
227 // Process <script type="dojo/*"> script tags
228 // <script type="dojo/method" event="foo"> tags are added to params, and passed to
229 // the widget on instantiation.
230 // <script type="dojo/method"> tags (with no event) are executed after instantiation
231 // <script type="dojo/connect" event="foo"> tags are dojo.connected after instantiation
232 // note: dojo/* script tags cannot exist in self closing widgets, like <input />
233 var connects
= [], // functions to connect after instantiation
234 calls
= []; // functions to call after instantiation
236 d
.forEach(scripts
, function(script
){
237 node
.removeChild(script
);
238 var event
= script
.getAttribute("event"),
239 type
= script
.getAttribute("type"),
240 nf
= d
.parser
._functionFromScript(script
);
242 if(type
== "dojo/connect"){
243 connects
.push({event
: event
, func
: nf
});
252 var markupFactory
= clazz
.markupFactory
|| clazz
.prototype && clazz
.prototype.markupFactory
;
253 // create the instance
254 var instance
= markupFactory
? markupFactory(params
, node
, clazz
) : new clazz(params
, node
);
255 thelist
.push(instance
);
257 // map it to the JS namespace if that makes sense
258 var jsname
= node
.getAttribute("jsId");
260 d
.setObject(jsname
, instance
);
263 // process connections and startup functions
264 d
.forEach(connects
, function(connect
){
265 d
.connect(instance
, connect
.event
, null, connect
.func
);
267 d
.forEach(calls
, function(func
){
272 // Call startup on each top level instance if it makes sense (as for
273 // widgets). Parent widgets will recursively call startup on their
274 // (non-top level) children
276 // TODO: for 2.0, when old instantiate() API is desupported, store parent-child
277 // relationships in the nodes[] array so that no getParent() call is needed.
278 // Note that will require a parse() call from ContentPane setting a param that the
279 // ContentPane is the parent widget (so that the parse doesn't call startup() on the
280 // ContentPane's children)
281 d
.forEach(thelist
, function(instance
){
282 if( !args
.noStart
&& instance
&&
284 !instance
._started
&&
285 (!instance
.getParent
|| !instance
.getParent())
294 this.parse = function(/*DomNode?*/ rootNode
, /* Object? */ args
){
296 // Scan the DOM for class instances, and instantiate them.
299 // Search specified node (or root node) recursively for class instances,
300 // and instantiate them Searches for
301 // dojoType="qualified.class.name"
303 // rootNode: DomNode?
304 // A default starting root node from which to start the parsing. Can be
305 // omitted, defaulting to the entire document. If omitted, the `args`
306 // object can be passed in this place. If the `args` object has a
307 // `rootNode` member, that is used.
310 // a kwArgs object passed along to instantiate()
312 // * noStart: Boolean?
313 // when set will prevent the parser from calling .startup()
314 // when locating the nodes.
315 // * rootNode: DomNode?
316 // identical to the function's `rootNode` argument, though
317 // allowed to be passed in via this `args object.
318 // * inherited: Object
319 // Hash possibly containing dir and lang settings to be applied to
320 // parsed widgets, unless there's another setting on a sub-node that overrides
324 // Parse all widgets on a page:
325 // | dojo.parser.parse();
328 // Parse all classes within the node with id="foo"
329 // | dojo.parser.parse(dojo.byId(foo));
332 // Parse all classes in a page, but do not call .startup() on any
334 // | dojo.parser.parse({ noStart: true })
337 // Parse all classes in a node, but do not call .startup()
338 // | dojo.parser.parse(someNode, { noStart:true });
340 // | dojo.parser.parse({ noStart:true, rootNode: someNode });
342 // determine the root node based on the passed arguments.
344 if(!args
&& rootNode
&& rootNode
.rootNode
){
346 root
= args
.rootNode
;
351 var attrName
= this._attrName
;
352 function scan(parent
, list
){
354 // Parent is an Object representing a DOMNode, with or without a dojoType specified.
355 // Scan parent's children looking for nodes with dojoType specified, storing in list[].
356 // If parent has a dojoType, also collects <script type=dojo/*> children and stores in parent.scripts[].
358 // Object representing the parent node, like
360 // | node: DomNode, // scan children of this node
361 // | inherited: {dir: "rtl"}, // dir/lang setting inherited from above node
363 // | // attributes only set if node has dojoType specified
364 // | scripts: [], // empty array, put <script type=dojo/*> in here
365 // | clsInfo: { cls: dijit.form.Button, ...}
368 // Output array of objects (same format as parent) representing nodes to be turned into widgets
370 // Effective dir and lang settings on parent node, either set directly or inherited from grandparent
371 var inherited
= dojo
.clone(parent
.inherited
);
372 dojo
.forEach(["dir", "lang"], function(name
){
373 var val
= parent
.node
.getAttribute(name
);
375 inherited
[name
] = val
;
379 // if parent is a widget, then search for <script type=dojo/*> tags and put them in scripts[].
380 var scripts
= parent
.scripts
;
382 // unless parent is a widget with the stopParser flag set, continue search for dojoType, recursively
383 var recurse
= !parent
.clsInfo
|| !parent
.clsInfo
.cls
.prototype.stopParser
;
385 // scan parent's children looking for dojoType and <script type=dojo/*>
386 for(var child
= parent
.node
.firstChild
; child
; child
= child
.nextSibling
){
387 if(child
.nodeType
== 1){
388 var type
= recurse
&& child
.getAttribute(attrName
);
390 // if dojoType specified, add to output array of nodes to instantiate
393 clsInfo
: getClassInfo(type
), // note: won't find classes declared via dojo.Declaration
395 scripts
: [], // <script> nodes that are parent's children
396 inherited
: inherited
// dir & lang attributes inherited from parent
400 // Recurse, collecting <script type="dojo/..."> children, and also looking for
401 // descendant nodes with dojoType specified (unless the widget has the stopParser flag),
403 }else if(scripts
&& child
.nodeName
.toLowerCase() == "script"){
404 // if <script type="dojo/...">, save in scripts[]
405 type
= child
.getAttribute("type");
406 if (type
&& /^dojo\//i.test(type
)) {
410 // Recurse, looking for grandchild nodes with dojoType specified
420 // Make list of all nodes on page w/dojoType specified
423 node
: root
? dojo
.byId(root
) : dojo
.body(),
424 inherited
: (args
&& args
.inherited
) || {
425 dir
: dojo
._isBodyLtr() ? "ltr" : "rtl"
429 // go build the object instances
430 return this.instantiate(list
, null, args
); // Array
434 //Register the parser callback. It should be the first callback
435 //after the a11y test.
438 var parseRunner = function(){
439 if(dojo
.config
.parseOnLoad
){
444 // FIXME: need to clobber cross-dependency!!
445 if(dojo
.exists("dijit.wai.onload") && (dijit
.wai
.onload
=== dojo
._loaders
[0])){
446 dojo
._loaders
.splice(1, 0, parseRunner
);
448 dojo
._loaders
.unshift(parseRunner
);