1 if(!dojo._hasResource["dijit.layout.ContentPane"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
2 dojo._hasResource["dijit.layout.ContentPane"] = true;
3 dojo.provide("dijit.layout.ContentPane");
5 dojo.require("dijit._Widget");
6 dojo.require("dijit.layout._LayoutWidget");
8 dojo.require("dojo.parser");
9 dojo.require("dojo.string");
10 dojo.requireLocalization("dijit", "loading", null, "zh,pt,da,tr,ru,ROOT,de,sv,ja,he,fi,nb,el,ar,pt-pt,cs,fr,es,ko,nl,zh-tw,pl,it,hu");
13 "dijit.layout.ContentPane",
17 // A widget that acts as a Container for other widgets, and includes a ajax interface
19 // A widget that can be used as a standalone widget
20 // or as a baseclass for other widgets
21 // Handles replacement of document fragment using either external uri or javascript
22 // generated markup or DOM content, instantiating widgets within that content.
23 // Don't confuse it with an iframe, it only needs/wants document fragments.
24 // It's useful as a child of LayoutContainer, SplitContainer, or TabContainer.
25 // But note that those classes can contain any widget as a child.
27 // Some quick samples:
28 // To change the innerHTML use .setContent('<b>new content</b>')
30 // Or you can send it a NodeList, .setContent(dojo.query('div [class=selected]', userSelection))
31 // please note that the nodes in NodeList will copied, not moved
33 // To do a ajax update use .setHref('url')
36 // The href of the content that displays now.
37 // Set this at construction if you want to load data externally when the
38 // pane is shown. (Set preload=true to load it immediately.)
39 // Changing href after creation doesn't have any effect; see setHref();
42 // extractContent: Boolean
43 // Extract visible content from inside of <body> .... </body>
44 extractContent: false,
46 // parseOnLoad: Boolean
47 // parse content and create the widgets, if any
50 // preventCache: Boolean
51 // Cache content retreived externally
55 // Force load of data even if pane is hidden.
58 // refreshOnShow: Boolean
59 // Refresh (re-download) content when pane goes from hidden to shown
62 // loadingMessage: String
63 // Message that shows while downloading
64 loadingMessage: "<span class='dijitContentPaneLoading'>${loadingState}</span>",
66 // errorMessage: String
67 // Message that shows if an error occurs
68 errorMessage: "<span class='dijitContentPaneError'>${errorState}</span>",
71 // Tells loading status see onLoad|onUnload for event hooks
75 // Class name to apply to ContentPane dom nodes
76 // TODO: this should be called "baseClass" like in the other widgets
77 "class": "dijitContentPane",
79 // doLayout: String/Boolean
80 // false - don't adjust size of children
81 // true - looks for the first sizable child widget (ie, having resize() method) and sets it's size to
82 // however big the ContentPane is (TODO: implement)
83 // auto - if there is a single sizable child widget (ie, having resize() method), set it's size to
84 // however big the ContentPane is
87 postCreate: function(){
88 // remove the title attribute so it doesn't show up when i hover
90 this.domNode.title = "";
92 if(!this.containerNode){
93 // make getDescendants() work
94 this.containerNode = this.domNode;
101 var messages = dojo.i18n.getLocalization("dijit", "loading", this.lang);
102 this.loadingMessage = dojo.string.substitute(this.loadingMessage, messages);
103 this.errorMessage = dojo.string.substitute(this.errorMessage, messages);
104 var curRole = dijit.getWaiRole(this.domNode);
106 dijit.setWaiRole(this.domNode, "group");
109 // for programatically created ContentPane (with <span> tag), need to muck w/CSS
110 // or it's as though overflow:visible is set
111 dojo.addClass(this.domNode, this["class"]);
115 if(this._started){ return; }
116 if(this.doLayout != "false" && this.doLayout !== false){
117 this._checkIfSingleChild();
118 if(this._singleChild){
119 this._singleChild.startup();
123 this.inherited(arguments);
126 _checkIfSingleChild: function(){
128 // Test if we have exactly one widget as a child, and if so assume that we are a container for that widget,
129 // and should propogate startup() and resize() calls to it.
131 // TODO: if there are two child widgets (a data store and a TabContainer, for example),
132 // should still find the TabContainer
133 var childNodes = dojo.query(">", this.containerNode || this.domNode),
134 childWidgets = childNodes.filter("[widgetId]");
136 if(childNodes.length == 1 && childWidgets.length == 1){
137 this.isContainer = true;
138 this._singleChild = dijit.byNode(childWidgets[0]);
140 delete this.isContainer;
141 delete this._singleChild;
147 // Force a refresh (re-download) of content, be sure to turn off cache
149 // we return result of _prepareLoad here to avoid code dup. in dojox.layout.ContentPane
150 return this._prepareLoad(true);
153 setHref: function(/*String|Uri*/ href){
155 // Reset the (external defined) content of this pane and replace with new url
156 // Note: It delays the download until widget is shown if preload is false
158 // url to the page you want to get, must be within the same domain as your mainpage
161 // we return result of _prepareLoad here to avoid code dup. in dojox.layout.ContentPane
162 return this._prepareLoad();
165 setContent: function(/*String|DomNode|Nodelist*/data){
167 // Replaces old content with data content, include style classes from old content
169 // the new Content may be String, DomNode or NodeList
171 // if data is a NodeList (or an array of nodes) nodes are copied
172 // so you can import nodes from another document implicitly
174 // clear href so we cant run refresh and clear content
175 // refresh should only work if we downloaded the content
176 if(!this._isDownloaded){
178 this._onUnloadHandler();
181 this._setContent(data || "");
183 this._isDownloaded = false; // must be set after _setContent(..), pathadjust in dojox.layout.ContentPane
185 if(this.parseOnLoad){
186 this._createSubWidgets();
189 if(this.doLayout != "false" && this.doLayout !== false){
190 this._checkIfSingleChild();
191 if(this._singleChild && this._singleChild.resize){
192 this._singleChild.startup();
193 this._singleChild.resize(this._contentBox || dojo.contentBox(this.containerNode || this.domNode));
197 this._onLoadHandler();
202 // Cancels a inflight download of content
203 if(this._xhrDfd && (this._xhrDfd.fired == -1)){
204 this._xhrDfd.cancel();
206 delete this._xhrDfd; // garbage collect
210 // if we have multiple controllers destroying us, bail after the first
211 if(this._beingDestroyed){
214 // make sure we call onUnload
215 this._onUnloadHandler();
216 this._beingDestroyed = true;
217 this.inherited("destroy",arguments);
220 resize: function(size){
221 dojo.marginBox(this.domNode, size);
223 // Compute content box size in case we [later] need to size child
224 // If either height or width wasn't specified by the user, then query node for it.
225 // But note that setting the margin box and then immediately querying dimensions may return
226 // inaccurate results, so try not to depend on it.
227 var node = this.containerNode || this.domNode,
228 mb = dojo.mixin(dojo.marginBox(node), size||{});
230 this._contentBox = dijit.layout.marginBox2contentBox(node, mb);
232 // If we have a single widget child then size it to fit snugly within my borders
233 if(this._singleChild && this._singleChild.resize){
234 this._singleChild.resize(this._contentBox);
238 _prepareLoad: function(forceLoad){
239 // sets up for a xhrLoad, load is deferred until widget onShow
240 // cancels a inflight download
242 this.isLoaded = false;
243 this._loadCheck(forceLoad);
246 _isShown: function(){
247 // summary: returns true if the content is currently shown
249 return this.open; // for TitlePane, etc.
251 var node = this.domNode;
252 return (node.style.display != 'none') && (node.style.visibility != 'hidden');
256 _loadCheck: function(/*Boolean*/ forceLoad){
257 // call this when you change onShow (onSelected) status when selected in parent container
258 // it's used as a trigger for href download when this.domNode.display != 'none'
261 // if no href -> bail
262 // forceLoad -> always load
263 // this.preload -> load when download not in progress, domNode display doesn't matter
264 // this.refreshOnShow -> load when download in progress bails, domNode display !='none' AND
265 // this.open !== false (undefined is ok), isLoaded doesn't matter
266 // else -> load when download not in progress, if this.open !== false (undefined is ok) AND
267 // domNode display != 'none', isLoaded must be false
269 var displayState = this._isShown();
273 (this.preload && !this._xhrDfd) ||
274 (this.refreshOnShow && displayState && !this._xhrDfd) ||
275 (!this.isLoaded && displayState && !this._xhrDfd)
278 this._downloadExternalContent();
282 _downloadExternalContent: function(){
283 this._onUnloadHandler();
285 // display loading message
287 this.onDownloadStart.call(this)
292 preventCache: (this.preventCache || this.refreshOnShow),
296 if(dojo.isObject(this.ioArgs)){
297 dojo.mixin(getArgs, this.ioArgs);
300 var hand = this._xhrDfd = (this.ioMethod || dojo.xhrGet)(getArgs);
302 hand.addCallback(function(html){
304 self.onDownloadEnd.call(self);
305 self._isDownloaded = true;
306 self.setContent.call(self, html); // onload event is called from here
308 self._onError.call(self, 'Content', err); // onContentError
314 hand.addErrback(function(err){
316 // show error message in the pane
317 self._onError.call(self, 'Download', err); // onDownloadError
324 _onLoadHandler: function(){
325 this.isLoaded = true;
327 this.onLoad.call(this);
329 console.error('Error '+this.widgetId+' running custom onLoad code');
333 _onUnloadHandler: function(){
334 this.isLoaded = false;
337 this.onUnload.call(this);
339 console.error('Error '+this.widgetId+' running custom onUnload code');
343 _setContent: function(cont){
344 this.destroyDescendants();
347 var node = this.containerNode || this.domNode;
348 while(node.firstChild){
349 dojo._destroyElement(node.firstChild);
351 if(typeof cont == "string"){
352 // dijit.ContentPane does only minimal fixes,
353 // No pathAdjustments, script retrieval, style clean etc
354 // some of these should be available in the dojox.layout.ContentPane
355 if(this.extractContent){
356 match = cont.match(/<body[^>]*>\s*([\s\S]+)\s*<\/body>/im);
357 if(match){ cont = match[1]; }
359 node.innerHTML = cont;
361 // domNode or NodeList
362 if(cont.nodeType){ // domNode (htmlNode 1 or textNode 3)
363 node.appendChild(cont);
364 }else{// nodelist or array such as dojo.Nodelist
365 dojo.forEach(cont, function(n){
366 node.appendChild(n.cloneNode(true));
371 // check if a domfault occurs when we are appending this.errorMessage
372 // like for instance if domNode is a UL and we try append a DIV
373 var errMess = this.onContentError(e);
375 node.innerHTML = errMess;
377 console.error('Fatal '+this.id+' could not change content due to '+e.message, e);
382 _onError: function(type, err, consoleText){
383 // shows user the string that is returned by on[type]Error
384 // overide on[type]Error and return your own string to customize
385 var errText = this['on' + type + 'Error'].call(this, err);
387 console.error(consoleText, err);
388 }else if(errText){// a empty string won't change current content
389 this._setContent.call(this, errText);
393 _createSubWidgets: function(){
394 // summary: scan my contents and create subwidgets
395 var rootNode = this.containerNode || this.domNode;
397 dojo.parser.parse(rootNode, true);
399 this._onError('Content', e, "Couldn't create widgets in "+this.id
400 +(this.href ? " from "+this.href : ""));
404 // EVENT's, should be overide-able
407 // Event hook, is called after everything is loaded and widgetified
410 onUnload: function(e){
412 // Event hook, is called before old content is cleared
415 onDownloadStart: function(){
417 // called before download starts
418 // the string returned by this function will be the html
419 // that tells the user we are loading something
420 // override with your own function if you want to change text
421 return this.loadingMessage;
424 onContentError: function(/*Error*/ error){
426 // called on DOM faults, require fault etc in content
427 // default is to display errormessage inside pane
430 onDownloadError: function(/*Error*/ error){
432 // Called when download error occurs, default is to display
433 // errormessage inside pane. Overide function to change that.
434 // The string returned by this function will be the html
435 // that tells the user a error happend
436 return this.errorMessage;
439 onDownloadEnd: function(){
441 // called when download is finished