SemanticScuttle/includes/js/dojo/parser.js

277 lines
8.5 KiB
JavaScript

if(!dojo._hasResource["dojo.parser"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
dojo._hasResource["dojo.parser"] = true;
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 + "]";
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 = d.parser._nameAnonFunc(new Function(value), this);
}
return d.getObject(value, false);
}catch(e){ return new Function(); }
case "array":
return 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.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={};
for(var name in proto){
if(name.charAt(0)=="_"){ continue; } // skip internal properties
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){
// 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
var thelist = [];
d.forEach(nodes, function(node){
if(!node){ return; }
var type = node.getAttribute(dtName);
if((!type)||(!type.length)){ return; }
var clsInfo = getClassInfo(type);
var clazz = clsInfo.cls;
var ps = clazz._noScript||clazz.prototype._noScript;
// read parameters (ie, attributes).
// clsInfo.params lists expected params like {"checked": "boolean", "n": "number"}
var params = {};
var attributes = node.attributes;
for(var name in clsInfo.params){
var item = 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 = node.className;
break;
case "style":
value = node.style && node.style.cssText; // FIXME: Opera?
}
var _type = clsInfo.params[name];
params[name] = str2obj(value, _type);
}
// 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);
}
})();
//TODO: ported from 0.4.x Dojo. Can we reduce this?
dojo.parser._anonCtr = 0;
dojo.parser._anon = {}; // why is this property required?
dojo.parser._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 jpn = "$joinpoint";
var nso = (thisObj|| dojo.parser._anon);
if(dojo.isIE){
var cn = anonFuncPtr["__dojoNameCache"];
if(cn && nso[cn] === anonFuncPtr){
return anonFuncPtr["__dojoNameCache"];
}
}
var ret = "__"+dojo.parser._anonCtr++;
while(typeof nso[ret] != "undefined"){
ret = "__"+dojo.parser._anonCtr++;
}
nso[ret] = anonFuncPtr;
return ret; // String
}
}