define("dijit/layout/_ContentPaneResizeMixin", [ "dojo/_base/array", // array.filter array.forEach "dojo/_base/declare", // declare "dojo/dom-class", // domClass.contains domClass.toggle "dojo/dom-geometry",// domGeometry.contentBox domGeometry.marginBox "dojo/dom-style", "dojo/_base/lang", // lang.mixin "dojo/query", // query "dojo/sniff", // has("ie") "../registry", // registry.byId "../Viewport", "./utils" // marginBox2contextBox ], function(array, declare, domClass, domGeometry, domStyle, lang, query, has, registry, Viewport, layoutUtils){ // module: // dijit/layout/_ContentPaneResizeMixin return declare("dijit.layout._ContentPaneResizeMixin", null, { // summary: // Resize() functionality of ContentPane. If there's a single layout widget // child then it will call resize() with the same dimensions as the ContentPane. // Otherwise just calls resize on each child. // // Also implements basic startup() functionality, where starting the parent // will start the children // doLayout: Boolean // - false - don't adjust size of children // - true - if there is a single visible child widget, set it's size to however big the ContentPane is doLayout: true, // isLayoutContainer: [protected] Boolean // Indicates that this widget will call resize() on it's child widgets // when they become visible. isLayoutContainer: true, startup: function(){ // summary: // See `dijit/layout/_LayoutWidget.startup()` for description. // Although ContentPane doesn't extend _LayoutWidget, it does implement // the same API. if(this._started){ return; } var parent = this.getParent(); this._childOfLayoutWidget = parent && parent.isLayoutContainer; // I need to call resize() on my child/children (when I become visible), unless // I'm the child of a layout widget in which case my parent will call resize() on me and I'll do it then. this._needLayout = !this._childOfLayoutWidget; this.inherited(arguments); if(this._isShown()){ this._onShow(); } if(!this._childOfLayoutWidget){ // Since my parent isn't a layout container, and my style *may be* width=height=100% // or something similar (either set directly or via a CSS class), // monitor when viewport size changes so that I can re-layout. // This is more for subclasses of ContentPane than ContentPane itself, although it // could be useful for a ContentPane if it has a single child widget inheriting ContentPane's size. this.own(Viewport.on("resize", lang.hitch(this, "resize"))); } }, _checkIfSingleChild: function(){ // summary: // Test if we have exactly one visible widget as a child, // and if so assume that we are a container for that widget, // and should propagate startup() and resize() calls to it. // Skips over things like data stores since they aren't visible. var candidateWidgets = [], otherVisibleNodes = false; query("> *", this.containerNode).some(function(node){ var widget = registry.byNode(node); if(widget && widget.resize){ candidateWidgets.push(widget); }else if(node.offsetHeight){ otherVisibleNodes = true; } }); this._singleChild = candidateWidgets.length == 1 && !otherVisibleNodes ? candidateWidgets[0] : null; // So we can set overflow: hidden to avoid a safari bug w/scrollbars showing up (#9449) domClass.toggle(this.containerNode, this.baseClass + "SingleChild", !!this._singleChild); }, resize: function(changeSize, resultSize){ // summary: // See `dijit/layout/_LayoutWidget.resize()` for description. // Although ContentPane doesn't extend _LayoutWidget, it does implement // the same API. this._resizeCalled = true; this._scheduleLayout(changeSize, resultSize); }, _scheduleLayout: function(changeSize, resultSize){ // summary: // Resize myself, and call resize() on each of my child layout widgets, either now // (if I'm currently visible) or when I become visible if(this._isShown()){ this._layout(changeSize, resultSize); }else{ this._needLayout = true; this._changeSize = changeSize; this._resultSize = resultSize; } }, _layout: function(changeSize, resultSize){ // summary: // Resize myself according to optional changeSize/resultSize parameters, like a layout widget. // Also, since I am an isLayoutContainer widget, each of my children expects me to // call resize() or layout() on it. // // Should be called on initialization and also whenever we get new content // (from an href, or from set('content', ...))... but deferred until // the ContentPane is visible delete this._needLayout; // For the TabContainer --> BorderContainer --> ContentPane case, _onShow() is // never called directly, so resize() is our trigger to do the initial href download (see [20099]). // However, don't load href for closed TitlePanes. if(!this._wasShown && this.open !== false){ this._onShow(); } // Set margin box size, unless it wasn't specified, in which case use current size. if(changeSize){ domGeometry.setMarginBox(this.domNode, changeSize); } // Compute content box size of containerNode in case we [later] need to size our single child. var cn = this.containerNode; if(cn === this.domNode){ // If changeSize or resultSize was passed to this method and this.containerNode == // this.domNode then we can compute the content-box size without querying the node, // which is more reliable (similar to LayoutWidget.resize) (see for example #9449). var mb = resultSize || {}; lang.mixin(mb, changeSize || {}); // changeSize overrides resultSize if(!("h" in mb) || !("w" in mb)){ mb = lang.mixin(domGeometry.getMarginBox(cn), mb); // just use domGeometry.setMarginBox() to fill in missing values } this._contentBox = layoutUtils.marginBox2contentBox(cn, mb); }else{ this._contentBox = domGeometry.getContentBox(cn); } this._layoutChildren(); }, _layoutChildren: function(){ // Call _checkIfSingleChild() again in case app has manually mucked w/the content // of the ContentPane (rather than changing it through the set("content", ...) API. if(this.doLayout){ this._checkIfSingleChild(); } if(this._singleChild && this._singleChild.resize){ var cb = this._contentBox || domGeometry.getContentBox(this.containerNode); // note: if widget has padding this._contentBox will have l and t set, // but don't pass them to resize() or it will doubly-offset the child this._singleChild.resize({w: cb.w, h: cb.h}); }else{ // All my child widgets are independently sized (rather than matching my size), // but I still need to call resize() on each child to make it layout. array.forEach(this.getChildren(), function(widget){ if(widget.resize){ widget.resize(); } }); } }, _isShown: function(){ // summary: // Returns true if the content is currently shown. // description: // If I am a child of a layout widget then it actually returns true if I've ever been visible, // not whether I'm currently visible, since that's much faster than tracing up the DOM/widget // tree every call, and at least solves the performance problem on page load by deferring loading // hidden ContentPanes until they are first shown if(this._childOfLayoutWidget){ // If we are TitlePane, etc - we return that only *IF* we've been resized if(this._resizeCalled && "open" in this){ return this.open; } return this._resizeCalled; }else if("open" in this){ return this.open; // for TitlePane, etc. }else{ var node = this.domNode, parent = this.domNode.parentNode; return (node.style.display != 'none') && (node.style.visibility != 'hidden') && !domClass.contains(node, "dijitHidden") && parent && parent.style && (parent.style.display != 'none'); } }, _onShow: function(){ // summary: // Called when the ContentPane is made visible // description: // For a plain ContentPane, this is called on initialization, from startup(). // If the ContentPane is a hidden pane of a TabContainer etc., then it's // called whenever the pane is made visible. // // Does layout/resize of child widget(s) // Need to keep track of whether ContentPane has been shown (which is different than // whether or not it's currently visible). this._wasShown = true; if(this._needLayout){ // If a layout has been scheduled for when we become visible, do it now this._layout(this._changeSize, this._resultSize); } this.inherited(arguments); } }); });