ResizeHandle.js 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382
  1. /*
  2. Copyright (c) 2004-2012, The Dojo Foundation All Rights Reserved.
  3. Available via Academic Free License >= 2.1 OR the modified BSD license.
  4. see: http://dojotoolkit.org/license for details
  5. */
  6. if(!dojo._hasResource["dojox.layout.ResizeHandle"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
  7. dojo._hasResource["dojox.layout.ResizeHandle"] = true;
  8. dojo.provide("dojox.layout.ResizeHandle");
  9. dojo.experimental("dojox.layout.ResizeHandle");
  10. dojo.require("dijit._Widget");
  11. dojo.require("dijit._Templated");
  12. dojo.require("dojo.fx");
  13. dojo.require("dojo.window");
  14. dojo.declare("dojox.layout.ResizeHandle",
  15. [dijit._Widget, dijit._Templated],
  16. {
  17. // summary: A dragable handle used to resize an attached node.
  18. //
  19. // description:
  20. // The handle on the bottom-right corner of FloatingPane or other widgets that allows
  21. // the widget to be resized.
  22. // Typically not used directly.
  23. //
  24. // targetId: String
  25. // id of the Widget OR DomNode that I will size
  26. targetId: "",
  27. // targetContainer: DomNode
  28. // over-ride targetId and attch this handle directly to a reference of a DomNode
  29. targetContainer: null,
  30. // resizeAxis: String
  31. // one of: x|y|xy limit resizing to a single axis, default to xy ...
  32. resizeAxis: "xy",
  33. // activeResize: Boolean
  34. // if true, node will size realtime with mouse movement,
  35. // if false, node will create virtual node, and only resize target on mouseUp
  36. activeResize: false,
  37. // activeResizeClass: String
  38. // css class applied to virtual resize node.
  39. activeResizeClass: "dojoxResizeHandleClone",
  40. // animateSizing: Boolean
  41. // only applicable if activeResize = false. onMouseup, animate the node to the
  42. // new size
  43. animateSizing: true,
  44. // animateMethod: String
  45. // one of "chain" or "combine" ... visual effect only. combine will "scale"
  46. // node to size, "chain" will alter width, then height
  47. animateMethod: "chain",
  48. // animateDuration: Integer
  49. // time in MS to run sizing animation. if animateMethod="chain", total animation
  50. // playtime is 2*animateDuration
  51. animateDuration: 225,
  52. // minHeight: Integer
  53. // smallest height in px resized node can be
  54. minHeight: 100,
  55. // minWidth: Integer
  56. // smallest width in px resize node can be
  57. minWidth: 100,
  58. // constrainMax: Boolean
  59. // Toggle if this widget cares about the maxHeight and maxWidth
  60. // parameters.
  61. constrainMax: false,
  62. // maxHeight: Integer
  63. // Largest height size in px the resize node can become.
  64. maxHeight:0,
  65. // maxWidth: Integer
  66. // Largest width size in px the reize node can become.
  67. maxWidth:0,
  68. // fixedAspect: Boolean
  69. // Toggle to enable this widget to maintain the aspect
  70. // ratio of the attached node.
  71. fixedAspect: false,
  72. // intermediateChanges: Boolean
  73. // Toggle to enable/disable this widget from firing onResize
  74. // events at every step of a resize. If `activeResize` is true,
  75. // and this is false, onResize only fires _after_ the drop
  76. // operation. Animated resizing is not affected by this setting.
  77. intermediateChanges: false,
  78. // startTopic: String
  79. // The name of the topic this resizehandle publishes when resize is starting
  80. startTopic: "/dojo/resize/start",
  81. // endTopic: String
  82. // The name of the topic this resizehandle publishes when resize is complete
  83. endTopic:"/dojo/resize/stop",
  84. templateString: '<div dojoAttachPoint="resizeHandle" class="dojoxResizeHandle"><div></div></div>',
  85. postCreate: function(){
  86. // summary: setup our one major listener upon creation
  87. this.connect(this.resizeHandle, "onmousedown", "_beginSizing");
  88. if(!this.activeResize){
  89. // there shall be only a single resize rubberbox that at the top
  90. // level so that we can overlay it on anything whenever the user
  91. // resizes something. Since there is only one mouse pointer he
  92. // can't at once resize multiple things interactively.
  93. this._resizeHelper = dijit.byId('dojoxGlobalResizeHelper');
  94. if(!this._resizeHelper){
  95. this._resizeHelper = new dojox.layout._ResizeHelper({
  96. id: 'dojoxGlobalResizeHelper'
  97. }).placeAt(dojo.body());
  98. dojo.addClass(this._resizeHelper.domNode, this.activeResizeClass);
  99. }
  100. }else{ this.animateSizing = false; }
  101. if(!this.minSize){
  102. this.minSize = { w: this.minWidth, h: this.minHeight };
  103. }
  104. if(this.constrainMax){
  105. this.maxSize = { w: this.maxWidth, h: this.maxHeight }
  106. }
  107. // should we modify the css for the cursor hover to n-resize nw-resize and w-resize?
  108. this._resizeX = this._resizeY = false;
  109. var addClass = dojo.partial(dojo.addClass, this.resizeHandle);
  110. switch(this.resizeAxis.toLowerCase()){
  111. case "xy" :
  112. this._resizeX = this._resizeY = true;
  113. // FIXME: need logic to determine NW or NE class to see
  114. // based on which [todo] corner is clicked
  115. addClass("dojoxResizeNW");
  116. break;
  117. case "x" :
  118. this._resizeX = true;
  119. addClass("dojoxResizeW");
  120. break;
  121. case "y" :
  122. this._resizeY = true;
  123. addClass("dojoxResizeN");
  124. break;
  125. }
  126. },
  127. _beginSizing: function(/*Event*/ e){
  128. // summary: setup movement listeners and calculate initial size
  129. if(this._isSizing){ return false; }
  130. dojo.publish(this.startTopic, [ this ]);
  131. this.targetWidget = dijit.byId(this.targetId);
  132. this.targetDomNode = this.targetWidget ? this.targetWidget.domNode : dojo.byId(this.targetId);
  133. if(this.targetContainer){ this.targetDomNode = this.targetContainer; }
  134. if(!this.targetDomNode){ return false; }
  135. if(!this.activeResize){
  136. var c = dojo.position(this.targetDomNode, true);
  137. console.log(c);
  138. console.log(dojo.window.getBox());
  139. this._resizeHelper.resize({l: c.x, t: c.y, w: c.w, h: c.h});
  140. this._resizeHelper.show();
  141. if(!this.isLeftToRight()){
  142. this._resizeHelper.startPosition = {l: c.x, t: c.y};
  143. }
  144. }
  145. this._isSizing = true;
  146. this.startPoint = { x:e.clientX, y:e.clientY};
  147. // FIXME: this is funky: marginBox adds height, contentBox ignores padding (expected, but foo!)
  148. var mb = this.targetWidget ? dojo.marginBox(this.targetDomNode) : dojo.contentBox(this.targetDomNode);
  149. this.startSize = { w:mb.w, h:mb.h };
  150. if(!this.isLeftToRight() && dojo.style(this.targetDomNode, "position") == "absolute"){
  151. this.startPosition = {l: mb.l, t: mb.t};
  152. }
  153. if(this.fixedAspect){
  154. var max, val;
  155. if(mb.w > mb.h){
  156. max = "w";
  157. val = mb.w / mb.h
  158. }else{
  159. max = "h";
  160. val = mb.h / mb.w
  161. }
  162. this._aspect = { prop: max };
  163. this._aspect[max] = val;
  164. }
  165. this._pconnects = [];
  166. this._pconnects.push(dojo.connect(dojo.doc,"onmousemove",this,"_updateSizing"));
  167. this._pconnects.push(dojo.connect(dojo.doc,"onmouseup", this, "_endSizing"));
  168. dojo.stopEvent(e);
  169. },
  170. _updateSizing: function(/*Event*/ e){
  171. // summary: called when moving the ResizeHandle ... determines
  172. // new size based on settings/position and sets styles.
  173. if(this.activeResize){
  174. this._changeSizing(e);
  175. }else{
  176. var tmp = this._getNewCoords(e, this._resizeHelper.startPosition);
  177. if(tmp === false){ return; }
  178. this._resizeHelper.resize(tmp);
  179. }
  180. e.preventDefault();
  181. },
  182. _getNewCoords: function(/* Event */ e, /* Object */startPosition){
  183. // On IE, if you move the mouse above/to the left of the object being resized,
  184. // sometimes clientX/Y aren't set, apparently. Just ignore the event.
  185. try{
  186. if(!e.clientX || !e.clientY){ return false; }
  187. }catch(e){
  188. // sometimes you get an exception accessing above fields...
  189. return false;
  190. }
  191. this._activeResizeLastEvent = e;
  192. var dx = (this.isLeftToRight()? this.startPoint.x - e.clientX: e.clientX - this.startPoint.x),
  193. dy = this.startPoint.y - e.clientY,
  194. newW = this.startSize.w - (this._resizeX ? dx : 0),
  195. newH = this.startSize.h - (this._resizeY ? dy : 0)
  196. ;
  197. var newSize = this._checkConstraints(newW, newH); // Object
  198. startPosition = (startPosition || this.startPosition);
  199. if(newSize && startPosition && this._resizeX){
  200. // adjust x position for RtoL
  201. newSize.l = startPosition.l + dx;
  202. if(newSize.w != newW){
  203. newSize.l += (newW - newSize.w);
  204. }
  205. newSize.t = startPosition.t;
  206. }
  207. return newSize;
  208. },
  209. _checkConstraints: function(newW, newH){
  210. // summary: filter through the various possible constaint possibilities.
  211. // minimum size check
  212. if(this.minSize){
  213. var tm = this.minSize;
  214. if(newW < tm.w){
  215. newW = tm.w;
  216. }
  217. if(newH < tm.h){
  218. newH = tm.h;
  219. }
  220. }
  221. // maximum size check:
  222. if(this.constrainMax && this.maxSize){
  223. var ms = this.maxSize;
  224. if(newW > ms.w){
  225. newW = ms.w;
  226. }
  227. if(newH > ms.h){
  228. newH = ms.h;
  229. }
  230. }
  231. if(this.fixedAspect){
  232. var ta = this._aspect[this._aspect.prop];
  233. if(newW < newH){
  234. newH = newW * ta;
  235. }else if(newH < newW){
  236. newW = newH * ta;
  237. }
  238. }
  239. return { w: newW, h: newH }; // Object
  240. },
  241. _changeSizing: function(/*Event*/ e){
  242. // summary: apply sizing information based on information in (e) to attached node
  243. var tmp = this._getNewCoords(e);
  244. if(tmp === false){ return; }
  245. if(this.targetWidget && dojo.isFunction(this.targetWidget.resize)){
  246. this.targetWidget.resize(tmp);
  247. }else{
  248. if(this.animateSizing){
  249. var anim = dojo.fx[this.animateMethod]([
  250. dojo.animateProperty({
  251. node: this.targetDomNode,
  252. properties: {
  253. width: { start: this.startSize.w, end: tmp.w }
  254. },
  255. duration: this.animateDuration
  256. }),
  257. dojo.animateProperty({
  258. node: this.targetDomNode,
  259. properties: {
  260. height: { start: this.startSize.h, end: tmp.h }
  261. },
  262. duration: this.animateDuration
  263. })
  264. ]);
  265. anim.play();
  266. }else{
  267. dojo.style(this.targetDomNode,{
  268. width: tmp.w + "px",
  269. height: tmp.h + "px"
  270. });
  271. }
  272. }
  273. if(this.intermediateChanges){
  274. this.onResize(e);
  275. }
  276. },
  277. _endSizing: function(/*Event*/ e){
  278. // summary: disconnect listenrs and cleanup sizing
  279. dojo.forEach(this._pconnects, dojo.disconnect);
  280. var pub = dojo.partial(dojo.publish, this.endTopic, [ this ]);
  281. if(!this.activeResize){
  282. this._resizeHelper.hide();
  283. this._changeSizing(e);
  284. setTimeout(pub, this.animateDuration + 15);
  285. }else{
  286. pub();
  287. }
  288. this._isSizing = false;
  289. this.onResize(e);
  290. },
  291. onResize: function(e){
  292. // summary: Stub fired when sizing is done. Fired once
  293. // after resize, or often when `intermediateChanges` is
  294. // set to true.
  295. }
  296. });
  297. dojo.declare("dojox.layout._ResizeHelper",
  298. dijit._Widget,
  299. {
  300. // summary: A global private resize helper shared between any
  301. // `dojox.layout.ResizeHandle` with activeSizing off.
  302. show: function(){
  303. // summary: show helper to start resizing
  304. dojo.fadeIn({
  305. node: this.domNode,
  306. duration: 120,
  307. beforeBegin: function(n){ dojo.style(n, "display", "") }
  308. }).play();
  309. },
  310. hide: function(){
  311. // summary: hide helper after resizing is complete
  312. dojo.fadeOut({
  313. node: this.domNode,
  314. duration: 250,
  315. onEnd: function(n){ dojo.style(n, "display", "none") }
  316. }).play();
  317. },
  318. resize: function(/* Object */dim){
  319. // summary: size the widget and place accordingly
  320. // FIXME: this is off when padding present
  321. dojo.marginBox(this.domNode, dim);
  322. }
  323. });
  324. }