1 if(!dojo._hasResource["dojo.parser"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
2 dojo._hasResource["dojo.parser"] = true;
3 dojo.provide("dojo.parser");
4 dojo.require("dojo.date.stamp");
6 dojo.parser = new function(){
7 // summary: The Dom/Widget parsing package
10 var dtName = d._scopeName + "Type";
11 var qry = "[" + dtName + "]";
13 function val2type(/*Object*/ value){
15 // Returns name of type of given value.
17 if(d.isString(value)){ return "string"; }
18 if(typeof value == "number"){ return "number"; }
19 if(typeof value == "boolean"){ return "boolean"; }
20 if(d.isFunction(value)){ return "function"; }
21 if(d.isArray(value)){ return "array"; } // typeof [] == "object"
22 if(value instanceof Date) { return "date"; } // assume timestamp
23 if(value instanceof d._Url){ return "url"; }
27 function str2obj(/*String*/ value, /*String*/ type){
29 // Convert given string value to given type
34 return value.length ? Number(value) : NaN;
36 // for checked/disabled value might be "" or "checked". interpret as true.
37 return typeof value == "boolean" ? value : !(value.toLowerCase()=="false");
39 if(d.isFunction(value)){
40 // IE gives us a function, even when we say something like onClick="foo"
41 // (in which case it gives us an invalid function "function(){ foo }").
42 // Therefore, convert to string
43 value=value.toString();
44 value=d.trim(value.substring(value.indexOf('{')+1, value.length-1));
47 if(value.search(/[^\w\.]+/i) != -1){
48 // TODO: "this" here won't work
49 value = d.parser._nameAnonFunc(new Function(value), this);
51 return d.getObject(value, false);
52 }catch(e){ return new Function(); }
54 return value.split(/\s*,\s*/);
57 case "": return new Date(""); // the NaN of dates
58 case "now": return new Date(); // current date
59 default: return d.date.stamp.fromISOString(value);
62 return d.baseUrl + value;
64 return d.fromJson(value);
68 var instanceClasses = {
69 // map from fully qualified name (like "dijit.Button") to structure like
70 // { cls: dijit.Button, params: {label: "string", disabled: "boolean"} }
73 function getClassInfo(/*String*/ className){
75 // fully qualified name (like "dijit.Button")
80 // params: { label: "string", disabled: "boolean"}
83 if(!instanceClasses[className]){
84 // get pointer to widget class
85 var cls = d.getObject(className);
86 if(!d.isFunction(cls)){
87 throw new Error("Could not load class '" + className +
88 "'. Did you spell the name correctly and use a full path, like 'dijit.form.Button'?");
90 var proto = cls.prototype;
92 // get table of parameter names & types
94 for(var name in proto){
95 if(name.charAt(0)=="_"){ continue; } // skip internal properties
96 var defVal = proto[name];
97 params[name]=val2type(defVal);
100 instanceClasses[className] = { cls: cls, params: params };
102 return instanceClasses[className];
105 this._functionFromScript = function(script){
108 var argsStr = script.getAttribute("args");
110 d.forEach(argsStr.split(/\s*,\s*/), function(part, idx){
111 preamble += "var "+part+" = arguments["+idx+"]; ";
114 var withStr = script.getAttribute("with");
115 if(withStr && withStr.length){
116 d.forEach(withStr.split(/\s*,\s*/), function(part){
117 preamble += "with("+part+"){";
121 return new Function(preamble+script.innerHTML+suffix);
124 this.instantiate = function(/* Array */nodes){
126 // Takes array of nodes, and turns them into class instances and
127 // potentially calls a layout method to allow them to connect with
130 d.forEach(nodes, function(node){
132 var type = node.getAttribute(dtName);
133 if((!type)||(!type.length)){ return; }
134 var clsInfo = getClassInfo(type);
135 var clazz = clsInfo.cls;
136 var ps = clazz._noScript||clazz.prototype._noScript;
138 // read parameters (ie, attributes).
139 // clsInfo.params lists expected params like {"checked": "boolean", "n": "number"}
141 var attributes = node.attributes;
142 for(var name in clsInfo.params){
143 var item = attributes.getNamedItem(name);
144 if(!item || (!item.specified && (!dojo.isIE || name.toLowerCase()!="value"))){ continue; }
145 var value = item.value;
146 // Deal with IE quirks for 'class' and 'style'
149 value = node.className;
152 value = node.style && node.style.cssText; // FIXME: Opera?
154 var _type = clsInfo.params[name];
155 params[name] = str2obj(value, _type);
158 // Process <script type="dojo/*"> script tags
159 // <script type="dojo/method" event="foo"> tags are added to params, and passed to
160 // the widget on instantiation.
161 // <script type="dojo/method"> tags (with no event) are executed after instantiation
162 // <script type="dojo/connect" event="foo"> tags are dojo.connected after instantiation
163 // note: dojo/* script tags cannot exist in self closing widgets, like <input />
165 var connects = [], // functions to connect after instantiation
166 calls = []; // functions to call after instantiation
168 d.query("> script[type^='dojo/']", node).orphan().forEach(function(script){
169 var event = script.getAttribute("event"),
170 type = script.getAttribute("type"),
171 nf = d.parser._functionFromScript(script);
173 if(type == "dojo/connect"){
174 connects.push({event: event, func: nf});
184 var markupFactory = clazz["markupFactory"];
185 if(!markupFactory && clazz["prototype"]){
186 markupFactory = clazz.prototype["markupFactory"];
188 // create the instance
189 var instance = markupFactory ? markupFactory(params, node, clazz) : new clazz(params, node);
190 thelist.push(instance);
192 // map it to the JS namespace if that makes sense
193 var jsname = node.getAttribute("jsId");
195 d.setObject(jsname, instance);
198 // process connections and startup functions
200 d.forEach(connects, function(connect){
201 d.connect(instance, connect.event, null, connect.func);
203 d.forEach(calls, function(func){
209 // Call startup on each top level instance if it makes sense (as for
210 // widgets). Parent widgets will recursively call startup on their
211 // (non-top level) children
212 d.forEach(thelist, function(instance){
215 !instance._started &&
216 (!instance.getParent || !instance.getParent())
224 this.parse = function(/*DomNode?*/ rootNode){
226 // Search specified node (or root node) recursively for class instances,
227 // and instantiate them Searches for
228 // dojoType="qualified.class.name"
229 var list = d.query(qry, rootNode);
230 // go build the object instances
231 var instances = this.instantiate(list);
236 //Register the parser callback. It should be the first callback
237 //after the a11y test.
240 var parseRunner = function(){
241 if(dojo.config["parseOnLoad"] == true){
246 // FIXME: need to clobber cross-dependency!!
247 if(dojo.exists("dijit.wai.onload") && (dijit.wai.onload === dojo._loaders[0])){
248 dojo._loaders.splice(1, 0, parseRunner);
250 dojo._loaders.unshift(parseRunner);
254 //TODO: ported from 0.4.x Dojo. Can we reduce this?
255 dojo.parser._anonCtr = 0;
256 dojo.parser._anon = {}; // why is this property required?
257 dojo.parser._nameAnonFunc = function(/*Function*/anonFuncPtr, /*Object*/thisObj){
259 // Creates a reference to anonFuncPtr in thisObj with a completely
260 // unique name. The new name is returned as a String.
261 var jpn = "$joinpoint";
262 var nso = (thisObj|| dojo.parser._anon);
264 var cn = anonFuncPtr["__dojoNameCache"];
265 if(cn && nso[cn] === anonFuncPtr){
266 return anonFuncPtr["__dojoNameCache"];
269 var ret = "__"+dojo.parser._anonCtr++;
270 while(typeof nso[ret] != "undefined"){
271 ret = "__"+dojo.parser._anonCtr++;
273 nso[ret] = anonFuncPtr;
274 return ret; // String