]>
git.wh0rd.org - tt-rss.git/blob - lib/dijit/layout/ContentPane.js.uncompressed.js
1 define("dijit/layout/ContentPane", [
2 "dojo/_base/kernel", // kernel.deprecated
3 "dojo/_base/lang", // lang.mixin lang.delegate lang.hitch lang.isFunction lang.isObject
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
19 ], function(kernel
, lang
, _Widget
, _Container
, _ContentPaneResizeMixin
, string
, html
, nlsLoading
,
20 array
, declare
, Deferred
, dom
, domAttr
, domConstruct
, xhr
, i18n
, when
){
23 // dijit/layout/ContentPane
26 return declare("dijit.layout.ContentPane", [_Widget
, _Container
, _ContentPaneResizeMixin
], {
28 // A widget containing an HTML fragment, specified inline
29 // or by uri. Fragment may include widgets.
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.
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)
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', ...);
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.
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,
73 // parseOnLoad: Boolean
74 // Parse content and create the widgets, if any.
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
,
84 // preventCache: Boolean
85 // Prevent caching of data from href's by appending a timestamp to the href.
89 // Force load of data on initialization even if pane is hidden.
92 // refreshOnShow: Boolean
93 // Refresh (re-download) content when pane goes from hidden to shown
96 // loadingMessage: String
97 // Message that shows while downloading
98 loadingMessage
: "<span class='dijitContentPaneLoading'><span class='dijitInline dijitIconLoading'></span>${loadingState}</span>",
100 // errorMessage: String
101 // Message that shows if an error occurs
102 errorMessage
: "<span class='dijitContentPaneError'><span class='dijitInline dijitIconError'></span>${errorState}</span>",
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', ...)
109 // False if it doesn't have any content, or if ContentPane is
110 // still in the process of downloading href.
113 baseClass
: "dijitContentPane",
116 // ioMethod: dojo/_base/xhr.get|dojo._base/xhr.post
117 // Function that should grab the content specified via href.
118 ioMethod: dojo.xhrGet,
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}">
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.
132 // This is different than an onLoad() handler which gets called any time any href
133 // or content is loaded.
134 onLoadDeferred
: null,
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
141 // Flag to parser that I'll parse my contents, so it shouldn't.
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
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
);
160 params
= lang
.delegate(params
, {content
: df
});
162 this.inherited(arguments
, [params
, srcNodeRef
]);
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
);
172 buildRendering: function(){
173 this.inherited(arguments
);
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
;
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
= "";
185 if(!domAttr
.get(this.domNode
,"role")){
186 this.domNode
.setAttribute("role", "group");
192 // Call startup() on all children including non _Widget ones like dojo/dnd/Source objects
194 // This starts all the widgets
195 this.inherited(arguments
);
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
)){
208 _startChildren: function(){
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).
213 // This starts all the widgets
214 array
.forEach(this.getChildren(), function(obj
){
215 if(!obj
._started
&& !obj
._destroyed
&& lang
.isFunction(obj
.startup
)){
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
)){
232 setHref: function(/*String|Uri*/ href
){
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
);
238 _setHrefAttr: function(/*String|Uri*/ href
){
240 // Hook so set("href", ...) works.
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.
245 // url to the page you want to get, must be within the same domain as your mainpage
247 // Cancel any in-flight requests (a set('href', ...) will cancel any in-flight set('href', ...))
250 this.onLoadDeferred
= new Deferred(lang
.hitch(this, "cancel"));
251 this.onLoadDeferred
.then(lang
.hitch(this, "onLoad"));
253 this._set("href", href
);
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())){
261 // Set flag to indicate that href needs to be loaded the next time the
262 // ContentPane is made visible
263 this._hrefChanged
= true;
266 return this.onLoadDeferred
; // Deferred
269 setContent: function(/*String|DomNode|Nodelist*/data
){
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
);
275 _setContentAttr: function(/*String|DomNode|Nodelist*/data
){
277 // Hook to make set("content", ...) work.
278 // Replaces old content with data content, include style classes from old content
280 // the new Content may be String, DomNode or NodeList
282 // if data is a NodeList (or an array of nodes) nodes are copied
283 // so you can import nodes from another document implicitly
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", "");
289 // Cancel any in-flight requests (a set('content', ...) will cancel any in-flight set('href', ...))
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"));
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"));
302 this._setContent(data
|| "");
304 this._isDownloaded
= false; // mark that content is from a set('content') not a set('href')
306 return this.onLoadDeferred
; // Deferred
308 _getContentAttr: function(){
310 // Hook to make get("content") work
311 return this.containerNode
.innerHTML
;
316 // Cancels an in-flight download of content
317 if(this._xhrDfd
&& (this._xhrDfd
.fired
== -1)){
318 this._xhrDfd
.cancel();
320 delete this._xhrDfd
; // garbage collect
322 this.onLoadDeferred
= null;
327 this.inherited(arguments
);
330 destroyRecursive: function(/*Boolean*/ preserveDom
){
332 // Destroy the ContentPane and its contents
334 // if we have multiple controllers destroying us, bail after the first
335 if(this._beingDestroyed
){
338 this.inherited(arguments
);
343 // Called when the ContentPane is made visible
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.
349 // Does necessary processing, including href download and layout/resize of
352 this.inherited(arguments
);
355 if(!this._xhrDfd
&& // if there's an href that isn't already being loaded
356 (!this.isLoaded
|| this._hrefChanged
|| this.refreshOnShow
)
358 return this.refresh(); // If child has an href, promise that fires when the load is complete
365 // [Re]download contents of href and display
367 // 1. cancels any currently in-flight requests
368 // 2. posts "loading..." message
369 // 3. sends XHR to download new data
371 // Cancel possible prior in-flight request
374 this.onLoadDeferred
= new Deferred(lang
.hitch(this, "cancel"));
375 this.onLoadDeferred
.then(lang
.hitch(this, "onLoad"));
377 return this.onLoadDeferred
; // If child has an href, promise that fires when refresh is complete
382 // Load/reload the href specified in this.href
384 // display loading message
385 this._setContent(this.onDownloadStart(), true);
389 preventCache
: (this.preventCache
|| this.refreshOnShow
),
393 if(lang
.isObject(this.ioArgs
)){
394 lang
.mixin(getArgs
, this.ioArgs
);
397 var hand
= (this._xhrDfd
= (this.ioMethod
|| xhr
.get)(getArgs
)),
404 self
._isDownloaded
= true;
405 return self
._setContent(html
, false);
407 self
._onError('Content', err
); // onContentError
412 // show error message in the pane
413 self
._onError('Download', err
); // onDownloadError
419 self
.onDownloadEnd();
424 // Remove flag saying that a load is needed
425 delete this._hrefChanged
;
428 _onLoadHandler: function(data
){
430 // This is called whenever new content is being loaded
431 this._set("isLoaded", true);
433 this.onLoadDeferred
.resolve(data
);
435 console
.error('Error '+this.widgetId
+' running custom onLoad code: ' + e
.message
);
439 _onUnloadHandler: function(){
441 // This is called whenever the content is being unloaded
442 this._set("isLoaded", false);
446 console
.error('Error '+this.widgetId
+' running custom onUnload code: ' + e
.message
);
450 destroyDescendants: function(/*Boolean*/ preserveDom
){
452 // Destroy all the widgets inside the ContentPane and empty containerNode
454 // Make sure we call onUnload (but only when the ContentPane has real content)
456 this._onUnloadHandler();
459 // Even if this.isLoaded == false there might still be a "Loading..." message
460 // to erase, so continue...
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
);
473 widget
._destroyed
= true;
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
);
487 widget
._destroyed
= true;
490 delete setter
.parseResults
;
493 // And then clear away all the DOM nodes
495 domConstruct
.empty(this.containerNode
);
498 // Delete any state information we have about current contents
499 delete this._singleChild
;
502 _setContent: function(/*String|DocumentFragment*/ cont
, /*Boolean*/ isFakeContent
){
504 // Insert the content into the container node
506 // Returns a Deferred promise that is resolved when the content is parsed.
508 // first get rid of child widgets
509 this.destroyDescendants();
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
);
526 this.containerNode
.innerHTML
= errMess
;
528 console
.error('Fatal '+this.id
+' could not change content due to '+e
.message
, e
);
535 var setterParams
= lang
.mixin({
536 cleanContent
: this.cleanContent
,
537 extractContent
: this.extractContent
,
538 parseContent
: !cont
.domNode
&& this.parseOnLoad
,
539 parserScope
: this.parserScope
,
543 textDir
: this.textDir
544 }, this._contentSetterParams
|| {});
546 var p
= setter
.set( (lang
.isObject(cont
) && cont
.domNode
) ? cont
.domNode
: cont
, setterParams
);
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
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
;
558 // Startup each top level child widget (and they will start their children, recursively)
559 self
._startChildren();
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();
566 self
._onLoadHandler(cont
);
571 _onError: function(type
, err
, consoleText
){
572 this.onLoadDeferred
.reject(err
);
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
);
578 console
.error(consoleText
, err
);
579 }else if(errText
){// a empty string won't change current content
580 this._setContent(errText
, true);
584 // EVENT's, should be overide-able
585 onLoad: function(/*===== data =====*/){
587 // Event hook, is called after everything is loaded and widgetified
592 onUnload: function(){
594 // Event hook, is called before old content is cleared
599 onDownloadStart: function(){
601 // Called before download starts.
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.
608 return this.loadingMessage
;
611 onContentError: function(/*Error*/ /*===== error =====*/){
613 // Called on DOM faults, require faults etc. in content.
615 // In order to display an error message in the pane, return
616 // the error message from this method, as an HTML string.
618 // By default (if this method is not overriden), it returns
619 // nothing, so the error message is just printed to the console.
624 onDownloadError: function(/*Error*/ /*===== error =====*/){
626 // Called when download error occurs.
628 // In order to display an error message in the pane, return
629 // the error message from this method, as an HTML string.
631 // Default behavior (if this method is not overriden) is to display
632 // the error message inside the pane.
635 return this.errorMessage
;
638 onDownloadEnd: function(){
640 // Called when download is finished.