dragresize_commented.js 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352
  1. /*
  2. DragResize v1.0
  3. (c) 2005-2006 Angus Turnbull, TwinHelix Designs http://www.twinhelix.com
  4. Licensed under the CC-GNU LGPL, version 2.1 or later:
  5. http://creativecommons.org/licenses/LGPL/2.1/
  6. This is distributed WITHOUT ANY WARRANTY; without even the implied
  7. warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
  8. */
  9. // Common API code.
  10. if (typeof addEvent != 'function')
  11. {
  12. var addEvent = function(o, t, f, l)
  13. {
  14. var d = 'addEventListener', n = 'on' + t, rO = o, rT = t, rF = f, rL = l;
  15. if (o[d] && !l) return o[d](t, f, false);
  16. if (!o._evts) o._evts = {};
  17. if (!o._evts[t])
  18. {
  19. o._evts[t] = o[n] ? { b: o[n] } : {};
  20. o[n] = new Function('e',
  21. 'var r = true, o = this, a = o._evts["' + t + '"], i; for (i in a) {' +
  22. 'o._f = a[i]; r = o._f(e||window.event) != false && r; o._f = null;' +
  23. '} return r');
  24. if (t != 'unload') addEvent(window, 'unload', function() {
  25. removeEvent(rO, rT, rF, rL);
  26. });
  27. }
  28. if (!f._i) f._i = addEvent._i++;
  29. o._evts[t][f._i] = f;
  30. };
  31. addEvent._i = 1;
  32. var removeEvent = function(o, t, f, l)
  33. {
  34. var d = 'removeEventListener';
  35. if (o[d] && !l) return o[d](t, f, false);
  36. if (o._evts && o._evts[t] && f._i) delete o._evts[t][f._i];
  37. };
  38. }
  39. function cancelEvent(e, c)
  40. {
  41. e.returnValue = false;
  42. if (e.preventDefault) e.preventDefault();
  43. if (c)
  44. {
  45. e.cancelBubble = true;
  46. if (e.stopPropagation) e.stopPropagation();
  47. }
  48. };
  49. // *** DRAG/RESIZE CODE ***
  50. function DragResize(myName, config)
  51. {
  52. var props = {
  53. myName: myName, // Name of the object.
  54. enabled: true, // Global toggle of drag/resize.
  55. handles: ['tl', 'tm', 'tr',
  56. 'ml', 'mr', 'bl', 'bm', 'br'], // Array of drag handles: top/mid/bot/right.
  57. isElement: null, // Function ref to test for an element.
  58. isHandle: null, // Function ref to test for move handle.
  59. element: null, // The currently selected element.
  60. handle: null, // Active handle reference of the element.
  61. minWidth: 10, minHeight: 10, // Minimum pixel size of elements.
  62. minLeft: 0, maxLeft: 9999, // Bounding box area, in pixels.
  63. minTop: 0, maxTop: 9999,
  64. zIndex: 1, // The highest Z-Index yet allocated.
  65. mouseX: 0, mouseY: 0, // Current mouse position, recorded live.
  66. lastMouseX: 0, lastMouseY: 0, // Last processed mouse positions.
  67. mOffX: 0, mOffY: 0, // A known offset between position & mouse.
  68. elmX: 0, elmY: 0, // Element position.
  69. elmW: 0, elmH: 0, // Element size.
  70. allowBlur: true, // Whether to allow automatic blur onclick.
  71. ondragfocus: null, // Event handler functions.
  72. ondragstart: null,
  73. ondragmove: null,
  74. ondragend: null,
  75. ondragblur: null
  76. };
  77. for (var p in props)
  78. this[p] = (typeof config[p] == 'undefined') ? props[p] : config[p];
  79. };
  80. DragResize.prototype.apply = function(node)
  81. {
  82. // Adds object event handlers to the specified DOM node.
  83. var obj = this;
  84. addEvent(node, 'mousedown', function(e) { obj.mouseDown(e) } );
  85. addEvent(node, 'mousemove', function(e) { obj.mouseMove(e) } );
  86. addEvent(node, 'mouseup', function(e) { obj.mouseUp(e) } );
  87. };
  88. DragResize.prototype.select = function(newElement) { with (this)
  89. {
  90. // Selects an element for dragging.
  91. if (!document.getElementById || !enabled) return;
  92. // Activate and record our new dragging element.
  93. if (newElement && (newElement != element) && enabled)
  94. {
  95. element = newElement;
  96. // Elevate it and give it resize handles.
  97. element.style.zIndex = ++zIndex;
  98. if (this.resizeHandleSet) this.resizeHandleSet(element, true);
  99. // Record element attributes for mouseMove().
  100. elmX = parseInt(element.style.left);
  101. elmY = parseInt(element.style.top);
  102. elmW = element.offsetWidth;
  103. elmH = element.offsetHeight;
  104. if (ondragfocus) this.ondragfocus();
  105. }
  106. }};
  107. DragResize.prototype.deselect = function(delHandles) { with (this)
  108. {
  109. // Immediately stops dragging an element. If 'delHandles' is true, this
  110. // remove the handles from the element and clears the element flag,
  111. // completely resetting the .
  112. if (!document.getElementById || !enabled) return;
  113. if (delHandles)
  114. {
  115. if (ondragblur) this.ondragblur();
  116. if (this.resizeHandleSet) this.resizeHandleSet(element, false);
  117. element = null;
  118. }
  119. handle = null;
  120. mOffX = 0;
  121. mOffY = 0;
  122. }};
  123. DragResize.prototype.mouseDown = function(e) { with (this)
  124. {
  125. // Suitable elements are selected for drag/resize on mousedown.
  126. // We also initialise the resize boxes, and drag parameters like mouse position etc.
  127. if (!document.getElementById || !enabled) return true;
  128. var elm = e.target || e.srcElement,
  129. newElement = null,
  130. newHandle = null,
  131. hRE = new RegExp(myName + '-([trmbl]{2})', '');
  132. while (elm)
  133. {
  134. // Loop up the DOM looking for matching elements. Remember one if found.
  135. if (elm.className)
  136. {
  137. if (!newHandle && (hRE.test(elm.className) || isHandle(elm))) newHandle = elm;
  138. if (isElement(elm)) { newElement = elm; break }
  139. }
  140. elm = elm.parentNode;
  141. }
  142. // If this isn't on the last dragged element, call deselect(),
  143. // which will hide its handles and clear element.
  144. if (element && (element != newElement) && allowBlur) deselect(true);
  145. // If we have a new matching element, call select().
  146. if (newElement && (!element || (newElement == element)))
  147. {
  148. // Stop mouse selections if we're dragging a handle.
  149. if (newHandle) cancelEvent(e);
  150. select(newElement, newHandle);
  151. handle = newHandle;
  152. if (handle && ondragstart) this.ondragstart(hRE.test(handle.className));
  153. }
  154. }};
  155. DragResize.prototype.mouseMove = function(e) { with (this)
  156. {
  157. // This continually offsets the dragged element by the difference between the
  158. // last recorded mouse position (mouseX/Y) and the current mouse position.
  159. if (!document.getElementById || !enabled) return true;
  160. // We always record the current mouse position.
  161. mouseX = e.pageX || e.clientX + document.documentElement.scrollLeft;
  162. mouseY = e.pageY || e.clientY + document.documentElement.scrollTop;
  163. // Record the relative mouse movement, in case we're dragging.
  164. // Add any previously stored & ignored offset to the calculations.
  165. var diffX = mouseX - lastMouseX + mOffX;
  166. var diffY = mouseY - lastMouseY + mOffY;
  167. mOffX = mOffY = 0;
  168. // Update last processed mouse positions.
  169. lastMouseX = mouseX;
  170. lastMouseY = mouseY;
  171. // That's all we do if we're not dragging anything.
  172. if (!handle) return true;
  173. // If included in the script, run the resize handle drag routine.
  174. // Let it create an object representing the drag offsets.
  175. var isResize = false;
  176. if (this.resizeHandleDrag && this.resizeHandleDrag(diffX, diffY))
  177. {
  178. isResize = true;
  179. }
  180. else
  181. {
  182. // If the resize drag handler isn't set or returns fase (to indicate the drag was
  183. // not on a resize handle), we must be dragging the whole element, so move that.
  184. // Bounds check left-right...
  185. var dX = diffX, dY = diffY;
  186. if (elmX + dX < minLeft) mOffX = (dX - (diffX = minLeft - elmX));
  187. else if (elmX + elmW + dX > maxLeft) mOffX = (dX - (diffX = maxLeft - elmX - elmW));
  188. // ...and up-down.
  189. if (elmY + dY < minTop) mOffY = (dY - (diffY = minTop - elmY));
  190. else if (elmY + elmH + dY > maxTop) mOffY = (dY - (diffY = maxTop - elmY - elmH));
  191. elmX += diffX;
  192. elmY += diffY;
  193. }
  194. // Assign new info back to the element, with minimum dimensions.
  195. with (element.style)
  196. {
  197. left = elmX + 'px';
  198. width = elmW + 'px';
  199. top = elmY + 'px';
  200. height = elmH + 'px';
  201. }
  202. // Evil, dirty, hackish Opera select-as-you-drag fix.
  203. if (window.opera && document.documentElement)
  204. {
  205. var oDF = document.getElementById('op-drag-fix');
  206. if (!oDF)
  207. {
  208. var oDF = document.createElement('input');
  209. oDF.id = 'op-drag-fix';
  210. oDF.style.display = 'none';
  211. document.body.appendChild(oDF);
  212. }
  213. oDF.focus();
  214. }
  215. if (ondragmove) this.ondragmove(isResize);
  216. // Stop a normal drag event.
  217. cancelEvent(e);
  218. }};
  219. DragResize.prototype.mouseUp = function(e) { with (this)
  220. {
  221. // On mouseup, stop dragging, but don't reset handler visibility.
  222. if (!document.getElementById || !enabled) return;
  223. var hRE = new RegExp(myName + '-([trmbl]{2})', '');
  224. if (handle && ondragend) this.ondragend(hRE.test(handle.className));
  225. deselect(false);
  226. }};
  227. /* Resize Code -- can be deleted if you're not using it. */
  228. DragResize.prototype.resizeHandleSet = function(elm, show) { with (this)
  229. {
  230. // Either creates, shows or hides the resize handles within an element.
  231. // If we're showing them, and no handles have been created, create 4 new ones.
  232. if (!elm._handle_tr)
  233. {
  234. for (var h = 0; h < handles.length; h++)
  235. {
  236. // Create 4 news divs, assign each a generic + specific class.
  237. var hDiv = document.createElement('div');
  238. hDiv.className = myName + ' ' + myName + '-' + handles[h];
  239. elm['_handle_' + handles[h]] = elm.appendChild(hDiv);
  240. }
  241. }
  242. // We now have handles. Find them all and show/hide.
  243. for (var h = 0; h < handles.length; h++)
  244. {
  245. elm['_handle_' + handles[h]].style.visibility = show ? 'inherit' : 'hidden';
  246. }
  247. }};
  248. DragResize.prototype.resizeHandleDrag = function(diffX, diffY) { with (this)
  249. {
  250. // Passed the mouse movement amounts. This function checks to see whether the
  251. // drag is from a resize handle created above; if so, it changes the stored
  252. // elm* dimensions and mOffX/Y.
  253. var hClass = handle && handle.className &&
  254. handle.className.match(new RegExp(myName + '-([tmblr]{2})')) ? RegExp.$1 : '';
  255. // If the hClass is one of the resize handles, resize one or two dimensions.
  256. // Bounds checking is the hard bit -- basically for each edge, check that the
  257. // element doesn't go under minimum size, and doesn't go beyond its boundary.
  258. var dY = diffY, dX = diffX, processed = false;
  259. if (hClass.indexOf('t') >= 0)
  260. {
  261. rs = 1;
  262. if (elmH - dY < minHeight) mOffY = (dY - (diffY = elmH - minHeight));
  263. else if (elmY + dY < minTop) mOffY = (dY - (diffY = minTop - elmY));
  264. elmY += diffY;
  265. elmH -= diffY;
  266. processed = true;
  267. }
  268. if (hClass.indexOf('b') >= 0)
  269. {
  270. rs = 1;
  271. if (elmH + dY < minHeight) mOffY = (dY - (diffY = minHeight - elmH));
  272. else if (elmY + elmH + dY > maxTop) mOffY = (dY - (diffY = maxTop - elmY - elmH));
  273. elmH += diffY;
  274. processed = true;
  275. }
  276. if (hClass.indexOf('l') >= 0)
  277. {
  278. rs = 1;
  279. if (elmW - dX < minWidth) mOffX = (dX - (diffX = elmW - minWidth));
  280. else if (elmX + dX < minLeft) mOffX = (dX - (diffX = minLeft - elmX));
  281. elmX += diffX;
  282. elmW -= diffX;
  283. processed = true;
  284. }
  285. if (hClass.indexOf('r') >= 0)
  286. {
  287. rs = 1;
  288. if (elmW + dX < minWidth) mOffX = (dX - (diffX = minWidth - elmW));
  289. else if (elmX + elmW + dX > maxLeft) mOffX = (dX - (diffX = maxLeft - elmX - elmW));
  290. elmW += diffX;
  291. processed = true;
  292. }
  293. return processed;
  294. }};