define("dijit/_TemplatedMixin", [ "dojo/_base/lang", // lang.getObject "dojo/touch", "./_WidgetBase", "dojo/string", // string.substitute string.trim "dojo/cache", // dojo.cache "dojo/_base/array", // array.forEach "dojo/_base/declare", // declare "dojo/dom-construct", // domConstruct.destroy, domConstruct.toDom "dojo/_base/sniff", // has("ie") "dojo/_base/unload", // unload.addOnWindowUnload "dojo/_base/window" // win.doc ], function(lang, touch, _WidgetBase, string, cache, array, declare, domConstruct, has, unload, win) { /*===== var _WidgetBase = dijit._WidgetBase; =====*/ // module: // dijit/_TemplatedMixin // summary: // Mixin for widgets that are instantiated from a template var _TemplatedMixin = declare("dijit._TemplatedMixin", null, { // summary: // Mixin for widgets that are instantiated from a template // templateString: [protected] String // A string that represents the widget template. // 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 require([... "dojo/text!..."], ...) instead templatePath: null, // skipNodeCache: [protected] Boolean // If using a cached widget template nodes 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 data-dojo-attach-point=... in the // template, ex: ["containerNode", "labelNode"] _attachPoints: [], =====*/ /*===== // _attachEvents: [private] Handle[] // List of connections associated with data-dojo-attach-event=... 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 string.substitute(tmpl, this, function(value, key){ if(key.charAt(0) == '!'){ value = lang.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 if(!this.templateString){ this.templateString = cache(this.templatePath, {sanitize: true}); } // 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 = _TemplatedMixin.getCachedTemplate(this.templateString, this._skipNodeCache); var node; if(lang.isString(cached)){ node = domConstruct.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 _setBaseClassAttr 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, function(n,p){ return n.getAttribute(p); }); this._beforeFillContent(); // hook for _WidgetsInTemplateMixin this._fillContent(this.srcNodeRef); }, _beforeFillContent: function(){ }, _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 data-dojo-attach-point // 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/data-dojo-attach-point // * dojoAttachEvent/data-dojo-attach-event // rootNode: DomNode|Widget[] // 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 var nodes = lang.isArray(rootNode) ? rootNode : (rootNode.all || rootNode.getElementsByTagName("*")); var x = lang.isArray(rootNode) ? 0 : -1; for(; x