diff options
Diffstat (limited to 'includes/js/dojo/parser.js')
-rw-r--r-- | includes/js/dojo/parser.js | 277 |
1 files changed, 277 insertions, 0 deletions
diff --git a/includes/js/dojo/parser.js b/includes/js/dojo/parser.js new file mode 100644 index 0000000..5394338 --- /dev/null +++ b/includes/js/dojo/parser.js @@ -0,0 +1,277 @@ +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 +} + +} |