Blame | Last modification | View Log | RSS feed
if(!dojo._hasResource["dijit.layout.SplitContainer"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.dojo._hasResource["dijit.layout.SplitContainer"] = true;dojo.provide("dijit.layout.SplitContainer");//// FIXME: make it prettier// FIXME: active dragging upwards doesn't always shift other bars (direction calculation is wrong in this case)//dojo.require("dojo.cookie");dojo.require("dijit.layout._LayoutWidget");dojo.declare("dijit.layout.SplitContainer",dijit.layout._LayoutWidget,{// summary:// A Container widget with sizing handles in-between each child// description:// Contains multiple children widgets, all of which are displayed side by side// (either horizontally or vertically); there's a bar between each of the children,// and you can adjust the relative size of each child by dragging the bars.//// You must specify a size (width and height) for the SplitContainer.//// activeSizing: Boolean// If true, the children's size changes as you drag the bar;// otherwise, the sizes don't change until you drop the bar (by mouse-up)activeSizing: false,// sizerWidth: Integer// Size in pixels of the bar between each childsizerWidth: 7, // FIXME: this should be a CSS attribute (at 7 because css wants it to be 7 until we fix to css)// orientation: String// either 'horizontal' or vertical; indicates whether the children are// arranged side-by-side or up/down.orientation: 'horizontal',// persist: Boolean// Save splitter positions in a cookiepersist: true,postMixInProperties: function(){this.inherited("postMixInProperties",arguments);this.isHorizontal = (this.orientation == 'horizontal');},postCreate: function(){this.inherited("postCreate",arguments);this.sizers = [];dojo.addClass(this.domNode, "dijitSplitContainer");// overflow has to be explicitly hidden for splitContainers using gekko (trac #1435)// to keep other combined css classes from inadvertantly making the overflow visibleif(dojo.isMozilla){this.domNode.style.overflow = '-moz-scrollbars-none'; // hidden doesn't work}// create the fake draggerif(typeof this.sizerWidth == "object"){try{ //FIXME: do this without a try/catchthis.sizerWidth = parseInt(this.sizerWidth.toString());}catch(e){ this.sizerWidth = 7; }}var sizer = this.virtualSizer = document.createElement('div');sizer.style.position = 'relative';// #1681: work around the dreaded 'quirky percentages in IE' layout bug// If the splitcontainer's dimensions are specified in percentages, it// will be resized when the virtualsizer is displayed in _showSizingLine// (typically expanding its bounds unnecessarily). This happens because// we use position: relative for .dijitSplitContainer.// The workaround: instead of changing the display style attribute,// switch to changing the zIndex (bring to front/move to back)sizer.style.zIndex = 10;sizer.className = this.isHorizontal ? 'dijitSplitContainerVirtualSizerH' : 'dijitSplitContainerVirtualSizerV';this.domNode.appendChild(sizer);dojo.setSelectable(sizer, false);},startup: function(){if(this._started){ return; }dojo.forEach(this.getChildren(), function(child, i, children){// attach the children and create the draggersthis._injectChild(child);if(i < children.length-1){this._addSizer();}}, this);if(this.persist){this._restoreState();}this.inherited("startup",arguments);this._started = true;},_injectChild: function(child){child.domNode.style.position = "absolute";dojo.addClass(child.domNode, "dijitSplitPane");},_addSizer: function(){var i = this.sizers.length;// TODO: use a template for this!!!var sizer = this.sizers[i] = document.createElement('div');sizer.className = this.isHorizontal ? 'dijitSplitContainerSizerH' : 'dijitSplitContainerSizerV';// add the thumb divvar thumb = document.createElement('div');thumb.className = 'thumb';sizer.appendChild(thumb);// FIXME: are you serious? why aren't we using mover start/stop combo?var self = this;var handler = (function(){ var sizer_i = i; return function(e){ self.beginSizing(e, sizer_i); } })();dojo.connect(sizer, "onmousedown", handler);this.domNode.appendChild(sizer);dojo.setSelectable(sizer, false);},removeChild: function(widget){// summary: Remove sizer, but only if widget is really our child and// we have at least one sizer to throw awayif(this.sizers.length && dojo.indexOf(this.getChildren(), widget) != -1){var i = this.sizers.length - 1;dojo._destroyElement(this.sizers[i]);this.sizers.length--;}// Remove widget and repaintthis.inherited("removeChild",arguments);if(this._started){this.layout();}},addChild: function(/*Widget*/ child, /*Integer?*/ insertIndex){// summary: Add a child widget to the container// child: a widget to add// insertIndex: postion in the "stack" to add the child widgetthis.inherited("addChild",arguments);if(this._started){// Do the stuff that startup() does for each widgetthis._injectChild(child);var children = this.getChildren();if(children.length > 1){this._addSizer();}// and then reposition (ie, shrink) every pane to make room for the new guythis.layout();}},layout: function(){// summary:// Do layout of panels// base class defines this._contentBox on initial creation and also// on resizethis.paneWidth = this._contentBox.w;this.paneHeight = this._contentBox.h;var children = this.getChildren();if(!children.length){ return; }//// calculate space//var space = this.isHorizontal ? this.paneWidth : this.paneHeight;if(children.length > 1){space -= this.sizerWidth * (children.length - 1);}//// calculate total of SizeShare values//var outOf = 0;dojo.forEach(children, function(child){outOf += child.sizeShare;});//// work out actual pixels per sizeshare unit//var pixPerUnit = space / outOf;//// set the SizeActual member of each pane//var totalSize = 0;dojo.forEach(children.slice(0, children.length - 1), function(child){var size = Math.round(pixPerUnit * child.sizeShare);child.sizeActual = size;totalSize += size;});children[children.length-1].sizeActual = space - totalSize;//// make sure the sizes are ok//this._checkSizes();//// now loop, positioning each pane and letting children resize themselves//var pos = 0;var size = children[0].sizeActual;this._movePanel(children[0], pos, size);children[0].position = pos;pos += size;// if we don't have any sizers, our layout method hasn't been called yet// so bail until we are called..TODO: REVISIT: need to change the startup// algorithm to guaranteed the ordering of calls to layout methodif(!this.sizers){return;}dojo.some(children.slice(1), function(child, i){// error-checkingif(!this.sizers[i]){return true;}// first we position the sizing handle before this panethis._moveSlider(this.sizers[i], pos, this.sizerWidth);this.sizers[i].position = pos;pos += this.sizerWidth;size = child.sizeActual;this._movePanel(child, pos, size);child.position = pos;pos += size;}, this);},_movePanel: function(panel, pos, size){if(this.isHorizontal){panel.domNode.style.left = pos + 'px'; // TODO: resize() takes l and t parameters too, don't need to set manuallypanel.domNode.style.top = 0;var box = {w: size, h: this.paneHeight};if(panel.resize){panel.resize(box);}else{dojo.marginBox(panel.domNode, box);}}else{panel.domNode.style.left = 0; // TODO: resize() takes l and t parameters too, don't need to set manuallypanel.domNode.style.top = pos + 'px';var box = {w: this.paneWidth, h: size};if(panel.resize){panel.resize(box);}else{dojo.marginBox(panel.domNode, box);}}},_moveSlider: function(slider, pos, size){if(this.isHorizontal){slider.style.left = pos + 'px';slider.style.top = 0;dojo.marginBox(slider, { w: size, h: this.paneHeight });}else{slider.style.left = 0;slider.style.top = pos + 'px';dojo.marginBox(slider, { w: this.paneWidth, h: size });}},_growPane: function(growth, pane){if(growth > 0){if(pane.sizeActual > pane.sizeMin){if((pane.sizeActual - pane.sizeMin) > growth){// stick all the growth in this panepane.sizeActual = pane.sizeActual - growth;growth = 0;}else{// put as much growth in here as we cangrowth -= pane.sizeActual - pane.sizeMin;pane.sizeActual = pane.sizeMin;}}}return growth;},_checkSizes: function(){var totalMinSize = 0;var totalSize = 0;var children = this.getChildren();dojo.forEach(children, function(child){totalSize += child.sizeActual;totalMinSize += child.sizeMin;});// only make adjustments if we have enough space for all the minimumsif(totalMinSize <= totalSize){var growth = 0;dojo.forEach(children, function(child){if(child.sizeActual < child.sizeMin){growth += child.sizeMin - child.sizeActual;child.sizeActual = child.sizeMin;}});if(growth > 0){var list = this.isDraggingLeft ? children.reverse() : children;dojo.forEach(list, function(child){growth = this._growPane(growth, child);}, this);}}else{dojo.forEach(children, function(child){child.sizeActual = Math.round(totalSize * (child.sizeMin / totalMinSize));});}},beginSizing: function(e, i){var children = this.getChildren();this.paneBefore = children[i];this.paneAfter = children[i+1];this.isSizing = true;this.sizingSplitter = this.sizers[i];if(!this.cover){this.cover = dojo.doc.createElement('div');this.domNode.appendChild(this.cover);var s = this.cover.style;s.position = 'absolute';s.zIndex = 1;s.top = 0;s.left = 0;s.width = "100%";s.height = "100%";}else{this.cover.style.zIndex = 1;}this.sizingSplitter.style.zIndex = 2;// TODO: REVISIT - we want MARGIN_BOX and core hasn't exposed that yet (but can't we use it anyway if we pay attention? we do elsewhere.)this.originPos = dojo.coords(children[0].domNode, true);if(this.isHorizontal){var client = (e.layerX ? e.layerX : e.offsetX);var screen = e.pageX;this.originPos = this.originPos.x;}else{var client = (e.layerY ? e.layerY : e.offsetY);var screen = e.pageY;this.originPos = this.originPos.y;}this.startPoint = this.lastPoint = screen;this.screenToClientOffset = screen - client;this.dragOffset = this.lastPoint - this.paneBefore.sizeActual - this.originPos - this.paneBefore.position;if(!this.activeSizing){this._showSizingLine();}//// attach mouse events//this._connects = [];this._connects.push(dojo.connect(document.documentElement, "onmousemove", this, "changeSizing"));this._connects.push(dojo.connect(document.documentElement, "onmouseup", this, "endSizing"));dojo.stopEvent(e);},changeSizing: function(e){if(!this.isSizing){ return; }this.lastPoint = this.isHorizontal ? e.pageX : e.pageY;this.movePoint();if(this.activeSizing){this._updateSize();}else{this._moveSizingLine();}dojo.stopEvent(e);},endSizing: function(e){if(!this.isSizing){ return; }if(this.cover){this.cover.style.zIndex = -1;}if(!this.activeSizing){this._hideSizingLine();}this._updateSize();this.isSizing = false;if(this.persist){this._saveState(this);}dojo.forEach(this._connects,dojo.disconnect);},movePoint: function(){// make sure lastPoint is a legal point to drag tovar p = this.lastPoint - this.screenToClientOffset;var a = p - this.dragOffset;a = this.legaliseSplitPoint(a);p = a + this.dragOffset;this.lastPoint = p + this.screenToClientOffset;},legaliseSplitPoint: function(a){a += this.sizingSplitter.position;this.isDraggingLeft = !!(a > 0);if(!this.activeSizing){var min = this.paneBefore.position + this.paneBefore.sizeMin;if(a < min){a = min;}var max = this.paneAfter.position + (this.paneAfter.sizeActual - (this.sizerWidth + this.paneAfter.sizeMin));if(a > max){a = max;}}a -= this.sizingSplitter.position;this._checkSizes();return a;},_updateSize: function(){//FIXME: sometimes this.lastPoint is NaNvar pos = this.lastPoint - this.dragOffset - this.originPos;var start_region = this.paneBefore.position;var end_region = this.paneAfter.position + this.paneAfter.sizeActual;this.paneBefore.sizeActual = pos - start_region;this.paneAfter.position = pos + this.sizerWidth;this.paneAfter.sizeActual = end_region - this.paneAfter.position;dojo.forEach(this.getChildren(), function(child){child.sizeShare = child.sizeActual;});if(this._started){this.layout();}},_showSizingLine: function(){this._moveSizingLine();dojo.marginBox(this.virtualSizer,this.isHorizontal ? { w: this.sizerWidth, h: this.paneHeight } : { w: this.paneWidth, h: this.sizerWidth });this.virtualSizer.style.display = 'block';},_hideSizingLine: function(){this.virtualSizer.style.display = 'none';},_moveSizingLine: function(){var pos = (this.lastPoint - this.startPoint) + this.sizingSplitter.position;dojo.style(this.virtualSizer,(this.isHorizontal ? "left" : "top"),pos+"px");// this.virtualSizer.style[ this.isHorizontal ? "left" : "top" ] = pos + 'px'; // FIXME: remove this line if the previous is better},_getCookieName: function(i){return this.id + "_" + i;},_restoreState: function(){dojo.forEach(this.getChildren(), function(child, i){var cookieName = this._getCookieName(i);var cookieValue = dojo.cookie(cookieName);if(cookieValue){var pos = parseInt(cookieValue);if(typeof pos == "number"){child.sizeShare = pos;}}}, this);},_saveState: function(){dojo.forEach(this.getChildren(), function(child, i){dojo.cookie(this._getCookieName(i), child.sizeShare);}, this);}});// These arguments can be specified for the children of a SplitContainer.// Since any widget can be specified as a SplitContainer child, mix them// into the base widget class. (This is a hack, but it's effective.)dojo.extend(dijit._Widget, {// sizeMin: Integer// Minimum size (width or height) of a child of a SplitContainer.// The value is relative to other children's sizeShare properties.sizeMin: 10,// sizeShare: Integer// Size (width or height) of a child of a SplitContainer.// The value is relative to other children's sizeShare properties.// For example, if there are two children and each has sizeShare=10, then// each takes up 50% of the available space.sizeShare: 10});}