_ContentPaneResizeMixin.js 9.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255
  1. define("dijit/layout/_ContentPaneResizeMixin", [
  2. "dojo/_base/array", // array.filter array.forEach
  3. "dojo/_base/declare", // declare
  4. "dojo/dom-attr", // domAttr.has
  5. "dojo/dom-class", // domClass.contains domClass.toggle
  6. "dojo/dom-geometry",// domGeometry.contentBox domGeometry.marginBox
  7. "dojo/_base/lang", // lang.mixin
  8. "dojo/query", // query
  9. "dojo/_base/sniff", // has("ie")
  10. "dojo/_base/window", // win.global
  11. "../registry", // registry.byId
  12. "./utils", // marginBox2contextBox
  13. "../_Contained"
  14. ], function(array, declare, domAttr, domClass, domGeometry, lang, query, has, win,
  15. registry, layoutUtils, _Contained){
  16. /*=====
  17. var _Contained = dijit._Contained;
  18. =====*/
  19. // module:
  20. // dijit/layout/_ContentPaneResizeMixin
  21. // summary:
  22. // Resize() functionality of ContentPane. If there's a single layout widget
  23. // child then it will call resize() with the same dimensions as the ContentPane.
  24. // Otherwise just calls resize on each child.
  25. return declare("dijit.layout._ContentPaneResizeMixin", null, {
  26. // summary:
  27. // Resize() functionality of ContentPane. If there's a single layout widget
  28. // child then it will call resize() with the same dimensions as the ContentPane.
  29. // Otherwise just calls resize on each child.
  30. //
  31. // Also implements basic startup() functionality, where starting the parent
  32. // will start the children
  33. // doLayout: Boolean
  34. // - false - don't adjust size of children
  35. // - true - if there is a single visible child widget, set it's size to
  36. // however big the ContentPane is
  37. doLayout: true,
  38. // isLayoutContainer: [protected] Boolean
  39. // Indicates that this widget will call resize() on it's child widgets
  40. // when they become visible.
  41. isLayoutContainer: true,
  42. startup: function(){
  43. // summary:
  44. // See `dijit.layout._LayoutWidget.startup` for description.
  45. // Although ContentPane doesn't extend _LayoutWidget, it does implement
  46. // the same API.
  47. if(this._started){ return; }
  48. var parent = this.getParent();
  49. this._childOfLayoutWidget = parent && parent.isLayoutContainer;
  50. // I need to call resize() on my child/children (when I become visible), unless
  51. // I'm the child of a layout widget in which case my parent will call resize() on me and I'll do it then.
  52. this._needLayout = !this._childOfLayoutWidget;
  53. this.inherited(arguments);
  54. if(this._isShown()){
  55. this._onShow();
  56. }
  57. if(!this._childOfLayoutWidget){
  58. // If my parent isn't a layout container, since my style *may be* width=height=100%
  59. // or something similar (either set directly or via a CSS class),
  60. // monitor when my size changes so that I can re-layout.
  61. // For browsers where I can't directly monitor when my size changes,
  62. // monitor when the viewport changes size, which *may* indicate a size change for me.
  63. this.connect(has("ie") ? this.domNode : win.global, 'onresize', function(){
  64. // Using function(){} closure to ensure no arguments to resize.
  65. this._needLayout = !this._childOfLayoutWidget;
  66. this.resize();
  67. });
  68. }
  69. },
  70. _checkIfSingleChild: function(){
  71. // summary:
  72. // Test if we have exactly one visible widget as a child,
  73. // and if so assume that we are a container for that widget,
  74. // and should propagate startup() and resize() calls to it.
  75. // Skips over things like data stores since they aren't visible.
  76. var childNodes = query("> *", this.containerNode).filter(function(node){
  77. return node.tagName !== "SCRIPT"; // or a regexp for hidden elements like script|area|map|etc..
  78. }),
  79. childWidgetNodes = childNodes.filter(function(node){
  80. return domAttr.has(node, "data-dojo-type") || domAttr.has(node, "dojoType") || domAttr.has(node, "widgetId");
  81. }),
  82. candidateWidgets = array.filter(childWidgetNodes.map(registry.byNode), function(widget){
  83. return widget && widget.domNode && widget.resize;
  84. });
  85. if(
  86. // all child nodes are widgets
  87. childNodes.length == childWidgetNodes.length &&
  88. // all but one are invisible (like dojo.data)
  89. candidateWidgets.length == 1
  90. ){
  91. this._singleChild = candidateWidgets[0];
  92. }else{
  93. delete this._singleChild;
  94. }
  95. // So we can set overflow: hidden to avoid a safari bug w/scrollbars showing up (#9449)
  96. domClass.toggle(this.containerNode, this.baseClass + "SingleChild", !!this._singleChild);
  97. },
  98. resize: function(changeSize, resultSize){
  99. // summary:
  100. // See `dijit.layout._LayoutWidget.resize` for description.
  101. // Although ContentPane doesn't extend _LayoutWidget, it does implement
  102. // the same API.
  103. // For the TabContainer --> BorderContainer --> ContentPane case, _onShow() is
  104. // never called, so resize() is our trigger to do the initial href download (see [20099]).
  105. // However, don't load href for closed TitlePanes.
  106. if(!this._wasShown && this.open !== false){
  107. this._onShow();
  108. }
  109. this._resizeCalled = true;
  110. this._scheduleLayout(changeSize, resultSize);
  111. },
  112. _scheduleLayout: function(changeSize, resultSize){
  113. // summary:
  114. // Resize myself, and call resize() on each of my child layout widgets, either now
  115. // (if I'm currently visible) or when I become visible
  116. if(this._isShown()){
  117. this._layout(changeSize, resultSize);
  118. }else{
  119. this._needLayout = true;
  120. this._changeSize = changeSize;
  121. this._resultSize = resultSize;
  122. }
  123. },
  124. _layout: function(changeSize, resultSize){
  125. // summary:
  126. // Resize myself according to optional changeSize/resultSize parameters, like a layout widget.
  127. // Also, since I am a Container widget, each of my children expects me to
  128. // call resize() or layout() on them.
  129. //
  130. // Should be called on initialization and also whenever we get new content
  131. // (from an href, or from set('content', ...))... but deferred until
  132. // the ContentPane is visible
  133. // Set margin box size, unless it wasn't specified, in which case use current size.
  134. if(changeSize){
  135. domGeometry.setMarginBox(this.domNode, changeSize);
  136. }
  137. // Compute content box size of containerNode in case we [later] need to size our single child.
  138. var cn = this.containerNode;
  139. if(cn === this.domNode){
  140. // If changeSize or resultSize was passed to this method and this.containerNode ==
  141. // this.domNode then we can compute the content-box size without querying the node,
  142. // which is more reliable (similar to LayoutWidget.resize) (see for example #9449).
  143. var mb = resultSize || {};
  144. lang.mixin(mb, changeSize || {}); // changeSize overrides resultSize
  145. if(!("h" in mb) || !("w" in mb)){
  146. mb = lang.mixin(domGeometry.getMarginBox(cn), mb); // just use domGeometry.setMarginBox() to fill in missing values
  147. }
  148. this._contentBox = layoutUtils.marginBox2contentBox(cn, mb);
  149. }else{
  150. this._contentBox = domGeometry.getContentBox(cn);
  151. }
  152. this._layoutChildren();
  153. delete this._needLayout;
  154. },
  155. _layoutChildren: function(){
  156. // Call _checkIfSingleChild() again in case app has manually mucked w/the content
  157. // of the ContentPane (rather than changing it through the set("content", ...) API.
  158. if(this.doLayout){
  159. this._checkIfSingleChild();
  160. }
  161. if(this._singleChild && this._singleChild.resize){
  162. var cb = this._contentBox || domGeometry.getContentBox(this.containerNode);
  163. // note: if widget has padding this._contentBox will have l and t set,
  164. // but don't pass them to resize() or it will doubly-offset the child
  165. this._singleChild.resize({w: cb.w, h: cb.h});
  166. }else{
  167. // All my child widgets are independently sized (rather than matching my size),
  168. // but I still need to call resize() on each child to make it layout.
  169. array.forEach(this.getChildren(), function(widget){
  170. if(widget.resize){
  171. widget.resize();
  172. }
  173. });
  174. }
  175. },
  176. _isShown: function(){
  177. // summary:
  178. // Returns true if the content is currently shown.
  179. // description:
  180. // If I am a child of a layout widget then it actually returns true if I've ever been visible,
  181. // not whether I'm currently visible, since that's much faster than tracing up the DOM/widget
  182. // tree every call, and at least solves the performance problem on page load by deferring loading
  183. // hidden ContentPanes until they are first shown
  184. if(this._childOfLayoutWidget){
  185. // If we are TitlePane, etc - we return that only *IF* we've been resized
  186. if(this._resizeCalled && "open" in this){
  187. return this.open;
  188. }
  189. return this._resizeCalled;
  190. }else if("open" in this){
  191. return this.open; // for TitlePane, etc.
  192. }else{
  193. var node = this.domNode, parent = this.domNode.parentNode;
  194. return (node.style.display != 'none') && (node.style.visibility != 'hidden') && !domClass.contains(node, "dijitHidden") &&
  195. parent && parent.style && (parent.style.display != 'none');
  196. }
  197. },
  198. _onShow: function(){
  199. // summary:
  200. // Called when the ContentPane is made visible
  201. // description:
  202. // For a plain ContentPane, this is called on initialization, from startup().
  203. // If the ContentPane is a hidden pane of a TabContainer etc., then it's
  204. // called whenever the pane is made visible.
  205. //
  206. // Does layout/resize of child widget(s)
  207. // Need to keep track of whether ContentPane has been shown (which is different than
  208. // whether or not it's currently visible).
  209. this._wasShown = true;
  210. if(this._needLayout){
  211. // If a layout has been scheduled for when we become visible, do it now
  212. this._layout(this._changeSize, this._resultSize);
  213. }
  214. this.inherited(arguments);
  215. }
  216. });
  217. });