]> git.wh0rd.org - tt-rss.git/blob - lib/dojo/html.js.uncompressed.js
upgrade dojo to 1.8.3 (refs #570)
[tt-rss.git] / lib / dojo / html.js.uncompressed.js
1 define("dojo/html", ["./_base/kernel", "./_base/lang", "./_base/array", "./_base/declare", "./dom", "./dom-construct", "./parser"],
2 function(kernel, lang, darray, declare, dom, domConstruct, parser){
3 // module:
4 // dojo/html
5
6 var html = {
7 // summary:
8 // TODOC
9 };
10 lang.setObject("dojo.html", html);
11
12 // the parser might be needed..
13
14 // idCounter is incremented with each instantiation to allow assignment of a unique id for tracking, logging purposes
15 var idCounter = 0;
16
17 html._secureForInnerHtml = function(/*String*/ cont){
18 // summary:
19 // removes !DOCTYPE and title elements from the html string.
20 //
21 // khtml is picky about dom faults, you can't attach a style or `<title>` node as child of body
22 // must go into head, so we need to cut out those tags
23 // cont:
24 // An html string for insertion into the dom
25 //
26 return cont.replace(/(?:\s*<!DOCTYPE\s[^>]+>|<title[^>]*>[\s\S]*?<\/title>)/ig, ""); // String
27 };
28
29 html._emptyNode = domConstruct.empty;
30 /*=====
31 dojo.html._emptyNode = function(node){
32 // summary:
33 // Removes all child nodes from the given node. Deprecated, should use dojo/dom-constuct.empty() directly
34 // instead.
35 // node: DOMNode
36 // the parent element
37 };
38 =====*/
39
40 html._setNodeContent = function(/*DomNode*/ node, /*String|DomNode|NodeList*/ cont){
41 // summary:
42 // inserts the given content into the given node
43 // node:
44 // the parent element
45 // content:
46 // the content to be set on the parent element.
47 // This can be an html string, a node reference or a NodeList, dojo/NodeList, Array or other enumerable list of nodes
48
49 // always empty
50 domConstruct.empty(node);
51
52 if(cont){
53 if(typeof cont == "string"){
54 cont = domConstruct.toDom(cont, node.ownerDocument);
55 }
56 if(!cont.nodeType && lang.isArrayLike(cont)){
57 // handle as enumerable, but it may shrink as we enumerate it
58 for(var startlen=cont.length, i=0; i<cont.length; i=startlen==cont.length ? i+1 : 0){
59 domConstruct.place( cont[i], node, "last");
60 }
61 }else{
62 // pass nodes, documentFragments and unknowns through to dojo.place
63 domConstruct.place(cont, node, "last");
64 }
65 }
66
67 // return DomNode
68 return node;
69 };
70
71 // we wrap up the content-setting operation in a object
72 html._ContentSetter = declare("dojo.html._ContentSetter", null,
73 {
74 // node: DomNode|String
75 // An node which will be the parent element that we set content into
76 node: "",
77
78 // content: String|DomNode|DomNode[]
79 // The content to be placed in the node. Can be an HTML string, a node reference, or a enumerable list of nodes
80 content: "",
81
82 // id: String?
83 // Usually only used internally, and auto-generated with each instance
84 id: "",
85
86 // cleanContent: Boolean
87 // Should the content be treated as a full html document,
88 // and the real content stripped of <html>, <body> wrapper before injection
89 cleanContent: false,
90
91 // extractContent: Boolean
92 // Should the content be treated as a full html document,
93 // and the real content stripped of `<html> <body>` wrapper before injection
94 extractContent: false,
95
96 // parseContent: Boolean
97 // Should the node by passed to the parser after the new content is set
98 parseContent: false,
99
100 // parserScope: String
101 // Flag passed to parser. Root for attribute names to search for. If scopeName is dojo,
102 // will search for data-dojo-type (or dojoType). For backwards compatibility
103 // reasons defaults to dojo._scopeName (which is "dojo" except when
104 // multi-version support is used, when it will be something like dojo16, dojo20, etc.)
105 parserScope: kernel._scopeName,
106
107 // startup: Boolean
108 // Start the child widgets after parsing them. Only obeyed if parseContent is true.
109 startup: true,
110
111 // lifecycle methods
112 constructor: function(/*Object*/ params, /*String|DomNode*/ node){
113 // summary:
114 // Provides a configurable, extensible object to wrap the setting on content on a node
115 // call the set() method to actually set the content..
116
117 // the original params are mixed directly into the instance "this"
118 lang.mixin(this, params || {});
119
120 // give precedence to params.node vs. the node argument
121 // and ensure its a node, not an id string
122 node = this.node = dom.byId( this.node || node );
123
124 if(!this.id){
125 this.id = [
126 "Setter",
127 (node) ? node.id || node.tagName : "",
128 idCounter++
129 ].join("_");
130 }
131 },
132 set: function(/* String|DomNode|NodeList? */ cont, /*Object?*/ params){
133 // summary:
134 // front-end to the set-content sequence
135 // cont:
136 // An html string, node or enumerable list of nodes for insertion into the dom
137 // If not provided, the object's content property will be used
138 if(undefined !== cont){
139 this.content = cont;
140 }
141 // in the re-use scenario, set needs to be able to mixin new configuration
142 if(params){
143 this._mixin(params);
144 }
145
146 this.onBegin();
147 this.setContent();
148
149 var ret = this.onEnd();
150
151 if(ret && ret.then){
152 // Make dojox/html/_ContentSetter.set() return a Promise that resolves when load and parse complete.
153 return ret;
154 }else{
155 // Vanilla dojo/html._ContentSetter.set() returns a DOMNode for back compat. For 2.0, switch it to
156 // return a Deferred like above.
157 return this.node;
158 }
159 },
160
161 setContent: function(){
162 // summary:
163 // sets the content on the node
164
165 var node = this.node;
166 if(!node){
167 // can't proceed
168 throw new Error(this.declaredClass + ": setContent given no node");
169 }
170 try{
171 node = html._setNodeContent(node, this.content);
172 }catch(e){
173 // check if a domfault occurs when we are appending this.errorMessage
174 // like for instance if domNode is a UL and we try append a DIV
175
176 // FIXME: need to allow the user to provide a content error message string
177 var errMess = this.onContentError(e);
178 try{
179 node.innerHTML = errMess;
180 }catch(e){
181 console.error('Fatal ' + this.declaredClass + '.setContent could not change content due to '+e.message, e);
182 }
183 }
184 // always put back the node for the next method
185 this.node = node; // DomNode
186 },
187
188 empty: function(){
189 // summary:
190 // cleanly empty out existing content
191
192 // If there is a parse in progress, cancel it.
193 if(this.parseDeferred){
194 if(!this.parseDeferred.isResolved()){
195 this.parseDeferred.cancel();
196 }
197 delete this.parseDeferred;
198 }
199
200 // destroy any widgets from a previous run
201 // NOTE: if you don't want this you'll need to empty
202 // the parseResults array property yourself to avoid bad things happening
203 if(this.parseResults && this.parseResults.length){
204 darray.forEach(this.parseResults, function(w){
205 if(w.destroy){
206 w.destroy();
207 }
208 });
209 delete this.parseResults;
210 }
211 // this is fast, but if you know its already empty or safe, you could
212 // override empty to skip this step
213 domConstruct.empty(this.node);
214 },
215
216 onBegin: function(){
217 // summary:
218 // Called after instantiation, but before set();
219 // It allows modification of any of the object properties -
220 // including the node and content provided - before the set operation actually takes place
221 // This default implementation checks for cleanContent and extractContent flags to
222 // optionally pre-process html string content
223 var cont = this.content;
224
225 if(lang.isString(cont)){
226 if(this.cleanContent){
227 cont = html._secureForInnerHtml(cont);
228 }
229
230 if(this.extractContent){
231 var match = cont.match(/<body[^>]*>\s*([\s\S]+)\s*<\/body>/im);
232 if(match){ cont = match[1]; }
233 }
234 }
235
236 // clean out the node and any cruft associated with it - like widgets
237 this.empty();
238
239 this.content = cont;
240 return this.node; // DomNode
241 },
242
243 onEnd: function(){
244 // summary:
245 // Called after set(), when the new content has been pushed into the node
246 // It provides an opportunity for post-processing before handing back the node to the caller
247 // This default implementation checks a parseContent flag to optionally run the dojo parser over the new content
248 if(this.parseContent){
249 // populates this.parseResults and this.parseDeferred if you need those..
250 this._parse();
251 }
252 return this.node; // DomNode
253 // TODO: for 2.0 return a Promise indicating that the parse completed.
254 },
255
256 tearDown: function(){
257 // summary:
258 // manually reset the Setter instance if its being re-used for example for another set()
259 // description:
260 // tearDown() is not called automatically.
261 // In normal use, the Setter instance properties are simply allowed to fall out of scope
262 // but the tearDown method can be called to explicitly reset this instance.
263 delete this.parseResults;
264 delete this.parseDeferred;
265 delete this.node;
266 delete this.content;
267 },
268
269 onContentError: function(err){
270 return "Error occurred setting content: " + err;
271 },
272
273 onExecError: function(err){
274 return "Error occurred executing scripts: " + err;
275 },
276
277 _mixin: function(params){
278 // mix properties/methods into the instance
279 // TODO: the intention with tearDown is to put the Setter's state
280 // back to that of the original constructor (vs. deleting/resetting everything regardless of ctor params)
281 // so we could do something here to move the original properties aside for later restoration
282 var empty = {}, key;
283 for(key in params){
284 if(key in empty){ continue; }
285 // TODO: here's our opportunity to mask the properties we don't consider configurable/overridable
286 // .. but history shows we'll almost always guess wrong
287 this[key] = params[key];
288 }
289 },
290 _parse: function(){
291 // summary:
292 // runs the dojo parser over the node contents, storing any results in this.parseResults
293 // and the parse promise in this.parseDeferred
294 // Any errors resulting from parsing are passed to _onError for handling
295
296 var rootNode = this.node;
297 try{
298 // store the results (widgets, whatever) for potential retrieval
299 var inherited = {};
300 darray.forEach(["dir", "lang", "textDir"], function(name){
301 if(this[name]){
302 inherited[name] = this[name];
303 }
304 }, this);
305 var self = this;
306 this.parseDeferred = parser.parse({
307 rootNode: rootNode,
308 noStart: !this.startup,
309 inherited: inherited,
310 scope: this.parserScope
311 }).then(function(results){
312 return self.parseResults = results;
313 });
314 }catch(e){
315 this._onError('Content', e, "Error parsing in _ContentSetter#"+this.id);
316 }
317 },
318
319 _onError: function(type, err, consoleText){
320 // summary:
321 // shows user the string that is returned by on[type]Error
322 // override/implement on[type]Error and return your own string to customize
323 var errText = this['on' + type + 'Error'].call(this, err);
324 if(consoleText){
325 console.error(consoleText, err);
326 }else if(errText){ // a empty string won't change current content
327 html._setNodeContent(this.node, errText, true);
328 }
329 }
330 }); // end declare()
331
332 html.set = function(/*DomNode*/ node, /*String|DomNode|NodeList*/ cont, /*Object?*/ params){
333 // summary:
334 // inserts (replaces) the given content into the given node. dojo.place(cont, node, "only")
335 // may be a better choice for simple HTML insertion.
336 // description:
337 // Unless you need to use the params capabilities of this method, you should use
338 // dojo.place(cont, node, "only"). dojo.place() has more robust support for injecting
339 // an HTML string into the DOM, but it only handles inserting an HTML string as DOM
340 // elements, or inserting a DOM node. dojo.place does not handle NodeList insertions
341 // or the other capabilities as defined by the params object for this method.
342 // node:
343 // the parent element that will receive the content
344 // cont:
345 // the content to be set on the parent element.
346 // This can be an html string, a node reference or a NodeList, dojo/NodeList, Array or other enumerable list of nodes
347 // params:
348 // Optional flags/properties to configure the content-setting. See dojo/html/_ContentSetter
349 // example:
350 // A safe string/node/nodelist content replacement/injection with hooks for extension
351 // Example Usage:
352 // | html.set(node, "some string");
353 // | html.set(node, contentNode, {options});
354 // | html.set(node, myNode.childNodes, {options});
355 if(undefined == cont){
356 console.warn("dojo.html.set: no cont argument provided, using empty string");
357 cont = "";
358 }
359 if(!params){
360 // simple and fast
361 return html._setNodeContent(node, cont, true);
362 }else{
363 // more options but slower
364 // note the arguments are reversed in order, to match the convention for instantiation via the parser
365 var op = new html._ContentSetter(lang.mixin(
366 params,
367 { content: cont, node: node }
368 ));
369 return op.set();
370 }
371 };
372
373 return html;
374 });