PageRenderTime 48ms CodeModel.GetById 18ms RepoModel.GetById 0ms app.codeStats 0ms

/public/javascript/jquery.simulate.drag-sortable.js

https://github.com/artemave/REST-assured
JavaScript | 235 lines | 177 code | 22 blank | 36 comment | 47 complexity | 5544bb3b7a87a5fa7ca3f5242d024542 MD5 | raw file
  1. (function($) {
  2. /*
  3. * Simulate drag of a JQuery UI sortable list
  4. * Repository: https://github.com/mattheworiordan/jquery.simulate.drag-sortable.js
  5. * Author: http://mattheworiordan.com
  6. *
  7. * options are:
  8. * - move: move item up (positive) or down (negative) by Integer amount
  9. * - dropOn: move item to a new linked list, move option now represents position in the new list (zero indexed)
  10. * - handle: selector for the draggable handle element (optional)
  11. * - listItem: selector to limit which sibling items can be used for reordering
  12. * - placeHolder: if a placeholder is used during dragging, we need to consider it's height
  13. * - tolerance: (optional) number of pixels to overlap by instead of the default 50% of the element height
  14. *
  15. */
  16. $.fn.simulateDragSortable = function(options) {
  17. // build main options before element iteration
  18. var opts = $.extend({}, $.fn.simulateDragSortable.defaults, options);
  19. applyDrag = function(options) {
  20. // allow for a drag handle if item is not draggable
  21. var that = this,
  22. options = options || opts, // default to plugin opts unless options explicitly provided
  23. handle = options.handle ? $(this).find(options.handle)[0] : $(this)[0],
  24. listItem = options.listItem,
  25. placeHolder = options.placeHolder,
  26. sibling = $(this),
  27. moveCounter = Math.floor(options.move),
  28. direction = moveCounter > 0 ? 'down' : 'up',
  29. moveVerticalAmount = 0,
  30. initialVerticalPosition = 0,
  31. extraDrag = !isNaN(parseInt(options.tolerance, 10)) ? function() { return Number(options.tolerance); } : function(obj) { return ($(obj).outerHeight() / 2) + 5; },
  32. dragPastBy = 0, // represents the additional amount one drags past an element and bounce back
  33. dropOn = options.dropOn ? $(options.dropOn) : false,
  34. center = findCenter(handle),
  35. x = Math.floor(center.x),
  36. y = Math.floor(center.y),
  37. mouseUpAfter = (opts.debug ? 2500 : 10);
  38. if (dropOn) {
  39. if (dropOn.length === 0) {
  40. if (console && console.log) { console.log('simulate.drag-sortable.js ERROR: Drop on target could not be found'); console.log(options.dropOn); }
  41. return;
  42. }
  43. sibling = dropOn.find('>*:last');
  44. moveCounter = -(dropOn.find('>*').length + 1) + (moveCounter + 1); // calculate length of list after this move, use moveCounter as a positive index position in list to reverse back up
  45. if (dropOn.offset().top - $(this).offset().top < 0) {
  46. // moving to a list above this list, so move to just above top of last item (tried moving to top but JQuery UI wouldn't bite)
  47. initialVerticalPosition = sibling.offset().top - $(this).offset().top - extraDrag(this);
  48. } else {
  49. // moving to a list below this list, so move to bottom and work up (JQuery UI does not trigger new list below unless you move past top item first)
  50. initialVerticalPosition = sibling.offset().top - $(this).offset().top - $(this).height();
  51. }
  52. } else if (moveCounter === 0) {
  53. if (console && console.log) { console.log('simulate.drag-sortable.js WARNING: Drag with move set to zero has no effect'); }
  54. return;
  55. } else {
  56. while (moveCounter !== 0) {
  57. if (direction === 'down') {
  58. if (sibling.next(listItem).length) {
  59. sibling = sibling.next(listItem);
  60. moveVerticalAmount += sibling.outerHeight();
  61. }
  62. moveCounter -= 1;
  63. } else {
  64. if (sibling.prev(listItem).length) {
  65. sibling = sibling.prev(listItem);
  66. moveVerticalAmount -= sibling.outerHeight();
  67. }
  68. moveCounter += 1;
  69. }
  70. }
  71. }
  72. dispatchEvent(handle, 'mousedown', createEvent('mousedown', handle, { clientX: x, clientY: y }));
  73. // simulate drag start
  74. dispatchEvent(document, 'mousemove', createEvent('mousemove', document, { clientX: x+1, clientY: y+1 }));
  75. if (dropOn) {
  76. // jump to top or bottom of new list but do it in increments so that JQuery UI registers the drag events
  77. slideUpTo(x, y, initialVerticalPosition);
  78. // reset y position to top or bottom of list and move from there
  79. y += initialVerticalPosition;
  80. // now call regular shift/down in a list
  81. options = jQuery.extend(options, { move: moveCounter });
  82. delete options.dropOn;
  83. // add some delays to allow JQuery UI to catch up
  84. setTimeout(function() {
  85. dispatchEvent(document, 'mousemove', createEvent('mousemove', document, { clientX: x, clientY: y }));
  86. }, 5);
  87. setTimeout(function() {
  88. dispatchEvent(handle, 'mouseup', createEvent('mouseup', handle, { clientX: x, clientY: y }));
  89. setTimeout(function() {
  90. if (options.move) {
  91. applyDrag.call(that, options);
  92. }
  93. }, 5);
  94. }, mouseUpAfter);
  95. // stop execution as applyDrag has been called again
  96. return;
  97. }
  98. // Sortable is using a fixed height placeholder meaning items jump up and down as you drag variable height items into fixed height placeholder
  99. placeHolder = placeHolder && $(this).parent().find(placeHolder);
  100. if (!placeHolder && (direction === 'down')) {
  101. // need to move at least as far as this item and or the last sibling
  102. if ($(this).outerHeight() > $(sibling).outerHeight()) {
  103. moveVerticalAmount += $(this).outerHeight() - $(sibling).outerHeight();
  104. }
  105. moveVerticalAmount += extraDrag(sibling);
  106. dragPastBy += extraDrag(sibling);
  107. } else if (direction === 'up') {
  108. // move a little extra to ensure item clips into next position
  109. moveVerticalAmount -= Math.max(extraDrag(this), 5);
  110. } else if (direction === 'down') {
  111. // moving down with a place holder
  112. if (placeHolder.height() < $(this).height()) {
  113. moveVerticalAmount += Math.max(placeHolder.height(), 5);
  114. } else {
  115. moveVerticalAmount += extraDrag(sibling);
  116. }
  117. }
  118. if (sibling[0] !== $(this)[0]) {
  119. // step through so that the UI controller can determine when to show the placeHolder
  120. slideUpTo(x, y, moveVerticalAmount, dragPastBy);
  121. } else {
  122. if (window.console) {
  123. console.log('simulate.drag-sortable.js WARNING: Could not move as at top or bottom already');
  124. }
  125. }
  126. setTimeout(function() {
  127. dispatchEvent(document, 'mousemove', createEvent('mousemove', document, { clientX: x, clientY: y + moveVerticalAmount }));
  128. }, 5);
  129. setTimeout(function() {
  130. dispatchEvent(handle, 'mouseup', createEvent('mouseup', handle, { clientX: x, clientY: y + moveVerticalAmount }));
  131. }, mouseUpAfter);
  132. };
  133. // iterate and move each matched element
  134. return this.each(applyDrag);
  135. };
  136. // fire mouse events, go half way, then the next half, so small mouse movements near target and big at the start
  137. function slideUpTo(x, y, targetOffset, goPastBy) {
  138. var moveBy, offset;
  139. if (!goPastBy) { goPastBy = 0; }
  140. if ((targetOffset < 0) && (goPastBy > 0)) { goPastBy = -goPastBy; } // ensure go past is in the direction as often passed in from object height so always positive
  141. // go forwards including goPastBy
  142. for (offset = 0; Math.abs(offset) + 1 < Math.abs(targetOffset + goPastBy); offset += ((targetOffset + goPastBy - offset)/2) ) {
  143. dispatchEvent(document, 'mousemove', createEvent('mousemove', document, { clientX: x, clientY: y + Math.ceil(offset) }));
  144. }
  145. offset = targetOffset + goPastBy;
  146. dispatchEvent(document, 'mousemove', createEvent('mousemove', document, { clientX: x, clientY: y + offset }));
  147. // now bounce back
  148. for (; Math.abs(offset) - 1 >= Math.abs(targetOffset); offset += ((targetOffset - offset)/2) ) {
  149. dispatchEvent(document, 'mousemove', createEvent('mousemove', document, { clientX: x, clientY: y + Math.ceil(offset) }));
  150. }
  151. dispatchEvent(document, 'mousemove', createEvent('mousemove', document, { clientX: x, clientY: y + targetOffset }));
  152. }
  153. function createEvent(type, target, options) {
  154. var evt;
  155. var e = $.extend({
  156. target: target,
  157. preventDefault: function() { },
  158. stopImmediatePropagation: function() { },
  159. stopPropagation: function() { },
  160. isPropagationStopped: function() { return true; },
  161. isImmediatePropagationStopped: function() { return true; },
  162. isDefaultPrevented: function() { return true; },
  163. bubbles: true,
  164. cancelable: (type != "mousemove"),
  165. view: window,
  166. detail: 0,
  167. screenX: 0,
  168. screenY: 0,
  169. clientX: 0,
  170. clientY: 0,
  171. ctrlKey: false,
  172. altKey: false,
  173. shiftKey: false,
  174. metaKey: false,
  175. button: 0,
  176. relatedTarget: undefined
  177. }, options || {});
  178. if ($.isFunction(document.createEvent)) {
  179. evt = document.createEvent("MouseEvents");
  180. evt.initMouseEvent(type, e.bubbles, e.cancelable, e.view, e.detail,
  181. e.screenX, e.screenY, e.clientX, e.clientY,
  182. e.ctrlKey, e.altKey, e.shiftKey, e.metaKey,
  183. e.button, e.relatedTarget || document.body.parentNode);
  184. } else if (document.createEventObject) {
  185. evt = document.createEventObject();
  186. $.extend(evt, e);
  187. evt.button = { 0:1, 1:4, 2:2 }[evt.button] || evt.button;
  188. }
  189. return evt;
  190. }
  191. function dispatchEvent(el, type, evt) {
  192. if (el.dispatchEvent) {
  193. el.dispatchEvent(evt);
  194. } else if (el.fireEvent) {
  195. el.fireEvent('on' + type, evt);
  196. }
  197. return evt;
  198. }
  199. function findCenter(el) {
  200. var elm = $(el),
  201. o = elm.offset();
  202. return {
  203. x: o.left + elm.outerWidth() / 2,
  204. y: o.top + elm.outerHeight() / 2
  205. };
  206. }
  207. //
  208. // plugin defaults
  209. //
  210. $.fn.simulateDragSortable.defaults = {
  211. move: 0
  212. };
  213. })(jQuery);