You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
389 lines
11 KiB
389 lines
11 KiB
define(["dojo/_base/kernel","dojo/_base/lang","dojo/_base/connect","dojo/_base/array","dojo/_base/event", |
|
"dojo/_base/fx","dojo/_base/window","dojo/fx","dojo/dom","dojo/dom-class", |
|
"dojo/dom-geometry","dojo/dom-style","dojo/_base/declare", "dojo/touch", |
|
"dijit/_base/manager","dijit/_Widget","dijit/_TemplatedMixin" |
|
], function ( |
|
kernel, lang, connect, arrayUtil, eventUtil, fxBase, windowBase, fxUtil, |
|
domUtil, domClass, domGeometry, domStyle, declare, touch, manager, Widget, TemplatedMixin) { |
|
|
|
kernel.experimental("dojox.layout.ResizeHandle"); |
|
|
|
var _ResizeHelper = 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); |
|
} |
|
|
|
}); |
|
|
|
var ResizeHandle = declare("dojox.layout.ResizeHandle",[Widget, TemplatedMixin], |
|
{ |
|
// summary: |
|
// A draggable 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 resize 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: '<div dojoAttachPoint="resizeHandle" class="dojoxResizeHandle"><div></div></div>', |
|
|
|
postCreate: function(){ |
|
// summary: |
|
// setup our one major listener upon creation |
|
this.connect(this.resizeHandle, touch.press, "_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(); |
|
if(!this.isLeftToRight()){ |
|
this._resizeHelper.startPosition = {l: c.x, t: c.y}; |
|
} |
|
} |
|
|
|
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); |
|
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}; |
|
if(!this.isLeftToRight() && domStyle.get(this.targetDomNode, "position") == "absolute"){ |
|
var p = domGeometry.position(this.targetDomNode, true); |
|
this.startPosition = {l: p.x, t: p.y}; |
|
} |
|
|
|
this._pconnects = [ |
|
connect.connect(windowBase.doc, touch.move, this,"_updateSizing"), |
|
connect.connect(windowBase.doc, touch.release, 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', this._resizeHelper.startPosition); |
|
if(tmp === false){ return; } |
|
this._resizeHelper.resize(tmp); |
|
} |
|
e.preventDefault(); |
|
}, |
|
|
|
_getNewCoords: function(/* Event */ e, /* String */ box, /* Object */startPosition){ |
|
|
|
// 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(err){ |
|
// 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) |
|
; |
|
|
|
startPosition = (startPosition || this.startPosition); |
|
if(startPosition && this._resizeX){ |
|
// adjust x position for RtoL |
|
r.l = startPosition.l + dx; |
|
if(r.w != newW){ |
|
r.l += (newW - r.w); |
|
} |
|
r.t = startPosition.t; |
|
} |
|
|
|
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. |
|
} |
|
|
|
}); |
|
|
|
return ResizeHandle; |
|
});
|
|
|