]>
git.wh0rd.org - tt-rss.git/blob - lib/dijit/_TemplatedMixin.js.uncompressed.js
1 define("dijit/_TemplatedMixin", [
2 "dojo/_base/lang", // lang.getObject
5 "dojo/string", // string.substitute string.trim
6 "dojo/cache", // dojo.cache
7 "dojo/_base/array", // array.forEach
8 "dojo/_base/declare", // declare
9 "dojo/dom-construct", // domConstruct.destroy, domConstruct.toDom
10 "dojo/sniff", // has("ie")
11 "dojo/_base/unload" // unload.addOnWindowUnload
12 ], function(lang
, touch
, _WidgetBase
, string
, cache
, array
, declare
, domConstruct
, has
, unload
) {
15 // dijit/_TemplatedMixin
17 var _TemplatedMixin
= declare("dijit._TemplatedMixin", null, {
19 // Mixin for widgets that are instantiated from a template
21 // templateString: [protected] String
22 // A string that represents the widget template.
23 // Use in conjunction with dojo.cache() to load from a file.
26 // templatePath: [protected deprecated] String
27 // Path to template (HTML file) for this widget relative to dojo.baseUrl.
28 // Deprecated: use templateString with require([... "dojo/text!..."], ...) instead
31 // skipNodeCache: [protected] Boolean
32 // If using a cached widget template nodes poses issues for a
33 // particular widget class, it can set this property to ensure
34 // that its template is always re-built from a string
35 _skipNodeCache
: false,
37 // _earlyTemplatedStartup: Boolean
38 // A fallback to preserve the 1.0 - 1.3 behavior of children in
39 // templates having their startup called before the parent widget
40 // fires postCreate. Defaults to 'false', causing child widgets to
41 // have their .startup() called immediately before a parent widget
42 // .startup(), but always after the parent .postCreate(). Set to
43 // 'true' to re-enable to previous, arguably broken, behavior.
44 _earlyTemplatedStartup
: false,
47 // _attachPoints: [private] String[]
48 // List of widget attribute names associated with data-dojo-attach-point=... in the
49 // template, ex: ["containerNode", "labelNode"]
52 // _attachEvents: [private] Handle[]
53 // List of connections associated with data-dojo-attach-event=... in the
58 constructor: function(/*===== params, srcNodeRef =====*/){
61 // params: Object|null
62 // Hash of initialization parameters for widget, including scalar values (like title, duration etc.)
63 // and functions, typically callbacks like onClick.
64 // The hash can contain any of the widget's properties, excluding read-only properties.
65 // srcNodeRef: DOMNode|String?
66 // If a srcNodeRef (DOM node) is specified, replace srcNodeRef with my generated DOM tree.
68 this._attachPoints
= [];
69 this._attachEvents
= [];
72 _stringRepl: function(tmpl
){
74 // Does substitution of ${foo} type properties in template string
77 var className
= this.declaredClass
, _this
= this;
78 // Cache contains a string because we need to do property replacement
79 // do the property replacement
80 return string
.substitute(tmpl
, this, function(value
, key
){
81 if(key
.charAt(0) == '!'){ value
= lang
.getObject(key
.substr(1), false, _this
); }
82 if(typeof value
== "undefined"){ throw new Error(className
+" template:"+key
); } // a debugging aide
83 if(value
== null){ return ""; }
85 // Substitution keys beginning with ! will skip the transform step,
86 // in case a user wishes to insert unescaped markup, e.g. ${!foo}
87 return key
.charAt(0) == "!" ? value
:
88 // Safer substitution, see heading "Attribute values" in
89 // http://www.w3.org/TR/REC-html40/appendix/notes.html#h-B.3.2
90 value
.toString().replace(/"/g,"""); //TODO
: add
&
? use encodeXML method
?
94 buildRendering: function(){
96 // Construct the UI for this widget from a template, setting this.domNode.
100 if(!this.templateString
){
101 this.templateString
= cache(this.templatePath
, {sanitize
: true});
104 // Lookup cached version of template, and download to cache if it
105 // isn't there already. Returns either a DomNode or a string, depending on
106 // whether or not the template contains ${foo} replacement parameters.
107 var cached
= _TemplatedMixin
.getCachedTemplate(this.templateString
, this._skipNodeCache
, this.ownerDocument
);
110 if(lang
.isString(cached
)){
111 node
= domConstruct
.toDom(this._stringRepl(cached
), this.ownerDocument
);
112 if(node
.nodeType
!= 1){
113 // Flag common problems such as templates with multiple top level nodes (nodeType == 11)
114 throw new Error("Invalid template: " + cached
);
117 // if it's a node, all we have to do is clone it
118 node
= cached
.cloneNode(true);
123 // Call down to _Widget.buildRendering() to get base classes assigned
124 // TODO: change the baseClass assignment to _setBaseClassAttr
125 this.inherited(arguments
);
127 // recurse through the node, looking for, and attaching to, our
128 // attachment points and events, which should be defined on the template node.
129 this._attachTemplateNodes(node
, function(n
,p
){ return n
.getAttribute(p
); });
131 this._beforeFillContent(); // hook for _WidgetsInTemplateMixin
133 this._fillContent(this.srcNodeRef
);
136 _beforeFillContent: function(){
139 _fillContent: function(/*DomNode*/ source
){
141 // Relocate source contents to templated container node.
142 // this.containerNode must be able to receive children, or exceptions will be thrown.
145 var dest
= this.containerNode
;
147 while(source
.hasChildNodes()){
148 dest
.appendChild(source
.firstChild
);
153 _attachTemplateNodes: function(rootNode
, getAttrFunc
){
155 // Iterate through the template and attach functions and nodes accordingly.
156 // Alternately, if rootNode is an array of widgets, then will process data-dojo-attach-point
157 // etc. for those widgets.
159 // Map widget properties and functions to the handlers specified in
160 // the dom node and it's descendants. This function iterates over all
161 // nodes and looks for these properties:
163 // - dojoAttachPoint/data-dojo-attach-point
164 // - dojoAttachEvent/data-dojo-attach-event
165 // rootNode: DomNode|Widget[]
166 // the node to search for properties. All children will be searched.
167 // getAttrFunc: Function
168 // a function which will be used to obtain property for a given
173 var nodes
= lang
.isArray(rootNode
) ? rootNode
: (rootNode
.all
|| rootNode
.getElementsByTagName("*"));
174 var x
= lang
.isArray(rootNode
) ? 0 : -1;
175 for(; x
< 0 || nodes
[x
]; x
++){ // don't access nodes.length on IE, see #14346
176 var baseNode
= (x
== -1) ? rootNode
: nodes
[x
];
177 if(this.widgetsInTemplate
&& (getAttrFunc(baseNode
, "dojoType") || getAttrFunc(baseNode
, "data-dojo-type"))){
180 // Process data-dojo-attach-point
181 var attachPoint
= getAttrFunc(baseNode
, "dojoAttachPoint") || getAttrFunc(baseNode
, "data-dojo-attach-point");
183 var point
, points
= attachPoint
.split(/\s*,\s*/);
184 while((point
= points
.shift())){
185 if(lang
.isArray(this[point
])){
186 this[point
].push(baseNode
);
188 this[point
]=baseNode
;
190 this._attachPoints
.push(point
);
194 // Process data-dojo-attach-event
195 var attachEvent
= getAttrFunc(baseNode
, "dojoAttachEvent") || getAttrFunc(baseNode
, "data-dojo-attach-event");
197 // NOTE: we want to support attributes that have the form
198 // "domEvent: nativeEvent; ..."
199 var event
, events
= attachEvent
.split(/\s*,\s*/);
200 var trim
= lang
.trim
;
201 while((event
= events
.shift())){
204 if(event
.indexOf(":") != -1){
205 // oh, if only JS had tuple assignment
206 var funcNameArr
= event
.split(":");
207 event
= trim(funcNameArr
[0]);
208 thisFunc
= trim(funcNameArr
[1]);
215 // Map "press", "move" and "release" to keys.touch, keys.move, keys.release
216 this._attachEvents
.push(this.connect(baseNode
, touch
[event
] || event
, thisFunc
));
223 destroyRendering: function(){
224 // Delete all attach points to prevent IE6 memory leaks.
225 array
.forEach(this._attachPoints
, function(point
){
228 this._attachPoints
= [];
230 // And same for event handlers
231 array
.forEach(this._attachEvents
, this.disconnect
, this);
232 this._attachEvents
= [];
234 this.inherited(arguments
);
238 // key is templateString; object is either string or DOM tree
239 _TemplatedMixin
._templateCache
= {};
241 _TemplatedMixin
.getCachedTemplate = function(templateString
, alwaysUseString
, doc
){
243 // Static method to get a template based on the templatePath or
244 // templateString key
245 // templateString: String
247 // alwaysUseString: Boolean
248 // Don't cache the DOM tree for this template, even if it doesn't have any variables
250 // The target document. Defaults to document global if unspecified.
252 // Either string (if there are ${} variables that need to be replaced) or just
253 // a DOM tree (if the node can be cloned directly)
255 // is it already cached?
256 var tmplts
= _TemplatedMixin
._templateCache
;
257 var key
= templateString
;
258 var cached
= tmplts
[key
];
261 // if the cached value is an innerHTML string (no ownerDocument) or a DOM tree created within the
262 // current document, then use the current cached value
263 if(!cached
.ownerDocument
|| cached
.ownerDocument
== (doc
|| document
)){
264 // string or node of the same document
267 }catch(e
){ /* squelch */ } // IE can throw an exception if cached.ownerDocument was reloaded
268 domConstruct
.destroy(cached
);
271 templateString
= string
.trim(templateString
);
273 if(alwaysUseString
|| templateString
.match(/\$\{([^\}]+)\}/g)){
274 // there are variables in the template so all we can do is cache the string
275 return (tmplts
[key
] = templateString
); //String
277 // there are no variables in the template so we can cache the DOM tree
278 var node
= domConstruct
.toDom(templateString
, doc
);
279 if(node
.nodeType
!= 1){
280 throw new Error("Invalid template: " + templateString
);
282 return (tmplts
[key
] = node
); //Node
287 unload
.addOnWindowUnload(function(){
288 var cache
= _TemplatedMixin
._templateCache
;
289 for(var key
in cache
){
290 var value
= cache
[key
];
291 if(typeof value
== "object"){ // value is either a string or a DOM node template
292 domConstruct
.destroy(value
);
299 // These arguments can be specified for widgets which are used in templates.
300 // Since any widget can be specified as sub widgets in template, mix it
301 // into the base widget class. (This is a hack, but it's effective.).
302 // Remove for 2.0. Also, hide from API doc parser.
303 lang
.extend(_WidgetBase
, /*===== {} || =====*/ {
308 return _TemplatedMixin
;