diff options
author | Andrew Dolgov <[email protected]> | 2011-11-08 20:40:44 +0400 |
---|---|---|
committer | Andrew Dolgov <[email protected]> | 2011-11-08 20:40:44 +0400 |
commit | 81bea17aefb26859f825b9293c7c99192874806e (patch) | |
tree | fb244408ca271affa2899adb634788802c9a89d8 /lib/dijit/_Templated.js | |
parent | 870a70e109ac9e80a88047044530de53d0404ec7 (diff) |
upgrade Dojo to 1.6.1
Diffstat (limited to 'lib/dijit/_Templated.js')
-rw-r--r-- | lib/dijit/_Templated.js | 503 |
1 files changed, 335 insertions, 168 deletions
diff --git a/lib/dijit/_Templated.js b/lib/dijit/_Templated.js index 65ca20bfa..2e8d6b72f 100644 --- a/lib/dijit/_Templated.js +++ b/lib/dijit/_Templated.js @@ -1,184 +1,351 @@ /* - Copyright (c) 2004-2010, The Dojo Foundation All Rights Reserved. + Copyright (c) 2004-2011, The Dojo Foundation All Rights Reserved. Available via Academic Free License >= 2.1 OR the modified BSD license. see: http://dojotoolkit.org/license for details */ -if(!dojo._hasResource["dijit._Templated"]){ -dojo._hasResource["dijit._Templated"]=true; +if(!dojo._hasResource["dijit._Templated"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code. +dojo._hasResource["dijit._Templated"] = true; dojo.provide("dijit._Templated"); dojo.require("dijit._Widget"); dojo.require("dojo.string"); dojo.require("dojo.parser"); dojo.require("dojo.cache"); -dojo.declare("dijit._Templated",null,{templateString:null,templatePath:null,widgetsInTemplate:false,_skipNodeCache:false,_earlyTemplatedStartup:false,constructor:function(){ -this._attachPoints=[]; -},_stringRepl:function(_1){ -var _2=this.declaredClass,_3=this; -return dojo.string.substitute(_1,this,function(_4,_5){ -if(_5.charAt(0)=="!"){ -_4=dojo.getObject(_5.substr(1),false,_3); -} -if(typeof _4=="undefined"){ -throw new Error(_2+" template:"+_5); -} -if(_4==null){ -return ""; -} -return _5.charAt(0)=="!"?_4:_4.toString().replace(/"/g,"""); -},this); -},buildRendering:function(){ -var _6=dijit._Templated.getCachedTemplate(this.templatePath,this.templateString,this._skipNodeCache); -var _7; -if(dojo.isString(_6)){ -_7=dojo._toDom(this._stringRepl(_6)); -if(_7.nodeType!=1){ -throw new Error("Invalid template: "+_6); -} -}else{ -_7=_6.cloneNode(true); -} -this.domNode=_7; -this._attachTemplateNodes(_7); -if(this.widgetsInTemplate){ -var _8=dojo.parser,_9,_a; -if(_8._query!="[dojoType]"){ -_9=_8._query; -_a=_8._attrName; -_8._query="[dojoType]"; -_8._attrName="dojoType"; -} -var cw=(this._startupWidgets=dojo.parser.parse(_7,{noStart:!this._earlyTemplatedStartup,inherited:{dir:this.dir,lang:this.lang}})); -if(_9){ -_8._query=_9; -_8._attrName=_a; -} -this._supportingWidgets=dijit.findWidgets(_7); -this._attachTemplateNodes(cw,function(n,p){ -return n[p]; -}); -} -this._fillContent(this.srcNodeRef); -},_fillContent:function(_b){ -var _c=this.containerNode; -if(_b&&_c){ -while(_b.hasChildNodes()){ -_c.appendChild(_b.firstChild); -} -} -},_attachTemplateNodes:function(_d,_e){ -_e=_e||function(n,p){ -return n.getAttribute(p); -}; -var _f=dojo.isArray(_d)?_d:(_d.all||_d.getElementsByTagName("*")); -var x=dojo.isArray(_d)?0:-1; -for(;x<_f.length;x++){ -var _10=(x==-1)?_d:_f[x]; -if(this.widgetsInTemplate&&_e(_10,"dojoType")){ -continue; -} -var _11=_e(_10,"dojoAttachPoint"); -if(_11){ -var _12,_13=_11.split(/\s*,\s*/); -while((_12=_13.shift())){ -if(dojo.isArray(this[_12])){ -this[_12].push(_10); -}else{ -this[_12]=_10; -} -this._attachPoints.push(_12); -} -} -var _14=_e(_10,"dojoAttachEvent"); -if(_14){ -var _15,_16=_14.split(/\s*,\s*/); -var _17=dojo.trim; -while((_15=_16.shift())){ -if(_15){ -var _18=null; -if(_15.indexOf(":")!=-1){ -var _19=_15.split(":"); -_15=_17(_19[0]); -_18=_17(_19[1]); -}else{ -_15=_17(_15); -} -if(!_18){ -_18=_15; -} -this.connect(_10,_15,_18); -} -} -} -var _1a=_e(_10,"waiRole"); -if(_1a){ -dijit.setWaiRole(_10,_1a); -} -var _1b=_e(_10,"waiState"); -if(_1b){ -dojo.forEach(_1b.split(/\s*,\s*/),function(_1c){ -if(_1c.indexOf("-")!=-1){ -var _1d=_1c.split("-"); -dijit.setWaiState(_10,_1d[0],_1d[1]); -} -}); -} -} -},startup:function(){ -dojo.forEach(this._startupWidgets,function(w){ -if(w&&!w._started&&w.startup){ -w.startup(); -} -}); -this.inherited(arguments); -},destroyRendering:function(){ -dojo.forEach(this._attachPoints,function(_1e){ -delete this[_1e]; -},this); -this._attachPoints=[]; -this.inherited(arguments); -}}); -dijit._Templated._templateCache={}; -dijit._Templated.getCachedTemplate=function(_1f,_20,_21){ -var _22=dijit._Templated._templateCache; -var key=_20||_1f; -var _23=_22[key]; -if(_23){ -try{ -if(!_23.ownerDocument||_23.ownerDocument==dojo.doc){ -return _23; -} -} -catch(e){ -} -dojo.destroy(_23); -} -if(!_20){ -_20=dojo.cache(_1f,{sanitize:true}); -} -_20=dojo.string.trim(_20); -if(_21||_20.match(/\$\{([^\}]+)\}/g)){ -return (_22[key]=_20); -}else{ -var _24=dojo._toDom(_20); -if(_24.nodeType!=1){ -throw new Error("Invalid template: "+_20); -} -return (_22[key]=_24); -} + + +dojo.declare("dijit._Templated", + null, + { + // summary: + // Mixin for widgets that are instantiated from a template + + // templateString: [protected] String + // A string that represents the widget template. Pre-empts the + // templatePath. In builds that have their strings "interned", the + // templatePath is converted to an inline templateString, thereby + // preventing a synchronous network call. + // + // Use in conjunction with dojo.cache() to load from a file. + templateString: null, + + // templatePath: [protected deprecated] String + // Path to template (HTML file) for this widget relative to dojo.baseUrl. + // Deprecated: use templateString with dojo.cache() instead. + templatePath: null, + + // widgetsInTemplate: [protected] Boolean + // Should we parse the template to find widgets that might be + // declared in markup inside it? False by default. + widgetsInTemplate: false, + + // skipNodeCache: [protected] Boolean + // If using a cached widget template node poses issues for a + // particular widget class, it can set this property to ensure + // that its template is always re-built from a string + _skipNodeCache: false, + + // _earlyTemplatedStartup: Boolean + // A fallback to preserve the 1.0 - 1.3 behavior of children in + // templates having their startup called before the parent widget + // fires postCreate. Defaults to 'false', causing child widgets to + // have their .startup() called immediately before a parent widget + // .startup(), but always after the parent .postCreate(). Set to + // 'true' to re-enable to previous, arguably broken, behavior. + _earlyTemplatedStartup: false, + +/*===== + // _attachPoints: [private] String[] + // List of widget attribute names associated with dojoAttachPoint=... in the + // template, ex: ["containerNode", "labelNode"] + _attachPoints: [], + =====*/ + +/*===== + // _attachEvents: [private] Handle[] + // List of connections associated with dojoAttachEvent=... in the + // template + _attachEvents: [], + =====*/ + + constructor: function(){ + this._attachPoints = []; + this._attachEvents = []; + }, + + _stringRepl: function(tmpl){ + // summary: + // Does substitution of ${foo} type properties in template string + // tags: + // private + var className = this.declaredClass, _this = this; + // Cache contains a string because we need to do property replacement + // do the property replacement + return dojo.string.substitute(tmpl, this, function(value, key){ + if(key.charAt(0) == '!'){ value = dojo.getObject(key.substr(1), false, _this); } + if(typeof value == "undefined"){ throw new Error(className+" template:"+key); } // a debugging aide + if(value == null){ return ""; } + + // Substitution keys beginning with ! will skip the transform step, + // in case a user wishes to insert unescaped markup, e.g. ${!foo} + return key.charAt(0) == "!" ? value : + // Safer substitution, see heading "Attribute values" in + // http://www.w3.org/TR/REC-html40/appendix/notes.html#h-B.3.2 + value.toString().replace(/"/g,"""); //TODO: add &? use encodeXML method? + }, this); + }, + + buildRendering: function(){ + // summary: + // Construct the UI for this widget from a template, setting this.domNode. + // tags: + // protected + + // Lookup cached version of template, and download to cache if it + // isn't there already. Returns either a DomNode or a string, depending on + // whether or not the template contains ${foo} replacement parameters. + var cached = dijit._Templated.getCachedTemplate(this.templatePath, this.templateString, this._skipNodeCache); + + var node; + if(dojo.isString(cached)){ + node = dojo._toDom(this._stringRepl(cached)); + if(node.nodeType != 1){ + // Flag common problems such as templates with multiple top level nodes (nodeType == 11) + throw new Error("Invalid template: " + cached); + } + }else{ + // if it's a node, all we have to do is clone it + node = cached.cloneNode(true); + } + + this.domNode = node; + + // Call down to _Widget.buildRendering() to get base classes assigned + // TODO: change the baseClass assignment to attributeMap + this.inherited(arguments); + + // recurse through the node, looking for, and attaching to, our + // attachment points and events, which should be defined on the template node. + this._attachTemplateNodes(node); + + if(this.widgetsInTemplate){ + // Store widgets that we need to start at a later point in time + var cw = (this._startupWidgets = dojo.parser.parse(node, { + noStart: !this._earlyTemplatedStartup, + template: true, + inherited: {dir: this.dir, lang: this.lang}, + propsThis: this, // so data-dojo-props of widgets in the template can reference "this" to refer to me + scope: "dojo" // even in multi-version mode templates use dojoType/data-dojo-type + })); + + this._supportingWidgets = dijit.findWidgets(node); + + this._attachTemplateNodes(cw, function(n,p){ + return n[p]; + }); + } + + this._fillContent(this.srcNodeRef); + }, + + _fillContent: function(/*DomNode*/ source){ + // summary: + // Relocate source contents to templated container node. + // this.containerNode must be able to receive children, or exceptions will be thrown. + // tags: + // protected + var dest = this.containerNode; + if(source && dest){ + while(source.hasChildNodes()){ + dest.appendChild(source.firstChild); + } + } + }, + + _attachTemplateNodes: function(rootNode, getAttrFunc){ + // summary: + // Iterate through the template and attach functions and nodes accordingly. + // Alternately, if rootNode is an array of widgets, then will process dojoAttachPoint + // etc. for those widgets. + // description: + // Map widget properties and functions to the handlers specified in + // the dom node and it's descendants. This function iterates over all + // nodes and looks for these properties: + // * dojoAttachPoint + // * dojoAttachEvent + // * waiRole + // * waiState + // rootNode: DomNode|Array[Widgets] + // the node to search for properties. All children will be searched. + // getAttrFunc: Function? + // a function which will be used to obtain property for a given + // DomNode/Widget + // tags: + // private + + getAttrFunc = getAttrFunc || function(n,p){ return n.getAttribute(p); }; + + var nodes = dojo.isArray(rootNode) ? rootNode : (rootNode.all || rootNode.getElementsByTagName("*")); + var x = dojo.isArray(rootNode) ? 0 : -1; + for(; x<nodes.length; x++){ + var baseNode = (x == -1) ? rootNode : nodes[x]; + if(this.widgetsInTemplate && (getAttrFunc(baseNode, "dojoType") || getAttrFunc(baseNode, "data-dojo-type"))){ + continue; + } + // Process dojoAttachPoint + var attachPoint = getAttrFunc(baseNode, "dojoAttachPoint") || getAttrFunc(baseNode, "data-dojo-attach-point"); + if(attachPoint){ + var point, points = attachPoint.split(/\s*,\s*/); + while((point = points.shift())){ + if(dojo.isArray(this[point])){ + this[point].push(baseNode); + }else{ + this[point]=baseNode; + } + this._attachPoints.push(point); + } + } + + // Process dojoAttachEvent + var attachEvent = getAttrFunc(baseNode, "dojoAttachEvent") || getAttrFunc(baseNode, "data-dojo-attach-event");; + if(attachEvent){ + // NOTE: we want to support attributes that have the form + // "domEvent: nativeEvent; ..." + var event, events = attachEvent.split(/\s*,\s*/); + var trim = dojo.trim; + while((event = events.shift())){ + if(event){ + var thisFunc = null; + if(event.indexOf(":") != -1){ + // oh, if only JS had tuple assignment + var funcNameArr = event.split(":"); + event = trim(funcNameArr[0]); + thisFunc = trim(funcNameArr[1]); + }else{ + event = trim(event); + } + if(!thisFunc){ + thisFunc = event; + } + this._attachEvents.push(this.connect(baseNode, event, thisFunc)); + } + } + } + + // waiRole, waiState + // TODO: remove this in 2.0, templates are now using role=... and aria-XXX=... attributes directicly + var role = getAttrFunc(baseNode, "waiRole"); + if(role){ + dijit.setWaiRole(baseNode, role); + } + var values = getAttrFunc(baseNode, "waiState"); + if(values){ + dojo.forEach(values.split(/\s*,\s*/), function(stateValue){ + if(stateValue.indexOf('-') != -1){ + var pair = stateValue.split('-'); + dijit.setWaiState(baseNode, pair[0], pair[1]); + } + }); + } + } + }, + + startup: function(){ + dojo.forEach(this._startupWidgets, function(w){ + if(w && !w._started && w.startup){ + w.startup(); + } + }); + this.inherited(arguments); + }, + + destroyRendering: function(){ + // Delete all attach points to prevent IE6 memory leaks. + dojo.forEach(this._attachPoints, function(point){ + delete this[point]; + }, this); + this._attachPoints = []; + + // And same for event handlers + dojo.forEach(this._attachEvents, this.disconnect, this); + this._attachEvents = []; + + this.inherited(arguments); + } + } +); + +// key is either templatePath or templateString; object is either string or DOM tree +dijit._Templated._templateCache = {}; + +dijit._Templated.getCachedTemplate = function(templatePath, templateString, alwaysUseString){ + // summary: + // Static method to get a template based on the templatePath or + // templateString key + // templatePath: String||dojo.uri.Uri + // The URL to get the template from. + // templateString: String? + // a string to use in lieu of fetching the template from a URL. Takes precedence + // over templatePath + // returns: Mixed + // Either string (if there are ${} variables that need to be replaced) or just + // a DOM tree (if the node can be cloned directly) + + // is it already cached? + var tmplts = dijit._Templated._templateCache; + var key = templateString || templatePath; + var cached = tmplts[key]; + if(cached){ + try{ + // if the cached value is an innerHTML string (no ownerDocument) or a DOM tree created within the current document, then use the current cached value + if(!cached.ownerDocument || cached.ownerDocument == dojo.doc){ + // string or node of the same document + return cached; + } + }catch(e){ /* squelch */ } // IE can throw an exception if cached.ownerDocument was reloaded + dojo.destroy(cached); + } + + // If necessary, load template string from template path + if(!templateString){ + templateString = dojo.cache(templatePath, {sanitize: true}); + } + templateString = dojo.string.trim(templateString); + + if(alwaysUseString || templateString.match(/\$\{([^\}]+)\}/g)){ + // there are variables in the template so all we can do is cache the string + return (tmplts[key] = templateString); //String + }else{ + // there are no variables in the template so we can cache the DOM tree + var node = dojo._toDom(templateString); + if(node.nodeType != 1){ + throw new Error("Invalid template: " + templateString); + } + return (tmplts[key] = node); //Node + } }; + if(dojo.isIE){ -dojo.addOnWindowUnload(function(){ -var _25=dijit._Templated._templateCache; -for(var key in _25){ -var _26=_25[key]; -if(typeof _26=="object"){ -dojo.destroy(_26); -} -delete _25[key]; + dojo.addOnWindowUnload(function(){ + var cache = dijit._Templated._templateCache; + for(var key in cache){ + var value = cache[key]; + if(typeof value == "object"){ // value is either a string or a DOM node template + dojo.destroy(value); + } + delete cache[key]; + } + }); } + +// These arguments can be specified for widgets which are used in templates. +// Since any widget can be specified as sub widgets in template, mix it +// into the base widget class. (This is a hack, but it's effective.) +dojo.extend(dijit._Widget,{ + dojoAttachEvent: "", + dojoAttachPoint: "", + waiRole: "", + waiState:"" }); -} -dojo.extend(dijit._Widget,{dojoAttachEvent:"",dojoAttachPoint:"",waiRole:"",waiState:""}); + } |