summaryrefslogtreecommitdiff
path: root/lib/dojo/parser.js
diff options
context:
space:
mode:
authorAndrew Dolgov <[email protected]>2011-03-04 19:02:28 +0300
committerAndrew Dolgov <[email protected]>2011-03-04 19:02:59 +0300
commita089699c8915636ba4f158d77dba9b012bc93208 (patch)
treeb2d7d051f1f55d44a6be07d3ee137e5a7ccfcefb /lib/dojo/parser.js
parentcfad9259a6feacfa8194b1312770ae6db1ecce50 (diff)
build custom layer of Dojo to speed up loading of tt-rss (refs #293)
Diffstat (limited to 'lib/dojo/parser.js')
-rw-r--r--lib/dojo/parser.js698
1 files changed, 439 insertions, 259 deletions
diff --git a/lib/dojo/parser.js b/lib/dojo/parser.js
index 245528328..7ae035691 100644
--- a/lib/dojo/parser.js
+++ b/lib/dojo/parser.js
@@ -5,268 +5,448 @@
*/
-if(!dojo._hasResource["dojo.parser"]){
-dojo._hasResource["dojo.parser"]=true;
+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");
-new Date("X");
-dojo.parser=new function(){
-var d=dojo;
-this._attrName=d._scopeName+"Type";
-this._query="["+this._attrName+"]";
-function _1(_2){
-if(d.isString(_2)){
-return "string";
-}
-if(typeof _2=="number"){
-return "number";
-}
-if(typeof _2=="boolean"){
-return "boolean";
-}
-if(d.isFunction(_2)){
-return "function";
-}
-if(d.isArray(_2)){
-return "array";
-}
-if(_2 instanceof Date){
-return "date";
-}
-if(_2 instanceof d._Url){
-return "url";
-}
-return "object";
-};
-function _3(_4,_5){
-switch(_5){
-case "string":
-return _4;
-case "number":
-return _4.length?Number(_4):NaN;
-case "boolean":
-return typeof _4=="boolean"?_4:!(_4.toLowerCase()=="false");
-case "function":
-if(d.isFunction(_4)){
-_4=_4.toString();
-_4=d.trim(_4.substring(_4.indexOf("{")+1,_4.length-1));
-}
-try{
-if(_4===""||_4.search(/[^\w\.]+/i)!=-1){
-return new Function(_4);
-}else{
-return d.getObject(_4,false)||new Function(_4);
-}
-}
-catch(e){
-return new Function();
-}
-case "array":
-return _4?_4.split(/\s*,\s*/):[];
-case "date":
-switch(_4){
-case "":
-return new Date("");
-case "now":
-return new Date();
-default:
-return d.date.stamp.fromISOString(_4);
-}
-case "url":
-return d.baseUrl+_4;
-default:
-return d.fromJson(_4);
-}
-};
-var _6={};
-dojo.connect(dojo,"extend",function(){
-_6={};
-});
-function _7(_8){
-if(!_6[_8]){
-var _9=d.getObject(_8);
-if(!_9){
-return null;
-}
-var _a=_9.prototype;
-var _b={},_c={};
-for(var _d in _a){
-if(_d.charAt(0)=="_"){
-continue;
-}
-if(_d in _c){
-continue;
-}
-var _e=_a[_d];
-_b[_d]=_1(_e);
-}
-_6[_8]={cls:_9,params:_b};
-}
-return _6[_8];
-};
-this._functionFromScript=function(_f){
-var _10="";
-var _11="";
-var _12=_f.getAttribute("args");
-if(_12){
-d.forEach(_12.split(/\s*,\s*/),function(_13,idx){
-_10+="var "+_13+" = arguments["+idx+"]; ";
-});
-}
-var _14=_f.getAttribute("with");
-if(_14&&_14.length){
-d.forEach(_14.split(/\s*,\s*/),function(_15){
-_10+="with("+_15+"){";
-_11+="}";
-});
-}
-return new Function(_10+_f.innerHTML+_11);
-};
-this.instantiate=function(_16,_17,_18){
-var _19=[],dp=dojo.parser;
-_17=_17||{};
-_18=_18||{};
-d.forEach(_16,function(obj){
-if(!obj){
-return;
-}
-var _1a,_1b,_1c,_1d,_1e;
-if(obj.node){
-_1a=obj.node;
-_1b=obj.type;
-_1c=obj.clsInfo||(_1b&&_7(_1b));
-_1d=_1c&&_1c.cls;
-_1e=obj.scripts;
-}else{
-_1a=obj;
-_1b=dp._attrName in _17?_17[dp._attrName]:_1a.getAttribute(dp._attrName);
-_1c=_1b&&_7(_1b);
-_1d=_1c&&_1c.cls;
-_1e=(_1d&&(_1d._noScript||_1d.prototype._noScript)?[]:d.query("> script[type^='dojo/']",_1a));
-}
-if(!_1c){
-throw new Error("Could not load class '"+_1b);
-}
-var _1f={},_20=_1a.attributes;
-if(_18.defaults){
-dojo.mixin(_1f,_18.defaults);
-}
-if(obj.inherited){
-dojo.mixin(_1f,obj.inherited);
-}
-for(var _21 in _1c.params){
-var _22=_21 in _17?{value:_17[_21],specified:true}:_20.getNamedItem(_21);
-if(!_22||(!_22.specified&&(!dojo.isIE||_21.toLowerCase()!="value"))){
-continue;
-}
-var _23=_22.value;
-switch(_21){
-case "class":
-_23="className" in _17?_17.className:_1a.className;
-break;
-case "style":
-_23="style" in _17?_17.style:(_1a.style&&_1a.style.cssText);
-}
-var _24=_1c.params[_21];
-if(typeof _23=="string"){
-_1f[_21]=_3(_23,_24);
-}else{
-_1f[_21]=_23;
-}
-}
-var _25=[],_26=[];
-d.forEach(_1e,function(_27){
-_1a.removeChild(_27);
-var _28=_27.getAttribute("event"),_1b=_27.getAttribute("type"),nf=d.parser._functionFromScript(_27);
-if(_28){
-if(_1b=="dojo/connect"){
-_25.push({event:_28,func:nf});
-}else{
-_1f[_28]=nf;
-}
-}else{
-_26.push(nf);
-}
-});
-var _29=_1d.markupFactory||_1d.prototype&&_1d.prototype.markupFactory;
-var _2a=_29?_29(_1f,_1a,_1d):new _1d(_1f,_1a);
-_19.push(_2a);
-var _2b=_1a.getAttribute("jsId");
-if(_2b){
-d.setObject(_2b,_2a);
-}
-d.forEach(_25,function(_2c){
-d.connect(_2a,_2c.event,null,_2c.func);
-});
-d.forEach(_26,function(_2d){
-_2d.call(_2a);
-});
-});
-if(!_17._started){
-d.forEach(_19,function(_2e){
-if(!_18.noStart&&_2e&&_2e.startup&&!_2e._started&&(!_2e.getParent||!_2e.getParent())){
-_2e.startup();
-}
-});
-}
-return _19;
-};
-this.parse=function(_2f,_30){
-var _31;
-if(!_30&&_2f&&_2f.rootNode){
-_30=_2f;
-_31=_30.rootNode;
-}else{
-_31=_2f;
-}
-var _32=this._attrName;
-function _33(_34,_35){
-var _36=dojo.clone(_34.inherited);
-dojo.forEach(["dir","lang"],function(_37){
-var val=_34.node.getAttribute(_37);
-if(val){
-_36[_37]=val;
-}
-});
-var _38=_34.scripts;
-var _39=!_34.clsInfo||!_34.clsInfo.cls.prototype.stopParser;
-for(var _3a=_34.node.firstChild;_3a;_3a=_3a.nextSibling){
-if(_3a.nodeType==1){
-var _3b=_39&&_3a.getAttribute(_32);
-if(_3b){
-var _3c={"type":_3b,clsInfo:_7(_3b),node:_3a,scripts:[],inherited:_36};
-_35.push(_3c);
-_33(_3c,_35);
-}else{
-if(_38&&_3a.nodeName.toLowerCase()=="script"){
-_3b=_3a.getAttribute("type");
-if(_3b&&/^dojo\//i.test(_3b)){
-_38.push(_3a);
-}
-}else{
-if(_39){
-_33({node:_3a,inherited:_36},_35);
-}
-}
-}
-}
-}
-};
-var _3d=[];
-_33({node:_31?dojo.byId(_31):dojo.body(),inherited:(_30&&_30.inherited)||{dir:dojo._isBodyLtr()?"ltr":"rtl"}},_3d);
-return this.instantiate(_3d,null,_30);
-};
+
+new Date("X"); // workaround for #11279, new Date("") == NaN
+
+dojo.parser = new function(){
+ // summary: The Dom/Widget parsing package
+
+ var d = dojo;
+ this._attrName = d._scopeName + "Type";
+ this._query = "[" + this._attrName + "]";
+
+ 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 === "" || value.search(/[^\w\.]+/i) != -1){
+ // The user has specified some text for a function like "return x+5"
+ return new Function(value);
+ }else{
+ // The user has specified the name of a function like "myOnClick"
+ // or a single word function "return"
+ return d.getObject(value, false) || new Function(value);
+ }
+ }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"} }
+ };
+
+ // Widgets like BorderContainer add properties to _Widget via dojo.extend().
+ // If BorderContainer is loaded after _Widget's parameter list has been cached,
+ // we need to refresh that parameter list (for _Widget and all widgets that extend _Widget).
+ dojo.connect(dojo, "extend", function(){
+ instanceClasses = {};
+ });
+
+ 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(!cls){ return null; } // class not defined [yet]
+
+ 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, /* Object? */args){
+ // summary:
+ // Takes array of nodes, and turns them into class instances and
+ // potentially calls a startup method to allow them to connect with
+ // any children.
+ // nodes: Array
+ // Array of nodes or objects like
+ // | {
+ // | type: "dijit.form.Button",
+ // | node: DOMNode,
+ // | scripts: [ ... ], // array of <script type="dojo/..."> children of node
+ // | inherited: { ... } // settings inherited from ancestors like dir, theme, etc.
+ // | }
+ // 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.
+ // args: Object?
+ // An object used to hold kwArgs for instantiation.
+ // Supports 'noStart' and inherited.
+ var thelist = [], dp = dojo.parser;
+ mixin = mixin||{};
+ args = args||{};
+
+ d.forEach(nodes, function(obj){
+ if(!obj){ return; }
+
+ // Get pointers to DOMNode, dojoType string, and clsInfo (metadata about the dojoType), etc.s
+ var node, type, clsInfo, clazz, scripts;
+ if(obj.node){
+ // new format of nodes[] array, object w/lots of properties pre-computed for me
+ node = obj.node;
+ type = obj.type;
+ clsInfo = obj.clsInfo || (type && getClassInfo(type));
+ clazz = clsInfo && clsInfo.cls;
+ scripts = obj.scripts;
+ }else{
+ // old (backwards compatible) format of nodes[] array, simple array of DOMNodes
+ node = obj;
+ type = dp._attrName in mixin ? mixin[dp._attrName] : node.getAttribute(dp._attrName);
+ clsInfo = type && getClassInfo(type);
+ clazz = clsInfo && clsInfo.cls;
+ scripts = (clazz && (clazz._noScript || clazz.prototype._noScript) ? [] :
+ d.query("> script[type^='dojo/']", node));
+ }
+ if(!clsInfo){
+ throw new Error("Could not load class '" + type);
+ }
+
+ // Setup hash to hold parameter settings for this widget. Start with the parameter
+ // settings inherited from ancestors ("dir" and "lang").
+ // Inherited setting may later be overridden by explicit settings on node itself.
+ var params = {},
+ attributes = node.attributes;
+ if(args.defaults){
+ // settings for the document itself (or whatever subtree is being parsed)
+ dojo.mixin(params, args.defaults);
+ }
+ if(obj.inherited){
+ // settings from dir=rtl or lang=... on a node above this node
+ dojo.mixin(params, obj.inherited);
+ }
+
+ // read parameters (ie, attributes) specified on DOMNode
+ // clsInfo.params lists expected params like {"checked": "boolean", "n": "number"}
+ 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 />
+ var connects = [], // functions to connect after instantiation
+ calls = []; // functions to call after instantiation
+
+ d.forEach(scripts, function(script){
+ node.removeChild(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 || clazz.prototype && 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
+ 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
+ if(!mixin._started){
+ // TODO: for 2.0, when old instantiate() API is desupported, store parent-child
+ // relationships in the nodes[] array so that no getParent() call is needed.
+ // Note that will require a parse() call from ContentPane setting a param that the
+ // ContentPane is the parent widget (so that the parse doesn't call startup() on the
+ // ContentPane's children)
+ d.forEach(thelist, function(instance){
+ if( !args.noStart && instance &&
+ instance.startup &&
+ !instance._started &&
+ (!instance.getParent || !instance.getParent())
+ ){
+ instance.startup();
+ }
+ });
+ }
+ return thelist;
+ };
+
+ this.parse = function(/*DomNode?*/ rootNode, /* Object? */ args){
+ // summary:
+ // Scan the DOM for class instances, and instantiate them.
+ //
+ // description:
+ // Search specified node (or root node) recursively for class instances,
+ // and instantiate them Searches for
+ // dojoType="qualified.class.name"
+ //
+ // rootNode: DomNode?
+ // A default starting root node from which to start the parsing. Can be
+ // omitted, defaulting to the entire document. If omitted, the `args`
+ // object can be passed in this place. If the `args` object has a
+ // `rootNode` member, that is used.
+ //
+ // args:
+ // a kwArgs object passed along to instantiate()
+ //
+ // * noStart: Boolean?
+ // when set will prevent the parser from calling .startup()
+ // when locating the nodes.
+ // * rootNode: DomNode?
+ // identical to the function's `rootNode` argument, though
+ // allowed to be passed in via this `args object.
+ // * inherited: Object
+ // Hash possibly containing dir and lang settings to be applied to
+ // parsed widgets, unless there's another setting on a sub-node that overrides
+ //
+ //
+ // example:
+ // Parse all widgets on a page:
+ // | dojo.parser.parse();
+ //
+ // example:
+ // Parse all classes within the node with id="foo"
+ // | dojo.parser.parse(dojo.byId(foo));
+ //
+ // example:
+ // Parse all classes in a page, but do not call .startup() on any
+ // child
+ // | dojo.parser.parse({ noStart: true })
+ //
+ // example:
+ // Parse all classes in a node, but do not call .startup()
+ // | dojo.parser.parse(someNode, { noStart:true });
+ // | // or
+ // | dojo.parser.parse({ noStart:true, rootNode: someNode });
+
+ // determine the root node based on the passed arguments.
+ var root;
+ if(!args && rootNode && rootNode.rootNode){
+ args = rootNode;
+ root = args.rootNode;
+ }else{
+ root = rootNode;
+ }
+
+ var attrName = this._attrName;
+ function scan(parent, list){
+ // summary:
+ // Parent is an Object representing a DOMNode, with or without a dojoType specified.
+ // Scan parent's children looking for nodes with dojoType specified, storing in list[].
+ // If parent has a dojoType, also collects <script type=dojo/*> children and stores in parent.scripts[].
+ // parent: Object
+ // Object representing the parent node, like
+ // | {
+ // | node: DomNode, // scan children of this node
+ // | inherited: {dir: "rtl"}, // dir/lang setting inherited from above node
+ // |
+ // | // attributes only set if node has dojoType specified
+ // | scripts: [], // empty array, put <script type=dojo/*> in here
+ // | clsInfo: { cls: dijit.form.Button, ...}
+ // | }
+ // list: DomNode[]
+ // Output array of objects (same format as parent) representing nodes to be turned into widgets
+
+ // Effective dir and lang settings on parent node, either set directly or inherited from grandparent
+ var inherited = dojo.clone(parent.inherited);
+ dojo.forEach(["dir", "lang"], function(name){
+ var val = parent.node.getAttribute(name);
+ if(val){
+ inherited[name] = val;
+ }
+ });
+
+ // if parent is a widget, then search for <script type=dojo/*> tags and put them in scripts[].
+ var scripts = parent.scripts;
+
+ // unless parent is a widget with the stopParser flag set, continue search for dojoType, recursively
+ var recurse = !parent.clsInfo || !parent.clsInfo.cls.prototype.stopParser;
+
+ // scan parent's children looking for dojoType and <script type=dojo/*>
+ for(var child = parent.node.firstChild; child; child = child.nextSibling){
+ if(child.nodeType == 1){
+ var type = recurse && child.getAttribute(attrName);
+ if(type){
+ // if dojoType specified, add to output array of nodes to instantiate
+ var params = {
+ "type": type,
+ clsInfo: getClassInfo(type), // note: won't find classes declared via dojo.Declaration
+ node: child,
+ scripts: [], // <script> nodes that are parent's children
+ inherited: inherited // dir & lang attributes inherited from parent
+ };
+ list.push(params);
+
+ // Recurse, collecting <script type="dojo/..."> children, and also looking for
+ // descendant nodes with dojoType specified (unless the widget has the stopParser flag),
+ scan(params, list);
+ }else if(scripts && child.nodeName.toLowerCase() == "script"){
+ // if <script type="dojo/...">, save in scripts[]
+ type = child.getAttribute("type");
+ if (type && /^dojo\//i.test(type)) {
+ scripts.push(child);
+ }
+ }else if(recurse){
+ // Recurse, looking for grandchild nodes with dojoType specified
+ scan({
+ node: child,
+ inherited: inherited
+ }, list);
+ }
+ }
+ }
+ }
+
+ // Make list of all nodes on page w/dojoType specified
+ var list = [];
+ scan({
+ node: root ? dojo.byId(root) : dojo.body(),
+ inherited: (args && args.inherited) || {
+ dir: dojo._isBodyLtr() ? "ltr" : "rtl"
+ }
+ }, list);
+
+ // go build the object instances
+ return this.instantiate(list, null, args); // Array
+ };
}();
+
+//Register the parser callback. It should be the first callback
+//after the a11y test.
+
(function(){
-var _3e=function(){
-if(dojo.config.parseOnLoad){
-dojo.parser.parse();
-}
-};
-if(dojo.exists("dijit.wai.onload")&&(dijit.wai.onload===dojo._loaders[0])){
-dojo._loaders.splice(1,0,_3e);
-}else{
-dojo._loaders.unshift(_3e);
-}
+ var parseRunner = function(){
+ if(dojo.config.parseOnLoad){
+ 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);
+ }
})();
+
}