summaryrefslogtreecommitdiff
path: root/lib/dijit/_Templated.js
diff options
context:
space:
mode:
Diffstat (limited to 'lib/dijit/_Templated.js')
-rw-r--r--lib/dijit/_Templated.js503
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,"&quot;"); //TODO: add &amp? 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:""});
+
}