]> git.wh0rd.org - tt-rss.git/blame - lib/dijit/layout/ContentPane.js.uncompressed.js
modify dojo rebuild script to remove uncompressed files
[tt-rss.git] / lib / dijit / layout / ContentPane.js.uncompressed.js
CommitLineData
f0cfe83e
AD
1define("dijit/layout/ContentPane", [
2 "dojo/_base/kernel", // kernel.deprecated
3 "dojo/_base/lang", // lang.mixin lang.delegate lang.hitch lang.isFunction lang.isObject
4 "../_Widget",
5 "../_Container",
6 "./_ContentPaneResizeMixin",
7 "dojo/string", // string.substitute
8 "dojo/html", // html._ContentSetter
9 "dojo/i18n!../nls/loading",
10 "dojo/_base/array", // array.forEach
11 "dojo/_base/declare", // declare
12 "dojo/_base/Deferred", // Deferred
13 "dojo/dom", // dom.byId
14 "dojo/dom-attr", // domAttr.attr
15 "dojo/dom-construct", // empty()
16 "dojo/_base/xhr", // xhr.get
17 "dojo/i18n", // i18n.getLocalization
18 "dojo/when"
19], function(kernel, lang, _Widget, _Container, _ContentPaneResizeMixin, string, html, nlsLoading,
20 array, declare, Deferred, dom, domAttr, domConstruct, xhr, i18n, when){
21
22// module:
23// dijit/layout/ContentPane
24
25
26return declare("dijit.layout.ContentPane", [_Widget, _Container, _ContentPaneResizeMixin], {
27 // summary:
28 // A widget containing an HTML fragment, specified inline
29 // or by uri. Fragment may include widgets.
30 //
31 // description:
32 // This widget embeds a document fragment in the page, specified
33 // either by uri, javascript generated markup or DOM reference.
34 // Any widgets within this content are instantiated and managed,
35 // but laid out according to the HTML structure. Unlike IFRAME,
36 // ContentPane embeds a document fragment as would be found
37 // inside the BODY tag of a full HTML document. It should not
38 // contain the HTML, HEAD, or BODY tags.
39 // For more advanced functionality with scripts and
40 // stylesheets, see dojox/layout/ContentPane. This widget may be
41 // used stand alone or as a base class for other widgets.
42 // ContentPane is useful as a child of other layout containers
43 // such as BorderContainer or TabContainer, but note that those
44 // widgets can contain any widget as a child.
45 //
46 // example:
47 // Some quick samples:
48 // To change the innerHTML:
49 // | cp.set('content', '<b>new content</b>')`
50 // Or you can send it a NodeList:
51 // | cp.set('content', dojo.query('div [class=selected]', userSelection))
52 // To do an ajax update:
53 // | cp.set('href', url)
54
55 // href: String
56 // The href of the content that displays now.
57 // Set this at construction if you want to load data externally when the
58 // pane is shown. (Set preload=true to load it immediately.)
59 // Changing href after creation doesn't have any effect; Use set('href', ...);
60 href: "",
61
62 // content: String|DomNode|NodeList|dijit/_Widget
63 // The innerHTML of the ContentPane.
64 // Note that the initialization parameter / argument to set("content", ...)
65 // can be a String, DomNode, Nodelist, or _Widget.
66 content: "",
67
68 // extractContent: Boolean
69 // Extract visible content from inside of `<body> .... </body>`.
70 // I.e., strip `<html>` and `<head>` (and it's contents) from the href
71 extractContent: false,
72
73 // parseOnLoad: Boolean
74 // Parse content and create the widgets, if any.
75 parseOnLoad: true,
76
77 // parserScope: String
78 // Flag passed to parser. Root for attribute names to search for. If scopeName is dojo,
79 // will search for data-dojo-type (or dojoType). For backwards compatibility
80 // reasons defaults to dojo._scopeName (which is "dojo" except when
81 // multi-version support is used, when it will be something like dojo16, dojo20, etc.)
82 parserScope: kernel._scopeName,
83
84 // preventCache: Boolean
85 // Prevent caching of data from href's by appending a timestamp to the href.
86 preventCache: false,
87
88 // preload: Boolean
89 // Force load of data on initialization even if pane is hidden.
90 preload: false,
91
92 // refreshOnShow: Boolean
93 // Refresh (re-download) content when pane goes from hidden to shown
94 refreshOnShow: false,
95
96 // loadingMessage: String
97 // Message that shows while downloading
98 loadingMessage: "<span class='dijitContentPaneLoading'><span class='dijitInline dijitIconLoading'></span>${loadingState}</span>",
99
100 // errorMessage: String
101 // Message that shows if an error occurs
102 errorMessage: "<span class='dijitContentPaneError'><span class='dijitInline dijitIconError'></span>${errorState}</span>",
103
104 // isLoaded: [readonly] Boolean
105 // True if the ContentPane has data in it, either specified
106 // during initialization (via href or inline content), or set
107 // via set('content', ...) / set('href', ...)
108 //
109 // False if it doesn't have any content, or if ContentPane is
110 // still in the process of downloading href.
111 isLoaded: false,
112
113 baseClass: "dijitContentPane",
114
115 /*======
116 // ioMethod: dojo/_base/xhr.get|dojo._base/xhr.post
117 // Function that should grab the content specified via href.
118 ioMethod: dojo.xhrGet,
119 ======*/
120
121 // ioArgs: Object
122 // Parameters to pass to xhrGet() request, for example:
123 // | <div data-dojo-type="dijit/layout/ContentPane" data-dojo-props="href: './bar', ioArgs: {timeout: 500}">
124 ioArgs: {},
125
126 // onLoadDeferred: [readonly] dojo.Deferred
127 // This is the `dojo.Deferred` returned by set('href', ...) and refresh().
128 // Calling onLoadDeferred.then() registers your
129 // callback to be called only once, when the prior set('href', ...) call or
130 // the initial href parameter to the constructor finishes loading.
131 //
132 // This is different than an onLoad() handler which gets called any time any href
133 // or content is loaded.
134 onLoadDeferred: null,
135
136 // Cancel _WidgetBase's _setTitleAttr because we don't want the title attribute (used to specify
137 // tab labels) to be copied to ContentPane.domNode... otherwise a tooltip shows up over the
138 // entire pane.
139 _setTitleAttr: null,
140
141 // Flag to parser that I'll parse my contents, so it shouldn't.
142 stopParser: true,
143
144 // template: [private] Boolean
145 // Flag from the parser that this ContentPane is inside a template
146 // so the contents are pre-parsed.
147 // TODO: this declaration can be commented out in 2.0
148 template: false,
149
150 create: function(params, srcNodeRef){
151 // Convert a srcNodeRef argument into a content parameter, so that the original contents are
152 // processed in the same way as contents set via set("content", ...), calling the parser etc.
153 // Avoid modifying original params object since that breaks NodeList instantiation, see #11906.
154 if((!params || !params.template) && srcNodeRef && !("href" in params) && !("content" in params)){
155 srcNodeRef = dom.byId(srcNodeRef);
156 var df = srcNodeRef.ownerDocument.createDocumentFragment();
157 while(srcNodeRef.firstChild){
158 df.appendChild(srcNodeRef.firstChild);
159 }
160 params = lang.delegate(params, {content: df});
161 }
162 this.inherited(arguments, [params, srcNodeRef]);
163 },
164
165 postMixInProperties: function(){
166 this.inherited(arguments);
167 var messages = i18n.getLocalization("dijit", "loading", this.lang);
168 this.loadingMessage = string.substitute(this.loadingMessage, messages);
169 this.errorMessage = string.substitute(this.errorMessage, messages);
170 },
171
172 buildRendering: function(){
173 this.inherited(arguments);
174
175 // Since we have no template we need to set this.containerNode ourselves, to make getChildren() work.
176 // For subclasses of ContentPane that do have a template, does nothing.
177 if(!this.containerNode){
178 this.containerNode = this.domNode;
179 }
180
181 // remove the title attribute so it doesn't show up when hovering
182 // over a node (TODO: remove in 2.0, no longer needed after #11490)
183 this.domNode.title = "";
184
185 if(!domAttr.get(this.domNode,"role")){
186 this.domNode.setAttribute("role", "group");
187 }
188 },
189
190 startup: function(){
191 // summary:
192 // Call startup() on all children including non _Widget ones like dojo/dnd/Source objects
193
194 // This starts all the widgets
195 this.inherited(arguments);
196
197 // And this catches stuff like dojo/dnd/Source
198 if(this._contentSetter){
199 array.forEach(this._contentSetter.parseResults, function(obj){
200 if(!obj._started && !obj._destroyed && lang.isFunction(obj.startup)){
201 obj.startup();
202 obj._started = true;
203 }
204 }, this);
205 }
206 },
207
208 _startChildren: function(){
209 // summary:
210 // Called when content is loaded. Calls startup on each child widget. Similar to ContentPane.startup()
211 // itself, but avoids marking the ContentPane itself as "restarted" (see #15581).
212
213 // This starts all the widgets
214 array.forEach(this.getChildren(), function(obj){
215 if(!obj._started && !obj._destroyed && lang.isFunction(obj.startup)){
216 obj.startup();
217 obj._started = true;
218 }
219 });
220
221 // And this catches stuff like dojo/dnd/Source
222 if(this._contentSetter){
223 array.forEach(this._contentSetter.parseResults, function(obj){
224 if(!obj._started && !obj._destroyed && lang.isFunction(obj.startup)){
225 obj.startup();
226 obj._started = true;
227 }
228 }, this);
229 }
230 },
231
232 setHref: function(/*String|Uri*/ href){
233 // summary:
234 // Deprecated. Use set('href', ...) instead.
235 kernel.deprecated("dijit.layout.ContentPane.setHref() is deprecated. Use set('href', ...) instead.", "", "2.0");
236 return this.set("href", href);
237 },
238 _setHrefAttr: function(/*String|Uri*/ href){
239 // summary:
240 // Hook so set("href", ...) works.
241 // description:
242 // Reset the (external defined) content of this pane and replace with new url
243 // Note: It delays the download until widget is shown if preload is false.
244 // href:
245 // url to the page you want to get, must be within the same domain as your mainpage
246
247 // Cancel any in-flight requests (a set('href', ...) will cancel any in-flight set('href', ...))
248 this.cancel();
249
250 this.onLoadDeferred = new Deferred(lang.hitch(this, "cancel"));
251 this.onLoadDeferred.then(lang.hitch(this, "onLoad"));
252
253 this._set("href", href);
254
255 // _setHrefAttr() is called during creation and by the user, after creation.
256 // Assuming preload == false, only in the second case do we actually load the URL;
257 // otherwise it's done in startup(), and only if this widget is shown.
258 if(this.preload || (this._created && this._isShown())){
259 this._load();
260 }else{
261 // Set flag to indicate that href needs to be loaded the next time the
262 // ContentPane is made visible
263 this._hrefChanged = true;
264 }
265
266 return this.onLoadDeferred; // Deferred
267 },
268
269 setContent: function(/*String|DomNode|Nodelist*/data){
270 // summary:
271 // Deprecated. Use set('content', ...) instead.
272 kernel.deprecated("dijit.layout.ContentPane.setContent() is deprecated. Use set('content', ...) instead.", "", "2.0");
273 this.set("content", data);
274 },
275 _setContentAttr: function(/*String|DomNode|Nodelist*/data){
276 // summary:
277 // Hook to make set("content", ...) work.
278 // Replaces old content with data content, include style classes from old content
279 // data:
280 // the new Content may be String, DomNode or NodeList
281 //
282 // if data is a NodeList (or an array of nodes) nodes are copied
283 // so you can import nodes from another document implicitly
284
285 // clear href so we can't run refresh and clear content
286 // refresh should only work if we downloaded the content
287 this._set("href", "");
288
289 // Cancel any in-flight requests (a set('content', ...) will cancel any in-flight set('href', ...))
290 this.cancel();
291
292 // Even though user is just setting content directly, still need to define an onLoadDeferred
293 // because the _onLoadHandler() handler is still getting called from setContent()
294 this.onLoadDeferred = new Deferred(lang.hitch(this, "cancel"));
295 if(this._created){
296 // For back-compat reasons, call onLoad() for set('content', ...)
297 // calls but not for content specified in srcNodeRef (ie: <div data-dojo-type=ContentPane>...</div>)
298 // or as initialization parameter (ie: new ContentPane({content: ...})
299 this.onLoadDeferred.then(lang.hitch(this, "onLoad"));
300 }
301
302 this._setContent(data || "");
303
304 this._isDownloaded = false; // mark that content is from a set('content') not a set('href')
305
306 return this.onLoadDeferred; // Deferred
307 },
308 _getContentAttr: function(){
309 // summary:
310 // Hook to make get("content") work
311 return this.containerNode.innerHTML;
312 },
313
314 cancel: function(){
315 // summary:
316 // Cancels an in-flight download of content
317 if(this._xhrDfd && (this._xhrDfd.fired == -1)){
318 this._xhrDfd.cancel();
319 }
320 delete this._xhrDfd; // garbage collect
321
322 this.onLoadDeferred = null;
323 },
324
325 destroy: function(){
326 this.cancel();
327 this.inherited(arguments);
328 },
329
330 destroyRecursive: function(/*Boolean*/ preserveDom){
331 // summary:
332 // Destroy the ContentPane and its contents
333
334 // if we have multiple controllers destroying us, bail after the first
335 if(this._beingDestroyed){
336 return;
337 }
338 this.inherited(arguments);
339 },
340
341 _onShow: function(){
342 // summary:
343 // Called when the ContentPane is made visible
344 // description:
345 // For a plain ContentPane, this is called on initialization, from startup().
346 // If the ContentPane is a hidden pane of a TabContainer etc., then it's
347 // called whenever the pane is made visible.
348 //
349 // Does necessary processing, including href download and layout/resize of
350 // child widget(s)
351
352 this.inherited(arguments);
353
354 if(this.href){
355 if(!this._xhrDfd && // if there's an href that isn't already being loaded
356 (!this.isLoaded || this._hrefChanged || this.refreshOnShow)
357 ){
358 return this.refresh(); // If child has an href, promise that fires when the load is complete
359 }
360 }
361 },
362
363 refresh: function(){
364 // summary:
365 // [Re]download contents of href and display
366 // description:
367 // 1. cancels any currently in-flight requests
368 // 2. posts "loading..." message
369 // 3. sends XHR to download new data
370
371 // Cancel possible prior in-flight request
372 this.cancel();
373
374 this.onLoadDeferred = new Deferred(lang.hitch(this, "cancel"));
375 this.onLoadDeferred.then(lang.hitch(this, "onLoad"));
376 this._load();
377 return this.onLoadDeferred; // If child has an href, promise that fires when refresh is complete
378 },
379
380 _load: function(){
381 // summary:
382 // Load/reload the href specified in this.href
383
384 // display loading message
385 this._setContent(this.onDownloadStart(), true);
386
387 var self = this;
388 var getArgs = {
389 preventCache: (this.preventCache || this.refreshOnShow),
390 url: this.href,
391 handleAs: "text"
392 };
393 if(lang.isObject(this.ioArgs)){
394 lang.mixin(getArgs, this.ioArgs);
395 }
396
397 var hand = (this._xhrDfd = (this.ioMethod || xhr.get)(getArgs)),
398 returnedHtml;
399
400 hand.then(
401 function(html){
402 returnedHtml = html;
403 try{
404 self._isDownloaded = true;
405 return self._setContent(html, false);
406 }catch(err){
407 self._onError('Content', err); // onContentError
408 }
409 },
410 function(err){
411 if(!hand.canceled){
412 // show error message in the pane
413 self._onError('Download', err); // onDownloadError
414 }
415 delete self._xhrDfd;
416 return err;
417 }
418 ).then(function(){
419 self.onDownloadEnd();
420 delete self._xhrDfd;
421 return returnedHtml;
422 });
423
424 // Remove flag saying that a load is needed
425 delete this._hrefChanged;
426 },
427
428 _onLoadHandler: function(data){
429 // summary:
430 // This is called whenever new content is being loaded
431 this._set("isLoaded", true);
432 try{
433 this.onLoadDeferred.resolve(data);
434 }catch(e){
435 console.error('Error '+this.widgetId+' running custom onLoad code: ' + e.message);
436 }
437 },
438
439 _onUnloadHandler: function(){
440 // summary:
441 // This is called whenever the content is being unloaded
442 this._set("isLoaded", false);
443 try{
444 this.onUnload();
445 }catch(e){
446 console.error('Error '+this.widgetId+' running custom onUnload code: ' + e.message);
447 }
448 },
449
450 destroyDescendants: function(/*Boolean*/ preserveDom){
451 // summary:
452 // Destroy all the widgets inside the ContentPane and empty containerNode
453
454 // Make sure we call onUnload (but only when the ContentPane has real content)
455 if(this.isLoaded){
456 this._onUnloadHandler();
457 }
458
459 // Even if this.isLoaded == false there might still be a "Loading..." message
460 // to erase, so continue...
461
462 // For historical reasons we need to delete all widgets under this.containerNode,
463 // even ones that the user has created manually.
464 var setter = this._contentSetter;
465 array.forEach(this.getChildren(), function(widget){
466 if(widget.destroyRecursive){
467 // All widgets will hit this branch
468 widget.destroyRecursive(preserveDom);
469 }else if(widget.destroy){
470 // Things like dojo/dnd/Source have destroy(), not destroyRecursive()
471 widget.destroy(preserveDom);
472 }
473 widget._destroyed = true;
474 });
475 if(setter){
476 // Most of the widgets in setter.parseResults have already been destroyed, but
477 // things like Menu that have been moved to <body> haven't yet
478 array.forEach(setter.parseResults, function(widget){
479 if(!widget._destroyed){
480 if(widget.destroyRecursive){
481 // All widgets will hit this branch
482 widget.destroyRecursive(preserveDom);
483 }else if(widget.destroy){
484 // Things like dojo/dnd/Source have destroy(), not destroyRecursive()
485 widget.destroy(preserveDom);
486 }
487 widget._destroyed = true;
488 }
489 });
490 delete setter.parseResults;
491 }
492
493 // And then clear away all the DOM nodes
494 if(!preserveDom){
495 domConstruct.empty(this.containerNode);
496 }
497
498 // Delete any state information we have about current contents
499 delete this._singleChild;
500 },
501
502 _setContent: function(/*String|DocumentFragment*/ cont, /*Boolean*/ isFakeContent){
503 // summary:
504 // Insert the content into the container node
505 // returns:
506 // Returns a Deferred promise that is resolved when the content is parsed.
507
508 // first get rid of child widgets
509 this.destroyDescendants();
510
511 // html.set will take care of the rest of the details
512 // we provide an override for the error handling to ensure the widget gets the errors
513 // configure the setter instance with only the relevant widget instance properties
514 // NOTE: unless we hook into attr, or provide property setters for each property,
515 // we need to re-configure the ContentSetter with each use
516 var setter = this._contentSetter;
517 if(! (setter && setter instanceof html._ContentSetter)){
518 setter = this._contentSetter = new html._ContentSetter({
519 node: this.containerNode,
520 _onError: lang.hitch(this, this._onError),
521 onContentError: lang.hitch(this, function(e){
522 // fires if a domfault occurs when we are appending this.errorMessage
523 // like for instance if domNode is a UL and we try append a DIV
524 var errMess = this.onContentError(e);
525 try{
526 this.containerNode.innerHTML = errMess;
527 }catch(e){
528 console.error('Fatal '+this.id+' could not change content due to '+e.message, e);
529 }
530 })/*,
531 _onError */
532 });
533 }
534
535 var setterParams = lang.mixin({
536 cleanContent: this.cleanContent,
537 extractContent: this.extractContent,
538 parseContent: !cont.domNode && this.parseOnLoad,
539 parserScope: this.parserScope,
540 startup: false,
541 dir: this.dir,
542 lang: this.lang,
543 textDir: this.textDir
544 }, this._contentSetterParams || {});
545
546 var p = setter.set( (lang.isObject(cont) && cont.domNode) ? cont.domNode : cont, setterParams );
547
548 // dojox/layout/html/_base::_ContentSetter.set() returns a Promise that indicates when everything is completed.
549 // dojo/html::_ContentSetter.set() currently returns the DOMNode, but that will be changed for 2.0.
550 // So, if set() returns a promise then use it, otherwise fallback to waiting on setter.parseDeferred
551 var self = this;
552 return when(p && p.then ? p : setter.parseDeferred, function(){
553 // setter params must be pulled afresh from the ContentPane each time
554 delete self._contentSetterParams;
555
556 if(!isFakeContent){
557 if(self._started){
558 // Startup each top level child widget (and they will start their children, recursively)
559 self._startChildren();
560
561 // Call resize() on each of my child layout widgets,
562 // or resize() on my single child layout widget...
563 // either now (if I'm currently visible) or when I become visible
564 self._scheduleLayout();
565 }
566 self._onLoadHandler(cont);
567 }
568 });
569 },
570
571 _onError: function(type, err, consoleText){
572 this.onLoadDeferred.reject(err);
573
574 // shows user the string that is returned by on[type]Error
575 // override on[type]Error and return your own string to customize
576 var errText = this['on' + type + 'Error'].call(this, err);
577 if(consoleText){
578 console.error(consoleText, err);
579 }else if(errText){// a empty string won't change current content
580 this._setContent(errText, true);
581 }
582 },
583
584 // EVENT's, should be overide-able
585 onLoad: function(/*===== data =====*/){
586 // summary:
587 // Event hook, is called after everything is loaded and widgetified
588 // tags:
589 // callback
590 },
591
592 onUnload: function(){
593 // summary:
594 // Event hook, is called before old content is cleared
595 // tags:
596 // callback
597 },
598
599 onDownloadStart: function(){
600 // summary:
601 // Called before download starts.
602 // description:
603 // The string returned by this function will be the html
604 // that tells the user we are loading something.
605 // Override with your own function if you want to change text.
606 // tags:
607 // extension
608 return this.loadingMessage;
609 },
610
611 onContentError: function(/*Error*/ /*===== error =====*/){
612 // summary:
613 // Called on DOM faults, require faults etc. in content.
614 //
615 // In order to display an error message in the pane, return
616 // the error message from this method, as an HTML string.
617 //
618 // By default (if this method is not overriden), it returns
619 // nothing, so the error message is just printed to the console.
620 // tags:
621 // extension
622 },
623
624 onDownloadError: function(/*Error*/ /*===== error =====*/){
625 // summary:
626 // Called when download error occurs.
627 //
628 // In order to display an error message in the pane, return
629 // the error message from this method, as an HTML string.
630 //
631 // Default behavior (if this method is not overriden) is to display
632 // the error message inside the pane.
633 // tags:
634 // extension
635 return this.errorMessage;
636 },
637
638 onDownloadEnd: function(){
639 // summary:
640 // Called when download is finished.
641 // tags:
642 // callback
643 }
644});
645
646});