8398c9048d
code was modified slightly, so the code differs from the original downloadable 1.9.5 version
280 lines
8.6 KiB
JavaScript
280 lines
8.6 KiB
JavaScript
dojo.provide("dojo.parser");
|
|
dojo.require("dojo.date.stamp");
|
|
|
|
dojo.parser = new function(){
|
|
// summary: The Dom/Widget parsing package
|
|
|
|
var d = dojo;
|
|
var dtName = d._scopeName + "Type";
|
|
var qry = "[" + dtName + "]";
|
|
|
|
var _anonCtr = 0, _anon = {};
|
|
var nameAnonFunc = function(/*Function*/anonFuncPtr, /*Object*/thisObj){
|
|
// summary:
|
|
// Creates a reference to anonFuncPtr in thisObj with a completely
|
|
// unique name. The new name is returned as a String.
|
|
var nso = thisObj || _anon;
|
|
if(dojo.isIE){
|
|
var cn = anonFuncPtr["__dojoNameCache"];
|
|
if(cn && nso[cn] === anonFuncPtr){
|
|
return cn;
|
|
}
|
|
}
|
|
var name;
|
|
do{
|
|
name = "__" + _anonCtr++;
|
|
}while(name in nso)
|
|
nso[name] = anonFuncPtr;
|
|
return name; // String
|
|
}
|
|
|
|
function val2type(/*Object*/ value){
|
|
// summary:
|
|
// Returns name of type of given value.
|
|
|
|
if(d.isString(value)){ return "string"; }
|
|
if(typeof value == "number"){ return "number"; }
|
|
if(typeof value == "boolean"){ return "boolean"; }
|
|
if(d.isFunction(value)){ return "function"; }
|
|
if(d.isArray(value)){ return "array"; } // typeof [] == "object"
|
|
if(value instanceof Date) { return "date"; } // assume timestamp
|
|
if(value instanceof d._Url){ return "url"; }
|
|
return "object";
|
|
}
|
|
|
|
function str2obj(/*String*/ value, /*String*/ type){
|
|
// summary:
|
|
// Convert given string value to given type
|
|
switch(type){
|
|
case "string":
|
|
return value;
|
|
case "number":
|
|
return value.length ? Number(value) : NaN;
|
|
case "boolean":
|
|
// for checked/disabled value might be "" or "checked". interpret as true.
|
|
return typeof value == "boolean" ? value : !(value.toLowerCase()=="false");
|
|
case "function":
|
|
if(d.isFunction(value)){
|
|
// IE gives us a function, even when we say something like onClick="foo"
|
|
// (in which case it gives us an invalid function "function(){ foo }").
|
|
// Therefore, convert to string
|
|
value=value.toString();
|
|
value=d.trim(value.substring(value.indexOf('{')+1, value.length-1));
|
|
}
|
|
try{
|
|
if(value.search(/[^\w\.]+/i) != -1){
|
|
// TODO: "this" here won't work
|
|
value = nameAnonFunc(new Function(value), this);
|
|
}
|
|
return d.getObject(value, false);
|
|
}catch(e){ return new Function(); }
|
|
case "array":
|
|
return value ? value.split(/\s*,\s*/) : [];
|
|
case "date":
|
|
switch(value){
|
|
case "": return new Date(""); // the NaN of dates
|
|
case "now": return new Date(); // current date
|
|
default: return d.date.stamp.fromISOString(value);
|
|
}
|
|
case "url":
|
|
return d.baseUrl + value;
|
|
default:
|
|
return d.fromJson(value);
|
|
}
|
|
}
|
|
|
|
var instanceClasses = {
|
|
// map from fully qualified name (like "dijit.Button") to structure like
|
|
// { cls: dijit.Button, params: {label: "string", disabled: "boolean"} }
|
|
};
|
|
|
|
function getClassInfo(/*String*/ className){
|
|
// className:
|
|
// fully qualified name (like "dijit.form.Button")
|
|
// returns:
|
|
// structure like
|
|
// {
|
|
// cls: dijit.Button,
|
|
// params: { label: "string", disabled: "boolean"}
|
|
// }
|
|
|
|
if(!instanceClasses[className]){
|
|
// get pointer to widget class
|
|
var cls = d.getObject(className);
|
|
if(!d.isFunction(cls)){
|
|
throw new Error("Could not load class '" + className +
|
|
"'. Did you spell the name correctly and use a full path, like 'dijit.form.Button'?");
|
|
}
|
|
var proto = cls.prototype;
|
|
|
|
// get table of parameter names & types
|
|
var params = {}, dummyClass = {};
|
|
for(var name in proto){
|
|
if(name.charAt(0)=="_"){ continue; } // skip internal properties
|
|
if(name in dummyClass){ continue; } // skip "constructor" and "toString"
|
|
var defVal = proto[name];
|
|
params[name]=val2type(defVal);
|
|
}
|
|
|
|
instanceClasses[className] = { cls: cls, params: params };
|
|
}
|
|
return instanceClasses[className];
|
|
}
|
|
|
|
this._functionFromScript = function(script){
|
|
var preamble = "";
|
|
var suffix = "";
|
|
var argsStr = script.getAttribute("args");
|
|
if(argsStr){
|
|
d.forEach(argsStr.split(/\s*,\s*/), function(part, idx){
|
|
preamble += "var "+part+" = arguments["+idx+"]; ";
|
|
});
|
|
}
|
|
var withStr = script.getAttribute("with");
|
|
if(withStr && withStr.length){
|
|
d.forEach(withStr.split(/\s*,\s*/), function(part){
|
|
preamble += "with("+part+"){";
|
|
suffix += "}";
|
|
});
|
|
}
|
|
return new Function(preamble+script.innerHTML+suffix);
|
|
}
|
|
|
|
this.instantiate = function(/* Array */nodes, /* Object? */mixin){
|
|
// summary:
|
|
// Takes array of nodes, and turns them into class instances and
|
|
// potentially calls a layout method to allow them to connect with
|
|
// any children
|
|
// 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.
|
|
var thelist = [];
|
|
mixin = mixin||{};
|
|
d.forEach(nodes, function(node){
|
|
if(!node){ return; }
|
|
var type = dtName in mixin?mixin[dtName]:node.getAttribute(dtName);
|
|
if(!type || !type.length){ return; }
|
|
var clsInfo = getClassInfo(type),
|
|
clazz = clsInfo.cls,
|
|
ps = clazz._noScript || clazz.prototype._noScript;
|
|
|
|
// read parameters (ie, attributes).
|
|
// clsInfo.params lists expected params like {"checked": "boolean", "n": "number"}
|
|
var params = {},
|
|
attributes = node.attributes;
|
|
for(var name in clsInfo.params){
|
|
var item = name in mixin?{value:mixin[name],specified:true}:attributes.getNamedItem(name);
|
|
if(!item || (!item.specified && (!dojo.isIE || name.toLowerCase()!="value"))){ continue; }
|
|
var value = item.value;
|
|
// Deal with IE quirks for 'class' and 'style'
|
|
switch(name){
|
|
case "class":
|
|
value = "className" in mixin?mixin.className:node.className;
|
|
break;
|
|
case "style":
|
|
value = "style" in mixin?mixin.style:(node.style && node.style.cssText); // FIXME: Opera?
|
|
}
|
|
var _type = clsInfo.params[name];
|
|
if(typeof value == "string"){
|
|
params[name] = str2obj(value, _type);
|
|
}else{
|
|
params[name] = value;
|
|
}
|
|
}
|
|
|
|
// 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" event="foo"> tags are dojo.connected after instantiation
|
|
// note: dojo/* script tags cannot exist in self closing widgets, like <input />
|
|
if(!ps){
|
|
var connects = [], // functions to connect after instantiation
|
|
calls = []; // functions to call after instantiation
|
|
|
|
d.query("> script[type^='dojo/']", node).orphan().forEach(function(script){
|
|
var event = script.getAttribute("event"),
|
|
type = script.getAttribute("type"),
|
|
nf = d.parser._functionFromScript(script);
|
|
if(event){
|
|
if(type == "dojo/connect"){
|
|
connects.push({event: event, func: nf});
|
|
}else{
|
|
params[event] = nf;
|
|
}
|
|
}else{
|
|
calls.push(nf);
|
|
}
|
|
});
|
|
}
|
|
|
|
var markupFactory = clazz["markupFactory"];
|
|
if(!markupFactory && clazz["prototype"]){
|
|
markupFactory = clazz.prototype["markupFactory"];
|
|
}
|
|
// create the instance
|
|
var instance = markupFactory ? markupFactory(params, node, clazz) : new clazz(params, node);
|
|
thelist.push(instance);
|
|
|
|
// map it to the JS namespace if that makes sense
|
|
var jsname = node.getAttribute("jsId");
|
|
if(jsname){
|
|
d.setObject(jsname, instance);
|
|
}
|
|
|
|
// process connections and startup functions
|
|
if(!ps){
|
|
d.forEach(connects, function(connect){
|
|
d.connect(instance, connect.event, null, connect.func);
|
|
});
|
|
d.forEach(calls, function(func){
|
|
func.call(instance);
|
|
});
|
|
}
|
|
});
|
|
|
|
// 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
|
|
d.forEach(thelist, function(instance){
|
|
if( instance &&
|
|
instance.startup &&
|
|
!instance._started &&
|
|
(!instance.getParent || !instance.getParent())
|
|
){
|
|
instance.startup();
|
|
}
|
|
});
|
|
return thelist;
|
|
};
|
|
|
|
this.parse = function(/*DomNode?*/ rootNode){
|
|
// summary:
|
|
// Search specified node (or root node) recursively for class instances,
|
|
// and instantiate them Searches for
|
|
// dojoType="qualified.class.name"
|
|
var list = d.query(qry, rootNode);
|
|
// go build the object instances
|
|
var instances = this.instantiate(list);
|
|
return instances;
|
|
};
|
|
}();
|
|
|
|
//Register the parser callback. It should be the first callback
|
|
//after the a11y test.
|
|
|
|
(function(){
|
|
var parseRunner = function(){
|
|
if(dojo.config["parseOnLoad"] == true){
|
|
dojo.parser.parse();
|
|
}
|
|
};
|
|
|
|
// FIXME: need to clobber cross-dependency!!
|
|
if(dojo.exists("dijit.wai.onload") && (dijit.wai.onload === dojo._loaders[0])){
|
|
dojo._loaders.splice(1, 0, parseRunner);
|
|
}else{
|
|
dojo._loaders.unshift(parseRunner);
|
|
}
|
|
})();
|