//>>built define("dojox/layout/ResizeHandle", ["dojo/_base/kernel","dojo/_base/lang","dojo/_base/connect","dojo/_base/array","dojo/_base/event", "dojo/_base/fx","dojo/_base/window","dojo/fx","dojo/window","dojo/dom","dojo/dom-class", "dojo/dom-geometry","dojo/dom-style","dijit/_base/manager","dijit/_Widget","dijit/_TemplatedMixin", "dojo/_base/declare"], function ( kernel, lang, connect, arrayUtil, eventUtil, fxBase, windowBase, fxUtil, windowUtil, domUtil, domClass, domGeometry, domStyle, manager, Widget, TemplatedMixin, declare) { kernel.experimental("dojox.layout.ResizeHandle"); /*===== var Widget = dijit._Widget; var TemplatedMixin = dijit._TemplatedMixin; =====*/ var ResizeHandle = declare("dojox.layout.ResizeHandle",[Widget, TemplatedMixin], { // summary: A dragable handle used to resize an attached node. // // description: // The handle on the bottom-right corner of FloatingPane or other widgets that allows // the widget to be resized. // Typically not used directly. // // targetId: String // id of the Widget OR DomNode that I will size targetId: "", // targetContainer: DomNode // over-ride targetId and attch this handle directly to a reference of a DomNode targetContainer: null, // resizeAxis: String // one of: x|y|xy limit resizing to a single axis, default to xy ... resizeAxis: "xy", // activeResize: Boolean // if true, node will size realtime with mouse movement, // if false, node will create virtual node, and only resize target on mouseUp activeResize: false, // activeResizeClass: String // css class applied to virtual resize node. activeResizeClass: "dojoxResizeHandleClone", // animateSizing: Boolean // only applicable if activeResize = false. onMouseup, animate the node to the // new size animateSizing: true, // animateMethod: String // one of "chain" or "combine" ... visual effect only. combine will "scale" // node to size, "chain" will alter width, then height animateMethod: "chain", // animateDuration: Integer // time in MS to run sizing animation. if animateMethod="chain", total animation // playtime is 2*animateDuration animateDuration: 225, // minHeight: Integer // smallest height in px resized node can be minHeight: 100, // minWidth: Integer // smallest width in px resize node can be minWidth: 100, // constrainMax: Boolean // Toggle if this widget cares about the maxHeight and maxWidth // parameters. constrainMax: false, // maxHeight: Integer // Largest height size in px the resize node can become. maxHeight:0, // maxWidth: Integer // Largest width size in px the reize node can become. maxWidth:0, // fixedAspect: Boolean // Toggle to enable this widget to maintain the aspect // ratio of the attached node. fixedAspect: false, // intermediateChanges: Boolean // Toggle to enable/disable this widget from firing onResize // events at every step of a resize. If `activeResize` is true, // and this is false, onResize only fires _after_ the drop // operation. Animated resizing is not affected by this setting. intermediateChanges: false, // startTopic: String // The name of the topic this resizehandle publishes when resize is starting startTopic: "/dojo/resize/start", // endTopic: String // The name of the topic this resizehandle publishes when resize is complete endTopic:"/dojo/resize/stop", templateString: '
', postCreate: function(){ // summary: setup our one major listener upon creation this.connect(this.resizeHandle, "onmousedown", "_beginSizing"); if(!this.activeResize){ // there shall be only a single resize rubberbox that at the top // level so that we can overlay it on anything whenever the user // resizes something. Since there is only one mouse pointer he // can't at once resize multiple things interactively. this._resizeHelper = manager.byId('dojoxGlobalResizeHelper'); if(!this._resizeHelper){ this._resizeHelper = new _ResizeHelper({ id: 'dojoxGlobalResizeHelper' }).placeAt(windowBase.body()); domClass.add(this._resizeHelper.domNode, this.activeResizeClass); } }else{ this.animateSizing = false; } if(!this.minSize){ this.minSize = { w: this.minWidth, h: this.minHeight }; } if(this.constrainMax){ this.maxSize = { w: this.maxWidth, h: this.maxHeight } } // should we modify the css for the cursor hover to n-resize nw-resize and w-resize? this._resizeX = this._resizeY = false; var addClass = lang.partial(domClass.add, this.resizeHandle); switch(this.resizeAxis.toLowerCase()){ case "xy" : this._resizeX = this._resizeY = true; // FIXME: need logic to determine NW or NE class to see // based on which [todo] corner is clicked addClass("dojoxResizeNW"); break; case "x" : this._resizeX = true; addClass("dojoxResizeW"); break; case "y" : this._resizeY = true; addClass("dojoxResizeN"); break; } }, _beginSizing: function(/*Event*/ e){ // summary: setup movement listeners and calculate initial size if(this._isSizing){ return; } connect.publish(this.startTopic, [ this ]); this.targetWidget = manager.byId(this.targetId); this.targetDomNode = this.targetWidget ? this.targetWidget.domNode : domUtil.byId(this.targetId); if(this.targetContainer){ this.targetDomNode = this.targetContainer; } if(!this.targetDomNode){ return; } if(!this.activeResize){ var c = domGeometry.position(this.targetDomNode, true); this._resizeHelper.resize({l: c.x, t: c.y, w: c.w, h: c.h}); this._resizeHelper.show(); } this._isSizing = true; this.startPoint = { x:e.clientX, y:e.clientY }; // widget.resize() or setting style.width/height expects native box model dimension // (in most cases content-box, but it may be border-box if in backcompact mode) var style = domStyle.getComputedStyle(this.targetDomNode), borderModel = domGeometry.boxModel==='border-model', padborder = borderModel?{w:0,h:0}:domGeometry.getPadBorderExtents(this.targetDomNode, style), margin = domGeometry.getMarginExtents(this.targetDomNode, style), mb; mb = this.startSize = { w: domStyle.get(this.targetDomNode, 'width', style), h: domStyle.get(this.targetDomNode, 'height', style), //ResizeHelper.resize expects a bounding box of the //border box, so let's keep track of padding/border //width/height as well pbw: padborder.w, pbh: padborder.h, mw: margin.w, mh: margin.h}; this._pconnects = [ connect.connect(windowBase.doc,"onmousemove",this,"_updateSizing"), connect.connect(windowBase.doc,"onmouseup", this, "_endSizing") ]; eventUtil.stop(e); }, _updateSizing: function(/*Event*/ e){ // summary: called when moving the ResizeHandle ... determines // new size based on settings/position and sets styles. if(this.activeResize){ this._changeSizing(e); }else{ var tmp = this._getNewCoords(e, 'border'); if(tmp === false){ return; } this._resizeHelper.resize(tmp); } e.preventDefault(); }, _getNewCoords: function(/* Event */ e, /* String */ box){ // On IE, if you move the mouse above/to the left of the object being resized, // sometimes clientX/Y aren't set, apparently. Just ignore the event. try{ if(!e.clientX || !e.clientY){ return false; } }catch(e){ // sometimes you get an exception accessing above fields... return false; } this._activeResizeLastEvent = e; var dx = (this.isLeftToRight()?1:-1) * (this.startPoint.x - e.clientX), dy = this.startPoint.y - e.clientY, newW = this.startSize.w - (this._resizeX ? dx : 0), newH = this.startSize.h - (this._resizeY ? dy : 0), r = this._checkConstraints(newW, newH) ; switch(box){ case 'margin': r.w += this.startSize.mw; r.h += this.startSize.mh; //pass through case "border": r.w += this.startSize.pbw; r.h += this.startSize.pbh; break; //default: //native, do nothing } return r; // Object }, _checkConstraints: function(newW, newH){ // summary: filter through the various possible constaint possibilities. // minimum size check if(this.minSize){ var tm = this.minSize; if(newW < tm.w){ newW = tm.w; } if(newH < tm.h){ newH = tm.h; } } // maximum size check: if(this.constrainMax && this.maxSize){ var ms = this.maxSize; if(newW > ms.w){ newW = ms.w; } if(newH > ms.h){ newH = ms.h; } } if(this.fixedAspect){ var w = this.startSize.w, h = this.startSize.h, delta = w * newH - h * newW; if(delta<0){ newW = newH * w / h; }else if(delta>0){ newH = newW * h / w; } } return { w: newW, h: newH }; // Object }, _changeSizing: function(/*Event*/ e){ // summary: apply sizing information based on information in (e) to attached node var isWidget = this.targetWidget && lang.isFunction(this.targetWidget.resize), tmp = this._getNewCoords(e, isWidget && 'margin'); if(tmp === false){ return; } if(isWidget){ this.targetWidget.resize(tmp); }else{ if(this.animateSizing){ var anim = fxUtil[this.animateMethod]([ fxBase.animateProperty({ node: this.targetDomNode, properties: { width: { start: this.startSize.w, end: tmp.w } }, duration: this.animateDuration }), fxBase.animateProperty({ node: this.targetDomNode, properties: { height: { start: this.startSize.h, end: tmp.h } }, duration: this.animateDuration }) ]); anim.play(); }else{ domStyle.set(this.targetDomNode,{ width: tmp.w + "px", height: tmp.h + "px" }); } } if(this.intermediateChanges){ this.onResize(e); } }, _endSizing: function(/*Event*/ e){ // summary: disconnect listenrs and cleanup sizing arrayUtil.forEach(this._pconnects, connect.disconnect); var pub = lang.partial(connect.publish, this.endTopic, [ this ]); if(!this.activeResize){ this._resizeHelper.hide(); this._changeSizing(e); setTimeout(pub, this.animateDuration + 15); }else{ pub(); } this._isSizing = false; this.onResize(e); }, onResize: function(e){ // summary: Stub fired when sizing is done. Fired once // after resize, or often when `intermediateChanges` is // set to true. } }); var _ResizeHelper = dojo.declare("dojox.layout._ResizeHelper", Widget, { // summary: A global private resize helper shared between any // `dojox.layout.ResizeHandle` with activeSizing off. show: function(){ // summary: show helper to start resizing domStyle.set(this.domNode, "display", ""); }, hide: function(){ // summary: hide helper after resizing is complete domStyle.set(this.domNode, "display", "none"); }, resize: function(/* Object */dim){ // summary: size the widget and place accordingly domGeometry.setMarginBox(this.domNode, dim); } }); return ResizeHandle; });