]> git.pond.sub.org Git - eow/blob - static/dojo-release-1.1.1/dijit/layout/ContentPane.js
add Dojo 1.1.1
[eow] / static / dojo-release-1.1.1 / dijit / layout / ContentPane.js
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");
4
5 dojo.require("dijit._Widget");
6 dojo.require("dijit.layout._LayoutWidget");
7
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");
11
12 dojo.declare(
13         "dijit.layout.ContentPane",
14         dijit._Widget,
15 {
16         // summary:
17         //              A widget that acts as a Container for other widgets, and includes a ajax interface
18         // description:
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.
26         // example:
27         //              Some quick samples:
28         //              To change the innerHTML use .setContent('<b>new content</b>')
29         //
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
32         //
33         //              To do a ajax update use .setHref('url')
34         //
35         // href: String
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();
40         href: "",
41
42         // extractContent: Boolean
43         //      Extract visible content from inside of <body> .... </body>
44         extractContent: false,
45
46         // parseOnLoad: Boolean
47         //      parse content and create the widgets, if any
48         parseOnLoad:    true,
49
50         // preventCache: Boolean
51         //              Cache content retreived externally
52         preventCache:   false,
53
54         // preload: Boolean
55         //      Force load of data even if pane is hidden.
56         preload: false,
57
58         // refreshOnShow: Boolean
59         //              Refresh (re-download) content when pane goes from hidden to shown
60         refreshOnShow: false,
61
62         // loadingMessage: String
63         //      Message that shows while downloading
64         loadingMessage: "<span class='dijitContentPaneLoading'>${loadingState}</span>", 
65
66         // errorMessage: String
67         //      Message that shows if an error occurs
68         errorMessage: "<span class='dijitContentPaneError'>${errorState}</span>", 
69
70         // isLoaded: Boolean
71         //      Tells loading status see onLoad|onUnload for event hooks
72         isLoaded: false,
73
74         // class: String
75         //      Class name to apply to ContentPane dom nodes
76         // TODO: this should be called "baseClass" like in the other widgets
77         "class": "dijitContentPane",
78
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
85         doLayout: "auto",
86
87         postCreate: function(){
88                 // remove the title attribute so it doesn't show up when i hover
89                 // over a node
90                 this.domNode.title = "";
91
92                 if(!this.containerNode){
93                         // make getDescendants() work
94                         this.containerNode = this.domNode;
95                 }
96
97                 if(this.preload){
98                         this._loadCheck();
99                 }
100
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);
105                 if (!curRole){
106                         dijit.setWaiRole(this.domNode, "group");
107                 }
108
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"]);
112         },
113
114         startup: function(){
115                 if(this._started){ return; }
116                 if(this.doLayout != "false" && this.doLayout !== false){
117                         this._checkIfSingleChild();
118                         if(this._singleChild){
119                                 this._singleChild.startup();
120                         }
121                 }
122                 this._loadCheck();
123                 this.inherited(arguments);
124         },
125
126         _checkIfSingleChild: function(){
127                 // summary:
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.
130
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]");
135
136                 if(childNodes.length == 1 && childWidgets.length == 1){
137                         this.isContainer = true;
138                         this._singleChild = dijit.byNode(childWidgets[0]);
139                 }else{
140                         delete this.isContainer;
141                         delete this._singleChild;
142                 }
143         },
144
145         refresh: function(){
146                 // summary:
147                 //      Force a refresh (re-download) of content, be sure to turn off cache
148
149                 // we return result of _prepareLoad here to avoid code dup. in dojox.layout.ContentPane
150                 return this._prepareLoad(true);
151         },
152
153         setHref: function(/*String|Uri*/ href){
154                 // summary:
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
157                 //      href:
158                 //              url to the page you want to get, must be within the same domain as your mainpage
159                 this.href = href;
160
161                 // we return result of _prepareLoad here to avoid code dup. in dojox.layout.ContentPane
162                 return this._prepareLoad();
163         },
164
165         setContent: function(/*String|DomNode|Nodelist*/data){
166                 // summary:
167                 //              Replaces old content with data content, include style classes from old content
168                 //      data:
169                 //              the new Content may be String, DomNode or NodeList
170                 //
171                 //              if data is a NodeList (or an array of nodes) nodes are copied
172                 //              so you can import nodes from another document implicitly
173
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){
177                         this.href = "";
178                         this._onUnloadHandler();
179                 }
180
181                 this._setContent(data || "");
182
183                 this._isDownloaded = false; // must be set after _setContent(..), pathadjust in dojox.layout.ContentPane
184
185                 if(this.parseOnLoad){
186                         this._createSubWidgets();
187                 }
188
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));
194                         }
195                 }
196
197                 this._onLoadHandler();
198         },
199
200         cancel: function(){
201                 // summary:
202                 //              Cancels a inflight download of content
203                 if(this._xhrDfd && (this._xhrDfd.fired == -1)){
204                         this._xhrDfd.cancel();
205                 }
206                 delete this._xhrDfd; // garbage collect
207         },
208
209         destroy: function(){
210                 // if we have multiple controllers destroying us, bail after the first
211                 if(this._beingDestroyed){
212                         return;
213                 }
214                 // make sure we call onUnload
215                 this._onUnloadHandler();
216                 this._beingDestroyed = true;
217                 this.inherited("destroy",arguments);
218         },
219
220         resize: function(size){
221                 dojo.marginBox(this.domNode, size);
222
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||{});
229
230                 this._contentBox = dijit.layout.marginBox2contentBox(node, mb);
231
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);
235                 }
236         },
237
238         _prepareLoad: function(forceLoad){
239                 // sets up for a xhrLoad, load is deferred until widget onShow
240                 // cancels a inflight download
241                 this.cancel();
242                 this.isLoaded = false;
243                 this._loadCheck(forceLoad);
244         },
245
246         _isShown: function(){
247                 // summary: returns true if the content is currently shown
248                 if("open" in this){
249                         return this.open;               // for TitlePane, etc.
250                 }else{
251                         var node = this.domNode;
252                         return (node.style.display != 'none')  && (node.style.visibility != 'hidden');
253                 }
254         },
255
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'
259
260                 // sequence:
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
268
269                 var displayState = this._isShown();
270
271                 if(this.href && 
272                         (forceLoad ||
273                                 (this.preload && !this._xhrDfd) ||
274                                 (this.refreshOnShow && displayState && !this._xhrDfd) ||
275                                 (!this.isLoaded && displayState && !this._xhrDfd)
276                         )
277                 ){
278                         this._downloadExternalContent();
279                 }
280         },
281
282         _downloadExternalContent: function(){
283                 this._onUnloadHandler();
284
285                 // display loading message
286                 this._setContent(
287                         this.onDownloadStart.call(this)
288                 );
289
290                 var self = this;
291                 var getArgs = {
292                         preventCache: (this.preventCache || this.refreshOnShow),
293                         url: this.href,
294                         handleAs: "text"
295                 };
296                 if(dojo.isObject(this.ioArgs)){
297                         dojo.mixin(getArgs, this.ioArgs);
298                 }
299
300                 var hand = this._xhrDfd = (this.ioMethod || dojo.xhrGet)(getArgs);
301
302                 hand.addCallback(function(html){
303                         try{
304                                 self.onDownloadEnd.call(self);
305                                 self._isDownloaded = true;
306                                 self.setContent.call(self, html); // onload event is called from here
307                         }catch(err){
308                                 self._onError.call(self, 'Content', err); // onContentError
309                         }
310                         delete self._xhrDfd;
311                         return html;
312                 });
313
314                 hand.addErrback(function(err){
315                         if(!hand.cancelled){
316                                 // show error message in the pane
317                                 self._onError.call(self, 'Download', err); // onDownloadError
318                         }
319                         delete self._xhrDfd;
320                         return err;
321                 });
322         },
323
324         _onLoadHandler: function(){
325                 this.isLoaded = true;
326                 try{
327                         this.onLoad.call(this);
328                 }catch(e){
329                         console.error('Error '+this.widgetId+' running custom onLoad code');
330                 }
331         },
332
333         _onUnloadHandler: function(){
334                 this.isLoaded = false;
335                 this.cancel();
336                 try{
337                         this.onUnload.call(this);
338                 }catch(e){
339                         console.error('Error '+this.widgetId+' running custom onUnload code');
340                 }
341         },
342
343         _setContent: function(cont){
344                 this.destroyDescendants();
345
346                 try{
347                         var node = this.containerNode || this.domNode;
348                         while(node.firstChild){
349                                 dojo._destroyElement(node.firstChild);
350                         }
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]; }
358                                 }
359                                 node.innerHTML = cont;
360                         }else{
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));
367                                         });
368                                 }
369                         }
370                 }catch(e){
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);
374                         try{
375                                 node.innerHTML = errMess;
376                         }catch(e){
377                                 console.error('Fatal '+this.id+' could not change content due to '+e.message, e);
378                         }
379                 }
380         },
381
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);
386                 if(consoleText){
387                         console.error(consoleText, err);
388                 }else if(errText){// a empty string won't change current content
389                         this._setContent.call(this, errText);
390                 }
391         },
392
393         _createSubWidgets: function(){
394                 // summary: scan my contents and create subwidgets
395                 var rootNode = this.containerNode || this.domNode;
396                 try{
397                         dojo.parser.parse(rootNode, true);
398                 }catch(e){
399                         this._onError('Content', e, "Couldn't create widgets in "+this.id
400                                 +(this.href ? " from "+this.href : ""));
401                 }
402         },
403
404         // EVENT's, should be overide-able
405         onLoad: function(e){
406                 // summary:
407                 //              Event hook, is called after everything is loaded and widgetified
408         },
409
410         onUnload: function(e){
411                 // summary:
412                 //              Event hook, is called before old content is cleared
413         },
414
415         onDownloadStart: function(){
416                 // summary:
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;
422         },
423
424         onContentError: function(/*Error*/ error){
425                 // summary:
426                 //              called on DOM faults, require fault etc in content
427                 //              default is to display errormessage inside pane
428         },
429
430         onDownloadError: function(/*Error*/ error){
431                 // summary:
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;
437         },
438
439         onDownloadEnd: function(){
440                 // summary:
441                 //              called when download is finished
442         }
443 });
444
445 }