]>
Commit | Line | Data |
---|---|---|
f0cfe83e AD |
1 | define("dijit/layout/BorderContainer", [ |
2 | "dojo/_base/array", // array.filter array.forEach array.map | |
3 | "dojo/cookie", // cookie | |
4 | "dojo/_base/declare", // declare | |
5 | "dojo/dom-class", // domClass.add domClass.remove domClass.toggle | |
6 | "dojo/dom-construct", // domConstruct.destroy domConstruct.place | |
7 | "dojo/dom-geometry", // domGeometry.marginBox | |
8 | "dojo/dom-style", // domStyle.style | |
9 | "dojo/_base/event", // event.stop | |
10 | "dojo/keys", | |
11 | "dojo/_base/lang", // lang.getObject lang.hitch | |
12 | "dojo/on", | |
13 | "dojo/touch", | |
14 | "../_WidgetBase", | |
15 | "../_Widget", | |
16 | "../_TemplatedMixin", | |
17 | "./_LayoutWidget", | |
18 | "./utils" // layoutUtils.layoutChildren | |
19 | ], function(array, cookie, declare, domClass, domConstruct, domGeometry, domStyle, event, keys, lang, on, touch, | |
20 | _WidgetBase, _Widget, _TemplatedMixin, _LayoutWidget, layoutUtils){ | |
21 | ||
22 | // module: | |
23 | // dijit/layout/BorderContainer | |
24 | ||
25 | var _Splitter = declare("dijit.layout._Splitter", [_Widget, _TemplatedMixin ], | |
26 | { | |
27 | // summary: | |
28 | // A draggable spacer between two items in a `dijit/layout/BorderContainer`. | |
29 | // description: | |
30 | // This is instantiated by `dijit/layout/BorderContainer`. Users should not | |
31 | // create it directly. | |
32 | // tags: | |
33 | // private | |
34 | ||
35 | /*===== | |
36 | // container: [const] dijit/layout/BorderContainer | |
37 | // Pointer to the parent BorderContainer | |
38 | container: null, | |
39 | ||
40 | // child: [const] dijit/layout/_LayoutWidget | |
41 | // Pointer to the pane associated with this splitter | |
42 | child: null, | |
43 | ||
44 | // region: [const] String | |
45 | // Region of pane associated with this splitter. | |
46 | // "top", "bottom", "left", "right". | |
47 | region: null, | |
48 | =====*/ | |
49 | ||
50 | // live: [const] Boolean | |
51 | // If true, the child's size changes and the child widget is redrawn as you drag the splitter; | |
52 | // otherwise, the size doesn't change until you drop the splitter (by mouse-up) | |
53 | live: true, | |
54 | ||
55 | templateString: '<div class="dijitSplitter" data-dojo-attach-event="onkeypress:_onKeyPress,press:_startDrag,onmouseenter:_onMouse,onmouseleave:_onMouse" tabIndex="0" role="separator"><div class="dijitSplitterThumb"></div></div>', | |
56 | ||
57 | constructor: function(){ | |
58 | this._handlers = []; | |
59 | }, | |
60 | ||
61 | postMixInProperties: function(){ | |
62 | this.inherited(arguments); | |
63 | ||
64 | this.horizontal = /top|bottom/.test(this.region); | |
65 | this._factor = /top|left/.test(this.region) ? 1 : -1; | |
66 | this._cookieName = this.container.id + "_" + this.region; | |
67 | }, | |
68 | ||
69 | buildRendering: function(){ | |
70 | this.inherited(arguments); | |
71 | ||
72 | domClass.add(this.domNode, "dijitSplitter" + (this.horizontal ? "H" : "V")); | |
73 | ||
74 | if(this.container.persist){ | |
75 | // restore old size | |
76 | var persistSize = cookie(this._cookieName); | |
77 | if(persistSize){ | |
78 | this.child.domNode.style[this.horizontal ? "height" : "width"] = persistSize; | |
79 | } | |
80 | } | |
81 | }, | |
82 | ||
83 | _computeMaxSize: function(){ | |
84 | // summary: | |
85 | // Return the maximum size that my corresponding pane can be set to | |
86 | ||
87 | var dim = this.horizontal ? 'h' : 'w', | |
88 | childSize = domGeometry.getMarginBox(this.child.domNode)[dim], | |
89 | center = array.filter(this.container.getChildren(), function(child){ return child.region == "center";})[0], | |
90 | spaceAvailable = domGeometry.getMarginBox(center.domNode)[dim]; // can expand until center is crushed to 0 | |
91 | ||
92 | return Math.min(this.child.maxSize, childSize + spaceAvailable); | |
93 | }, | |
94 | ||
95 | _startDrag: function(e){ | |
96 | if(!this.cover){ | |
97 | this.cover = domConstruct.place("<div class=dijitSplitterCover></div>", this.child.domNode, "after"); | |
98 | } | |
99 | domClass.add(this.cover, "dijitSplitterCoverActive"); | |
100 | ||
101 | // Safeguard in case the stop event was missed. Shouldn't be necessary if we always get the mouse up. | |
102 | if(this.fake){ domConstruct.destroy(this.fake); } | |
103 | if(!(this._resize = this.live)){ //TODO: disable live for IE6? | |
104 | // create fake splitter to display at old position while we drag | |
105 | (this.fake = this.domNode.cloneNode(true)).removeAttribute("id"); | |
106 | domClass.add(this.domNode, "dijitSplitterShadow"); | |
107 | domConstruct.place(this.fake, this.domNode, "after"); | |
108 | } | |
109 | domClass.add(this.domNode, "dijitSplitterActive dijitSplitter" + (this.horizontal ? "H" : "V") + "Active"); | |
110 | if(this.fake){ | |
111 | domClass.remove(this.fake, "dijitSplitterHover dijitSplitter" + (this.horizontal ? "H" : "V") + "Hover"); | |
112 | } | |
113 | ||
114 | //Performance: load data info local vars for onmousevent function closure | |
115 | var factor = this._factor, | |
116 | isHorizontal = this.horizontal, | |
117 | axis = isHorizontal ? "pageY" : "pageX", | |
118 | pageStart = e[axis], | |
119 | splitterStyle = this.domNode.style, | |
120 | dim = isHorizontal ? 'h' : 'w', | |
121 | childStart = domGeometry.getMarginBox(this.child.domNode)[dim], | |
122 | max = this._computeMaxSize(), | |
123 | min = this.child.minSize || 20, | |
124 | region = this.region, | |
125 | splitterAttr = region == "top" || region == "bottom" ? "top" : "left", // style attribute of splitter to adjust | |
126 | splitterStart = parseInt(splitterStyle[splitterAttr], 10), | |
127 | resize = this._resize, | |
128 | layoutFunc = lang.hitch(this.container, "_layoutChildren", this.child.id), | |
129 | de = this.ownerDocument; | |
130 | ||
131 | this._handlers = this._handlers.concat([ | |
132 | on(de, touch.move, this._drag = function(e, forceResize){ | |
133 | var delta = e[axis] - pageStart, | |
134 | childSize = factor * delta + childStart, | |
135 | boundChildSize = Math.max(Math.min(childSize, max), min); | |
136 | ||
137 | if(resize || forceResize){ | |
138 | layoutFunc(boundChildSize); | |
139 | } | |
140 | // TODO: setting style directly (usually) sets content box size, need to set margin box size | |
141 | splitterStyle[splitterAttr] = delta + splitterStart + factor*(boundChildSize - childSize) + "px"; | |
142 | }), | |
143 | on(de, "dragstart", event.stop), | |
144 | on(this.ownerDocumentBody, "selectstart", event.stop), | |
145 | on(de, touch.release, lang.hitch(this, "_stopDrag")) | |
146 | ]); | |
147 | event.stop(e); | |
148 | }, | |
149 | ||
150 | _onMouse: function(e){ | |
151 | // summary: | |
152 | // Handler for onmouseenter / onmouseleave events | |
153 | var o = (e.type == "mouseover" || e.type == "mouseenter"); | |
154 | domClass.toggle(this.domNode, "dijitSplitterHover", o); | |
155 | domClass.toggle(this.domNode, "dijitSplitter" + (this.horizontal ? "H" : "V") + "Hover", o); | |
156 | }, | |
157 | ||
158 | _stopDrag: function(e){ | |
159 | try{ | |
160 | if(this.cover){ | |
161 | domClass.remove(this.cover, "dijitSplitterCoverActive"); | |
162 | } | |
163 | if(this.fake){ domConstruct.destroy(this.fake); } | |
164 | domClass.remove(this.domNode, "dijitSplitterActive dijitSplitter" | |
165 | + (this.horizontal ? "H" : "V") + "Active dijitSplitterShadow"); | |
166 | this._drag(e); //TODO: redundant with onmousemove? | |
167 | this._drag(e, true); | |
168 | }finally{ | |
169 | this._cleanupHandlers(); | |
170 | delete this._drag; | |
171 | } | |
172 | ||
173 | if(this.container.persist){ | |
174 | cookie(this._cookieName, this.child.domNode.style[this.horizontal ? "height" : "width"], {expires:365}); | |
175 | } | |
176 | }, | |
177 | ||
178 | _cleanupHandlers: function(){ | |
179 | var h; | |
180 | while(h = this._handlers.pop()){ h.remove(); } | |
181 | }, | |
182 | ||
183 | _onKeyPress: function(/*Event*/ e){ | |
184 | // should we apply typematic to this? | |
185 | this._resize = true; | |
186 | var horizontal = this.horizontal; | |
187 | var tick = 1; | |
188 | switch(e.charOrCode){ | |
189 | case horizontal ? keys.UP_ARROW : keys.LEFT_ARROW: | |
190 | tick *= -1; | |
191 | // break; | |
192 | case horizontal ? keys.DOWN_ARROW : keys.RIGHT_ARROW: | |
193 | break; | |
194 | default: | |
195 | // this.inherited(arguments); | |
196 | return; | |
197 | } | |
198 | var childSize = domGeometry.getMarginSize(this.child.domNode)[ horizontal ? 'h' : 'w' ] + this._factor * tick; | |
199 | this.container._layoutChildren(this.child.id, Math.max(Math.min(childSize, this._computeMaxSize()), this.child.minSize)); | |
200 | event.stop(e); | |
201 | }, | |
202 | ||
203 | destroy: function(){ | |
204 | this._cleanupHandlers(); | |
205 | delete this.child; | |
206 | delete this.container; | |
207 | delete this.cover; | |
208 | delete this.fake; | |
209 | this.inherited(arguments); | |
210 | } | |
211 | }); | |
212 | ||
213 | var _Gutter = declare("dijit.layout._Gutter", [_Widget, _TemplatedMixin], | |
214 | { | |
215 | // summary: | |
216 | // Just a spacer div to separate side pane from center pane. | |
217 | // Basically a trick to lookup the gutter/splitter width from the theme. | |
218 | // description: | |
219 | // Instantiated by `dijit/layout/BorderContainer`. Users should not | |
220 | // create directly. | |
221 | // tags: | |
222 | // private | |
223 | ||
224 | templateString: '<div class="dijitGutter" role="presentation"></div>', | |
225 | ||
226 | postMixInProperties: function(){ | |
227 | this.inherited(arguments); | |
228 | this.horizontal = /top|bottom/.test(this.region); | |
229 | }, | |
230 | ||
231 | buildRendering: function(){ | |
232 | this.inherited(arguments); | |
233 | domClass.add(this.domNode, "dijitGutter" + (this.horizontal ? "H" : "V")); | |
234 | } | |
235 | }); | |
236 | ||
237 | var BorderContainer = declare("dijit.layout.BorderContainer", _LayoutWidget, { | |
238 | // summary: | |
239 | // Provides layout in up to 5 regions, a mandatory center with optional borders along its 4 sides. | |
240 | // description: | |
241 | // A BorderContainer is a box with a specified size, such as style="width: 500px; height: 500px;", | |
242 | // that contains a child widget marked region="center" and optionally children widgets marked | |
243 | // region equal to "top", "bottom", "leading", "trailing", "left" or "right". | |
244 | // Children along the edges will be laid out according to width or height dimensions and may | |
245 | // include optional splitters (splitter="true") to make them resizable by the user. The remaining | |
246 | // space is designated for the center region. | |
247 | // | |
248 | // The outer size must be specified on the BorderContainer node. Width must be specified for the sides | |
249 | // and height for the top and bottom, respectively. No dimensions should be specified on the center; | |
250 | // it will fill the remaining space. Regions named "leading" and "trailing" may be used just like | |
251 | // "left" and "right" except that they will be reversed in right-to-left environments. | |
252 | // | |
253 | // For complex layouts, multiple children can be specified for a single region. In this case, the | |
254 | // layoutPriority flag on the children determines which child is closer to the edge (low layoutPriority) | |
255 | // and which child is closer to the center (high layoutPriority). layoutPriority can also be used | |
256 | // instead of the design attribute to control layout precedence of horizontal vs. vertical panes. | |
257 | // | |
258 | // See `BorderContainer.ChildWidgetProperties` for details on the properties that can be set on | |
259 | // children of a `BorderContainer`. | |
260 | // example: | |
261 | // | <div data-dojo-type="dijit/layout/BorderContainer" data-dojo-props="design: 'sidebar', gutters: false" | |
262 | // | style="width: 400px; height: 300px;"> | |
263 | // | <div data-dojo-type="dijit/layout/ContentPane" data-dojo-props="region: 'top'">header text</div> | |
264 | // | <div data-dojo-type="dijit/layout/ContentPane" data-dojo-props="region: 'right', splitter: true" style="width: 200px;">table of contents</div> | |
265 | // | <div data-dojo-type="dijit/layout/ContentPane" data-dojo-props="region: 'center'">client area</div> | |
266 | // | </div> | |
267 | ||
268 | // design: String | |
269 | // Which design is used for the layout: | |
270 | // | |
271 | // - "headline" (default) where the top and bottom extend the full width of the container | |
272 | // - "sidebar" where the left and right sides extend from top to bottom. | |
273 | design: "headline", | |
274 | ||
275 | // gutters: [const] Boolean | |
276 | // Give each pane a border and margin. | |
277 | // Margin determined by domNode.paddingLeft. | |
278 | // When false, only resizable panes have a gutter (i.e. draggable splitter) for resizing. | |
279 | gutters: true, | |
280 | ||
281 | // liveSplitters: [const] Boolean | |
282 | // Specifies whether splitters resize as you drag (true) or only upon mouseup (false) | |
283 | liveSplitters: true, | |
284 | ||
285 | // persist: Boolean | |
286 | // Save splitter positions in a cookie. | |
287 | persist: false, | |
288 | ||
289 | baseClass: "dijitBorderContainer", | |
290 | ||
291 | // _splitterClass: Function||String | |
292 | // Optional hook to override the default Splitter widget used by BorderContainer | |
293 | _splitterClass: _Splitter, | |
294 | ||
295 | postMixInProperties: function(){ | |
296 | // change class name to indicate that BorderContainer is being used purely for | |
297 | // layout (like LayoutContainer) rather than for pretty formatting. | |
298 | if(!this.gutters){ | |
299 | this.baseClass += "NoGutter"; | |
300 | } | |
301 | this.inherited(arguments); | |
302 | }, | |
303 | ||
304 | startup: function(){ | |
305 | if(this._started){ return; } | |
306 | array.forEach(this.getChildren(), this._setupChild, this); | |
307 | this.inherited(arguments); | |
308 | }, | |
309 | ||
310 | _setupChild: function(/*dijit/_WidgetBase*/ child){ | |
311 | // Override _LayoutWidget._setupChild(). | |
312 | ||
313 | var region = child.region; | |
314 | if(region){ | |
315 | this.inherited(arguments); | |
316 | ||
317 | domClass.add(child.domNode, this.baseClass+"Pane"); | |
318 | ||
319 | var ltr = this.isLeftToRight(); | |
320 | if(region == "leading"){ region = ltr ? "left" : "right"; } | |
321 | if(region == "trailing"){ region = ltr ? "right" : "left"; } | |
322 | ||
323 | // Create draggable splitter for resizing pane, | |
324 | // or alternately if splitter=false but BorderContainer.gutters=true then | |
325 | // insert dummy div just for spacing | |
326 | if(region != "center" && (child.splitter || this.gutters) && !child._splitterWidget){ | |
327 | var _Splitter = child.splitter ? this._splitterClass : _Gutter; | |
328 | if(lang.isString(_Splitter)){ | |
329 | _Splitter = lang.getObject(_Splitter); // for back-compat, remove in 2.0 | |
330 | } | |
331 | var splitter = new _Splitter({ | |
332 | id: child.id + "_splitter", | |
333 | container: this, | |
334 | child: child, | |
335 | region: region, | |
336 | live: this.liveSplitters | |
337 | }); | |
338 | splitter.isSplitter = true; | |
339 | child._splitterWidget = splitter; | |
340 | ||
341 | domConstruct.place(splitter.domNode, child.domNode, "after"); | |
342 | ||
343 | // Splitters aren't added as Contained children, so we need to call startup explicitly | |
344 | splitter.startup(); | |
345 | } | |
346 | child.region = region; // TODO: technically wrong since it overwrites "trailing" with "left" etc. | |
347 | } | |
348 | }, | |
349 | ||
350 | layout: function(){ | |
351 | // Implement _LayoutWidget.layout() virtual method. | |
352 | this._layoutChildren(); | |
353 | }, | |
354 | ||
355 | addChild: function(/*dijit/_WidgetBase*/ child, /*Integer?*/ insertIndex){ | |
356 | // Override _LayoutWidget.addChild(). | |
357 | this.inherited(arguments); | |
358 | if(this._started){ | |
359 | this.layout(); //OPT | |
360 | } | |
361 | }, | |
362 | ||
363 | removeChild: function(/*dijit/_WidgetBase*/ child){ | |
364 | // Override _LayoutWidget.removeChild(). | |
365 | ||
366 | var region = child.region; | |
367 | var splitter = child._splitterWidget; | |
368 | if(splitter){ | |
369 | splitter.destroy(); | |
370 | delete child._splitterWidget; | |
371 | } | |
372 | this.inherited(arguments); | |
373 | ||
374 | if(this._started){ | |
375 | this._layoutChildren(); | |
376 | } | |
377 | // Clean up whatever style changes we made to the child pane. | |
378 | // Unclear how height and width should be handled. | |
379 | domClass.remove(child.domNode, this.baseClass+"Pane"); | |
380 | domStyle.set(child.domNode, { | |
381 | top: "auto", | |
382 | bottom: "auto", | |
383 | left: "auto", | |
384 | right: "auto", | |
385 | position: "static" | |
386 | }); | |
387 | domStyle.set(child.domNode, region == "top" || region == "bottom" ? "width" : "height", "auto"); | |
388 | }, | |
389 | ||
390 | getChildren: function(){ | |
391 | // Override _LayoutWidget.getChildren() to only return real children, not the splitters. | |
392 | return array.filter(this.inherited(arguments), function(widget){ | |
393 | return !widget.isSplitter; | |
394 | }); | |
395 | }, | |
396 | ||
397 | // TODO: remove in 2.0 | |
398 | getSplitter: function(/*String*/region){ | |
399 | // summary: | |
400 | // Returns the widget responsible for rendering the splitter associated with region | |
401 | // tags: | |
402 | // deprecated | |
403 | return array.filter(this.getChildren(), function(child){ | |
404 | return child.region == region; | |
405 | })[0]._splitterWidget; | |
406 | }, | |
407 | ||
408 | resize: function(newSize, currentSize){ | |
409 | // Overrides _LayoutWidget.resize(). | |
410 | ||
411 | // resetting potential padding to 0px to provide support for 100% width/height + padding | |
412 | // TODO: this hack doesn't respect the box model and is a temporary fix | |
413 | if(!this.cs || !this.pe){ | |
414 | var node = this.domNode; | |
415 | this.cs = domStyle.getComputedStyle(node); | |
416 | this.pe = domGeometry.getPadExtents(node, this.cs); | |
417 | this.pe.r = domStyle.toPixelValue(node, this.cs.paddingRight); | |
418 | this.pe.b = domStyle.toPixelValue(node, this.cs.paddingBottom); | |
419 | ||
420 | domStyle.set(node, "padding", "0px"); | |
421 | } | |
422 | ||
423 | this.inherited(arguments); | |
424 | }, | |
425 | ||
426 | _layoutChildren: function(/*String?*/ changedChildId, /*Number?*/ changedChildSize){ | |
427 | // summary: | |
428 | // This is the main routine for setting size/position of each child. | |
429 | // description: | |
430 | // With no arguments, measures the height of top/bottom panes, the width | |
431 | // of left/right panes, and then sizes all panes accordingly. | |
432 | // | |
433 | // With changedRegion specified (as "left", "top", "bottom", or "right"), | |
434 | // it changes that region's width/height to changedRegionSize and | |
435 | // then resizes other regions that were affected. | |
436 | // changedChildId: | |
437 | // Id of the child which should be resized because splitter was dragged. | |
438 | // changedChildSize: | |
439 | // The new width/height (in pixels) to make specified child | |
440 | ||
441 | if(!this._borderBox || !this._borderBox.h){ | |
442 | // We are currently hidden, or we haven't been sized by our parent yet. | |
443 | // Abort. Someone will resize us later. | |
444 | return; | |
445 | } | |
446 | ||
447 | // Generate list of wrappers of my children in the order that I want layoutChildren() | |
448 | // to process them (i.e. from the outside to the inside) | |
449 | var wrappers = array.map(this.getChildren(), function(child, idx){ | |
450 | return { | |
451 | pane: child, | |
452 | weight: [ | |
453 | child.region == "center" ? Infinity : 0, | |
454 | child.layoutPriority, | |
455 | (this.design == "sidebar" ? 1 : -1) * (/top|bottom/.test(child.region) ? 1 : -1), | |
456 | idx | |
457 | ] | |
458 | }; | |
459 | }, this); | |
460 | wrappers.sort(function(a, b){ | |
461 | var aw = a.weight, bw = b.weight; | |
462 | for(var i=0; i<aw.length; i++){ | |
463 | if(aw[i] != bw[i]){ | |
464 | return aw[i] - bw[i]; | |
465 | } | |
466 | } | |
467 | return 0; | |
468 | }); | |
469 | ||
470 | // Make new list, combining the externally specified children with splitters and gutters | |
471 | var childrenAndSplitters = []; | |
472 | array.forEach(wrappers, function(wrapper){ | |
473 | var pane = wrapper.pane; | |
474 | childrenAndSplitters.push(pane); | |
475 | if(pane._splitterWidget){ | |
476 | childrenAndSplitters.push(pane._splitterWidget); | |
477 | } | |
478 | }); | |
479 | ||
480 | // Compute the box in which to lay out my children | |
481 | var dim = { | |
482 | l: this.pe.l, | |
483 | t: this.pe.t, | |
484 | w: this._borderBox.w - this.pe.w, | |
485 | h: this._borderBox.h - this.pe.h | |
486 | }; | |
487 | ||
488 | // Layout the children, possibly changing size due to a splitter drag | |
489 | layoutUtils.layoutChildren(this.domNode, dim, childrenAndSplitters, | |
490 | changedChildId, changedChildSize); | |
491 | }, | |
492 | ||
493 | destroyRecursive: function(){ | |
494 | // Destroy splitters first, while getChildren() still works | |
495 | array.forEach(this.getChildren(), function(child){ | |
496 | var splitter = child._splitterWidget; | |
497 | if(splitter){ | |
498 | splitter.destroy(); | |
499 | } | |
500 | delete child._splitterWidget; | |
501 | }); | |
502 | ||
503 | // Then destroy the real children, and myself | |
504 | this.inherited(arguments); | |
505 | } | |
506 | }); | |
507 | ||
508 | BorderContainer.ChildWidgetProperties = { | |
509 | // summary: | |
510 | // These properties can be specified for the children of a BorderContainer. | |
511 | ||
512 | // region: [const] String | |
513 | // Values: "top", "bottom", "leading", "trailing", "left", "right", "center". | |
514 | // See the `dijit/layout/BorderContainer` description for details. | |
515 | region: '', | |
516 | ||
517 | // layoutPriority: [const] Number | |
518 | // Children with a higher layoutPriority will be placed closer to the BorderContainer center, | |
519 | // between children with a lower layoutPriority. | |
520 | layoutPriority: 0, | |
521 | ||
522 | // splitter: [const] Boolean | |
523 | // Parameter for children where region != "center". | |
524 | // If true, enables user to resize the widget by putting a draggable splitter between | |
525 | // this widget and the region=center widget. | |
526 | splitter: false, | |
527 | ||
528 | // minSize: [const] Number | |
529 | // Specifies a minimum size (in pixels) for this widget when resized by a splitter. | |
530 | minSize: 0, | |
531 | ||
532 | // maxSize: [const] Number | |
533 | // Specifies a maximum size (in pixels) for this widget when resized by a splitter. | |
534 | maxSize: Infinity | |
535 | }; | |
536 | ||
537 | // Since any widget can be specified as a LayoutContainer child, mix it | |
538 | // into the base widget class. (This is a hack, but it's effective.) | |
539 | // This is for the benefit of the parser. Remove for 2.0. Also, hide from doc viewer. | |
540 | lang.extend(_WidgetBase, /*===== {} || =====*/ BorderContainer.ChildWidgetProperties); | |
541 | ||
542 | // For monkey patching | |
543 | BorderContainer._Splitter = _Splitter; | |
544 | BorderContainer._Gutter = _Gutter; | |
545 | ||
546 | return BorderContainer; | |
547 | }); |