ContentPane.js 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437
  1. if(!dojo._hasResource["dijit.layout.ContentPane"]){ // _hasResource checks added
  2. // by build. Do not use
  3. // _hasResource directly in
  4. // your code.
  5. dojo._hasResource["dijit.layout.ContentPane"] = true;
  6. dojo.provide("dijit.layout.ContentPane");
  7. dojo.require("dijit._Widget");
  8. dojo.require("dijit.layout._LayoutWidget");
  9. dojo.require("dojo.parser");
  10. dojo.require("dojo.string");
  11. dojo.requireLocalization("dijit", "loading", null, "ko,zh,ja,zh-tw,ru,it,ROOT,hu,fr,pt,pl,es,de,cs");
  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
  18. // interface
  19. // description:
  20. // A widget that can be used as a standalone widget
  21. // or as a baseclass for other widgets
  22. // Handles replacement of document fragment using either external uri or
  23. // javascript
  24. // generated markup or DOM content, instantiating widgets within that
  25. // content.
  26. // Don't confuse it with an iframe, it only needs/wants document fragments.
  27. // It's useful as a child of LayoutContainer, SplitContainer, or
  28. // TabContainer.
  29. // But note that those classes can contain any widget as a child.
  30. // example:
  31. // Some quick samples:
  32. // To change the innerHTML use .setContent('<b>new content</b>')
  33. //
  34. // Or you can send it a NodeList, .setContent(dojo.query('div
  35. // [class=selected]', userSelection))
  36. // please note that the nodes in NodeList will copied, not moved
  37. //
  38. // To do a ajax update use .setHref('url')
  39. //
  40. // href: String
  41. // The href of the content that displays now.
  42. // Set this at construction if you want to load data externally when the
  43. // pane is shown. (Set preload=true to load it immediately.)
  44. // Changing href after creation doesn't have any effect; see setHref();
  45. href: "",
  46. // extractContent: Boolean
  47. // Extract visible content from inside of <body> .... </body>
  48. extractContent: false,
  49. // parseOnLoad: Boolean
  50. // parse content and create the widgets, if any
  51. parseOnLoad: true,
  52. // preventCache: Boolean
  53. // Cache content retreived externally
  54. preventCache: false,
  55. // preload: Boolean
  56. // Force load of data even if pane is hidden.
  57. preload: false,
  58. // refreshOnShow: Boolean
  59. // Refresh (re-download) content when pane goes from hidden to shown
  60. refreshOnShow: false,
  61. // loadingMessage: String
  62. // Message that shows while downloading
  63. loadingMessage: "<span class='dijitContentPaneLoading'>${loadingState}</span>",
  64. // errorMessage: String
  65. // Message that shows if an error occurs
  66. errorMessage: "<span class='dijitContentPaneError'>${errorState}</span>",
  67. // isLoaded: Boolean
  68. // Tells loading status see onLoad|onUnload for event hooks
  69. isLoaded: false,
  70. // class: String
  71. // Class name to apply to ContentPane dom nodes
  72. "class": "dijitContentPane",
  73. postCreate: function(){
  74. // remove the title attribute so it doesn't show up when i hover
  75. // over a node
  76. this.domNode.title = "";
  77. if(this.preload){
  78. this._loadCheck();
  79. }
  80. var messages = dojo.i18n.getLocalization("dijit", "loading", this.lang);
  81. this.loadingMessage = dojo.string.substitute(this.loadingMessage, messages);
  82. this.errorMessage = dojo.string.substitute(this.errorMessage, messages);
  83. // for programatically created ContentPane (with <span> tag), need to
  84. // muck w/CSS
  85. // or it's as though overflow:visible is set
  86. dojo.addClass(this.domNode, this["class"]);
  87. },
  88. startup: function(){
  89. if(this._started){ return; }
  90. this._checkIfSingleChild();
  91. if(this._singleChild){
  92. this._singleChild.startup();
  93. }
  94. this._loadCheck();
  95. this._started = true;
  96. },
  97. _checkIfSingleChild: function(){
  98. // summary:
  99. // Test if we have exactly one widget as a child, and if so assume that
  100. // we are a container for that widget,
  101. // and should propogate startup() and resize() calls to it.
  102. var childNodes = dojo.query(">", this.containerNode || this.domNode),
  103. childWidgets = childNodes.filter("[widgetId]");
  104. if(childNodes.length == 1 && childWidgets.length == 1){
  105. this.isContainer = true;
  106. this._singleChild = dijit.byNode(childWidgets[0]);
  107. }else{
  108. delete this.isContainer;
  109. delete this._singleChild;
  110. }
  111. },
  112. refresh: function(){
  113. // summary:
  114. // Force a refresh (re-download) of content, be sure to turn off cache
  115. // we return result of _prepareLoad here to avoid code dup. in
  116. // dojox.layout.ContentPane
  117. return this._prepareLoad(true);
  118. },
  119. setHref: function(/* String|Uri */ href){
  120. // summary:
  121. // Reset the (external defined) content of this pane and replace with
  122. // new url
  123. // Note: It delays the download until widget is shown if preload is
  124. // false
  125. // href:
  126. // url to the page you want to get, must be within the same domain as
  127. // your mainpage
  128. this.href = href;
  129. // we return result of _prepareLoad here to avoid code dup. in
  130. // dojox.layout.ContentPane
  131. return this._prepareLoad();
  132. },
  133. setContent: function(/* String|DomNode|Nodelist */data){
  134. // summary:
  135. // Replaces old content with data content, include style classes from
  136. // old content
  137. // data:
  138. // the new Content may be String, DomNode or NodeList
  139. //
  140. // if data is a NodeList (or an array of nodes) nodes are copied
  141. // so you can import nodes from another document implicitly
  142. // clear href so we cant run refresh and clear content
  143. // refresh should only work if we downloaded the content
  144. if(!this._isDownloaded){
  145. this.href = "";
  146. this._onUnloadHandler();
  147. }
  148. this._setContent(data || "");
  149. this._isDownloaded = false; // must be set after _setContent(..),
  150. // pathadjust in dojox.layout.ContentPane
  151. if(this.parseOnLoad){
  152. this._createSubWidgets();
  153. }
  154. this._checkIfSingleChild();
  155. if(this._singleChild && this._singleChild.resize){
  156. this._singleChild.resize(this._contentBox);
  157. }
  158. this._onLoadHandler();
  159. },
  160. cancel: function(){
  161. // summary:
  162. // Cancels a inflight download of content
  163. if(this._xhrDfd && (this._xhrDfd.fired == -1)){
  164. this._xhrDfd.cancel();
  165. }
  166. delete this._xhrDfd; // garbage collect
  167. },
  168. destroy: function(){
  169. // if we have multiple controllers destroying us, bail after the first
  170. if(this._beingDestroyed){
  171. return;
  172. }
  173. // make sure we call onUnload
  174. this._onUnloadHandler();
  175. this._beingDestroyed = true;
  176. this.inherited("destroy",arguments);
  177. },
  178. resize: function(size){
  179. dojo.marginBox(this.domNode, size);
  180. // Compute content box size in case we [later] need to size child
  181. // If either height or width wasn't specified by the user, then query
  182. // node for it.
  183. // But note that setting the margin box and then immediately querying
  184. // dimensions may return
  185. // inaccurate results, so try not to depend on it.
  186. var node = this.containerNode || this.domNode,
  187. mb = dojo.mixin(dojo.marginBox(node), size||{});
  188. this._contentBox = dijit.layout.marginBox2contentBox(node, mb);
  189. // If we have a single widget child then size it to fit snugly within my
  190. // borders
  191. if(this._singleChild && this._singleChild.resize){
  192. this._singleChild.resize(this._contentBox);
  193. }
  194. },
  195. _prepareLoad: function(forceLoad){
  196. // sets up for a xhrLoad, load is deferred until widget onShow
  197. // cancels a inflight download
  198. this.cancel();
  199. this.isLoaded = false;
  200. this._loadCheck(forceLoad);
  201. },
  202. _loadCheck: function(forceLoad){
  203. // call this when you change onShow (onSelected) status when selected in
  204. // parent container
  205. // it's used as a trigger for href download when this.domNode.display !=
  206. // 'none'
  207. // sequence:
  208. // if no href -> bail
  209. // forceLoad -> always load
  210. // this.preload -> load when download not in progress, domNode display
  211. // doesn't matter
  212. // this.refreshOnShow -> load when download in progress bails, domNode
  213. // display !='none' AND
  214. // this.open !== false (undefined is ok), isLoaded doesn't matter
  215. // else -> load when download not in progress, if this.open !== false
  216. // (undefined is ok) AND
  217. // domNode display != 'none', isLoaded must be false
  218. var displayState = ((this.open !== false) && (this.domNode.style.display != 'none'));
  219. if(this.href &&
  220. (forceLoad ||
  221. (this.preload && !this._xhrDfd) ||
  222. (this.refreshOnShow && displayState && !this._xhrDfd) ||
  223. (!this.isLoaded && displayState && !this._xhrDfd)
  224. )
  225. ){
  226. this._downloadExternalContent();
  227. }
  228. },
  229. _downloadExternalContent: function(){
  230. this._onUnloadHandler();
  231. // display loading message
  232. this._setContent(
  233. this.onDownloadStart.call(this)
  234. );
  235. var self = this;
  236. var getArgs = {
  237. preventCache: (this.preventCache || this.refreshOnShow),
  238. url: this.href,
  239. handleAs: "text"
  240. };
  241. if(dojo.isObject(this.ioArgs)){
  242. dojo.mixin(getArgs, this.ioArgs);
  243. }
  244. var hand = this._xhrDfd = (this.ioMethod || dojo.xhrGet)(getArgs);
  245. hand.addCallback(function(html){
  246. try{
  247. self.onDownloadEnd.call(self);
  248. self._isDownloaded = true;
  249. self.setContent.call(self, html); // onload event is called
  250. // from here
  251. }catch(err){
  252. self._onError.call(self, 'Content', err); // onContentError
  253. }
  254. delete self._xhrDfd;
  255. return html;
  256. });
  257. hand.addErrback(function(err){
  258. if(!hand.cancelled){
  259. // show error message in the pane
  260. self._onError.call(self, 'Download', err); // onDownloadError
  261. }
  262. delete self._xhrDfd;
  263. return err;
  264. });
  265. },
  266. _onLoadHandler: function(){
  267. this.isLoaded = true;
  268. try{
  269. this.onLoad.call(this);
  270. }catch(e){
  271. console.error('Error '+this.widgetId+' running custom onLoad code');
  272. }
  273. },
  274. _onUnloadHandler: function(){
  275. this.isLoaded = false;
  276. this.cancel();
  277. try{
  278. this.onUnload.call(this);
  279. }catch(e){
  280. console.error('Error '+this.widgetId+' running custom onUnload code');
  281. }
  282. },
  283. _setContent: function(cont){
  284. this.destroyDescendants();
  285. try{
  286. var node = this.containerNode || this.domNode;
  287. while(node.firstChild){
  288. dojo._destroyElement(node.firstChild);
  289. }
  290. if(typeof cont == "string"){
  291. // dijit.ContentPane does only minimal fixes,
  292. // No pathAdjustments, script retrieval, style clean etc
  293. // some of these should be available in the
  294. // dojox.layout.ContentPane
  295. if(this.extractContent){
  296. match = cont.match(/<body[^>]*>\s*([\s\S]+)\s*<\/body>/im);
  297. if(match){ cont = match[1]; }
  298. }
  299. node.innerHTML = cont;
  300. }else{
  301. // domNode or NodeList
  302. if(cont.nodeType){ // domNode (htmlNode 1 or textNode 3)
  303. node.appendChild(cont);
  304. }else{// nodelist or array such as dojo.Nodelist
  305. dojo.forEach(cont, function(n){
  306. node.appendChild(n.cloneNode(true));
  307. });
  308. }
  309. }
  310. }catch(e){
  311. // check if a domfault occurs when we are appending
  312. // this.errorMessage
  313. // like for instance if domNode is a UL and we try append a DIV
  314. var errMess = this.onContentError(e);
  315. try{
  316. node.innerHTML = errMess;
  317. }catch(e){
  318. console.error('Fatal '+this.id+' could not change content due to '+e.message, e);
  319. }
  320. }
  321. },
  322. _onError: function(type, err, consoleText){
  323. // shows user the string that is returned by on[type]Error
  324. // overide on[type]Error and return your own string to customize
  325. var errText = this['on' + type + 'Error'].call(this, err);
  326. if(consoleText){
  327. console.error(consoleText, err);
  328. }else if(errText){// a empty string won't change current content
  329. this._setContent.call(this, errText);
  330. }
  331. },
  332. _createSubWidgets: function(){
  333. // summary: scan my contents and create subwidgets
  334. var rootNode = this.containerNode || this.domNode;
  335. try{
  336. dojo.parser.parse(rootNode, true);
  337. }catch(e){
  338. this._onError('Content', e, "Couldn't create widgets in "+this.id
  339. +(this.href ? " from "+this.href : ""));
  340. }
  341. },
  342. // EVENT's, should be overide-able
  343. onLoad: function(e){
  344. // summary:
  345. // Event hook, is called after everything is loaded and widgetified
  346. },
  347. onUnload: function(e){
  348. // summary:
  349. // Event hook, is called before old content is cleared
  350. },
  351. onDownloadStart: function(){
  352. // summary:
  353. // called before download starts
  354. // the string returned by this function will be the html
  355. // that tells the user we are loading something
  356. // override with your own function if you want to change text
  357. return this.loadingMessage;
  358. },
  359. onContentError: function(/* Error */ error){
  360. // summary:
  361. // called on DOM faults, require fault etc in content
  362. // default is to display errormessage inside pane
  363. },
  364. onDownloadError: function(/* Error */ error){
  365. // summary:
  366. // Called when download error occurs, default is to display
  367. // errormessage inside pane. Overide function to change that.
  368. // The string returned by this function will be the html
  369. // that tells the user a error happend
  370. return this.errorMessage;
  371. },
  372. onDownloadEnd: function(){
  373. // summary:
  374. // called when download is finished
  375. }
  376. });
  377. }