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