Sortable.js

Sortable — is a JavaScript library for reorderable drag-and-drop lists on modern browsers and touch devices. No jQuery. Supports Meteor, AngularJS, React, Polymer, Knockout and any CSS library, e.g. Bootstrap. http://rubaxa.github.io/Sortable/

As of 2017-12-07. See the latest version.

This script should not be not be installed directly. It is a library for other scripts to include with the meta directive // @require https://update.greatest.deepsurf.us/scripts/36108/235493/Sortablejs.js

  1. /**!
  2. * Sortable
  3. * @author RubaXa <trash@rubaxa.org>
  4. * @license MIT
  5. */
  6.  
  7. (function sortableModule(factory) {
  8. "use strict";
  9.  
  10. if (typeof define === "function" && define.amd) {
  11. define(factory);
  12. }
  13. else if (typeof module != "undefined" && typeof module.exports != "undefined") {
  14. module.exports = factory();
  15. }
  16. else {
  17. /* jshint sub:true */
  18. window["Sortable"] = factory();
  19. }
  20. })(function sortableFactory() {
  21. "use strict";
  22.  
  23. if (typeof window === "undefined" || !window.document) {
  24. return function sortableError() {
  25. throw new Error("Sortable.js requires a window with a document");
  26. };
  27. }
  28.  
  29. var dragEl,
  30. parentEl,
  31. ghostEl,
  32. cloneEl,
  33. rootEl,
  34. nextEl,
  35. lastDownEl,
  36.  
  37. scrollEl,
  38. scrollParentEl,
  39. scrollCustomFn,
  40.  
  41. lastEl,
  42. lastCSS,
  43. lastParentCSS,
  44.  
  45. oldIndex,
  46. newIndex,
  47.  
  48. activeGroup,
  49. putSortable,
  50.  
  51. autoScroll = {},
  52.  
  53. tapEvt,
  54. touchEvt,
  55.  
  56. moved,
  57.  
  58. /** @const */
  59. R_SPACE = /\s+/g,
  60. R_FLOAT = /left|right|inline/,
  61.  
  62. expando = 'Sortable' + (new Date).getTime(),
  63.  
  64. win = window,
  65. document = win.document,
  66. parseInt = win.parseInt,
  67. setTimeout = win.setTimeout,
  68.  
  69. $ = win.jQuery || win.Zepto,
  70. Polymer = win.Polymer,
  71.  
  72. captureMode = false,
  73. passiveMode = false,
  74.  
  75. supportDraggable = ('draggable' in document.createElement('div')),
  76. supportCssPointerEvents = (function (el) {
  77. // false when IE11
  78. if (!!navigator.userAgent.match(/(?:Trident.*rv[ :]?11\.|msie)/i)) {
  79. return false;
  80. }
  81. el = document.createElement('x');
  82. el.style.cssText = 'pointer-events:auto';
  83. return el.style.pointerEvents === 'auto';
  84. })(),
  85.  
  86. _silent = false,
  87.  
  88. abs = Math.abs,
  89. min = Math.min,
  90.  
  91. savedInputChecked = [],
  92. touchDragOverListeners = [],
  93.  
  94. alwaysFalse = function () { return false },
  95.  
  96. _autoScroll = _throttle(function (/**Event*/evt, /**Object*/options, /**HTMLElement*/rootEl) {
  97. // Bug: https://bugzilla.mozilla.org/show_bug.cgi?id=505521
  98. if (rootEl && options.scroll) {
  99. var _this = rootEl[expando],
  100. el,
  101. rect,
  102. sens = options.scrollSensitivity,
  103. speed = options.scrollSpeed,
  104.  
  105. x = evt.clientX,
  106. y = evt.clientY,
  107.  
  108. winWidth = window.innerWidth,
  109. winHeight = window.innerHeight,
  110.  
  111. vx,
  112. vy,
  113.  
  114. scrollOffsetX,
  115. scrollOffsetY
  116. ;
  117.  
  118. // Delect scrollEl
  119. if (scrollParentEl !== rootEl) {
  120. scrollEl = options.scroll;
  121. scrollParentEl = rootEl;
  122. scrollCustomFn = options.scrollFn;
  123.  
  124. if (scrollEl === true) {
  125. scrollEl = rootEl;
  126.  
  127. do {
  128. if ((scrollEl.offsetWidth < scrollEl.scrollWidth) ||
  129. (scrollEl.offsetHeight < scrollEl.scrollHeight)
  130. ) {
  131. break;
  132. }
  133. /* jshint boss:true */
  134. } while (scrollEl = scrollEl.parentNode);
  135. }
  136. }
  137.  
  138. if (scrollEl) {
  139. el = scrollEl;
  140. rect = scrollEl.getBoundingClientRect();
  141. vx = (abs(rect.right - x) <= sens) - (abs(rect.left - x) <= sens);
  142. vy = (abs(rect.bottom - y) <= sens) - (abs(rect.top - y) <= sens);
  143. }
  144.  
  145.  
  146. if (!(vx || vy)) {
  147. vx = (winWidth - x <= sens) - (x <= sens);
  148. vy = (winHeight - y <= sens) - (y <= sens);
  149.  
  150. /* jshint expr:true */
  151. (vx || vy) && (el = win);
  152. }
  153.  
  154.  
  155. if (autoScroll.vx !== vx || autoScroll.vy !== vy || autoScroll.el !== el) {
  156. autoScroll.el = el;
  157. autoScroll.vx = vx;
  158. autoScroll.vy = vy;
  159.  
  160. clearInterval(autoScroll.pid);
  161.  
  162. if (el) {
  163. autoScroll.pid = setInterval(function () {
  164. scrollOffsetY = vy ? vy * speed : 0;
  165. scrollOffsetX = vx ? vx * speed : 0;
  166.  
  167. if ('function' === typeof(scrollCustomFn)) {
  168. return scrollCustomFn.call(_this, scrollOffsetX, scrollOffsetY, evt);
  169. }
  170.  
  171. if (el === win) {
  172. win.scrollTo(win.pageXOffset + scrollOffsetX, win.pageYOffset + scrollOffsetY);
  173. } else {
  174. el.scrollTop += scrollOffsetY;
  175. el.scrollLeft += scrollOffsetX;
  176. }
  177. }, 24);
  178. }
  179. }
  180. }
  181. }, 30),
  182.  
  183. _prepareGroup = function (options) {
  184. function toFn(value, pull) {
  185. if (value == null || value === true) {
  186. value = group.name;
  187. if (value == null) {
  188. return alwaysFalse;
  189. }
  190. }
  191.  
  192. if (typeof value === 'function') {
  193. return value;
  194. } else {
  195. return function (to, from) {
  196. var fromGroup = from.options.group.name;
  197.  
  198. return pull
  199. ? value
  200. : value && (value.join
  201. ? value.indexOf(fromGroup) > -1
  202. : (fromGroup == value)
  203. );
  204. };
  205. }
  206. }
  207.  
  208. var group = {};
  209. var originalGroup = options.group;
  210.  
  211. if (!originalGroup || typeof originalGroup != 'object') {
  212. originalGroup = {name: originalGroup};
  213. }
  214.  
  215. group.name = originalGroup.name;
  216. group.checkPull = toFn(originalGroup.pull, true);
  217. group.checkPut = toFn(originalGroup.put);
  218. group.revertClone = originalGroup.revertClone;
  219.  
  220. options.group = group;
  221. }
  222. ;
  223.  
  224. // Detect support a passive mode
  225. try {
  226. window.addEventListener('test', null, Object.defineProperty({}, 'passive', {
  227. get: function () {
  228. // `false`, because everything starts to work incorrectly and instead of d'n'd,
  229. // begins the page has scrolled.
  230. passiveMode = false;
  231. captureMode = {
  232. capture: false,
  233. passive: passiveMode
  234. };
  235. }
  236. }));
  237. } catch (err) {}
  238.  
  239. /**
  240. * @class Sortable
  241. * @param {HTMLElement} el
  242. * @param {Object} [options]
  243. */
  244. function Sortable(el, options) {
  245. if (!(el && el.nodeType && el.nodeType === 1)) {
  246. throw 'Sortable: `el` must be HTMLElement, and not ' + {}.toString.call(el);
  247. }
  248.  
  249. this.el = el; // root element
  250. this.options = options = _extend({}, options);
  251.  
  252.  
  253. // Export instance
  254. el[expando] = this;
  255.  
  256. // Default options
  257. var defaults = {
  258. group: null,
  259. sort: true,
  260. disabled: false,
  261. store: null,
  262. handle: null,
  263. scroll: true,
  264. scrollSensitivity: 30,
  265. scrollSpeed: 10,
  266. draggable: /[uo]l/i.test(el.nodeName) ? 'li' : '>*',
  267. ghostClass: 'sortable-ghost',
  268. chosenClass: 'sortable-chosen',
  269. dragClass: 'sortable-drag',
  270. ignore: 'a, img',
  271. filter: null,
  272. preventOnFilter: true,
  273. animation: 0,
  274. setData: function (dataTransfer, dragEl) {
  275. dataTransfer.setData('Text', dragEl.textContent);
  276. },
  277. dropBubble: false,
  278. dragoverBubble: false,
  279. dataIdAttr: 'data-id',
  280. delay: 0,
  281. forceFallback: false,
  282. fallbackClass: 'sortable-fallback',
  283. fallbackOnBody: false,
  284. fallbackTolerance: 0,
  285. fallbackOffset: {x: 0, y: 0},
  286. supportPointer: Sortable.supportPointer !== false
  287. };
  288.  
  289.  
  290. // Set default options
  291. for (var name in defaults) {
  292. !(name in options) && (options[name] = defaults[name]);
  293. }
  294.  
  295. _prepareGroup(options);
  296.  
  297. // Bind all private methods
  298. for (var fn in this) {
  299. if (fn.charAt(0) === '_' && typeof this[fn] === 'function') {
  300. this[fn] = this[fn].bind(this);
  301. }
  302. }
  303.  
  304. // Setup drag mode
  305. this.nativeDraggable = options.forceFallback ? false : supportDraggable;
  306.  
  307. // Bind events
  308. _on(el, 'mousedown', this._onTapStart);
  309. _on(el, 'touchstart', this._onTapStart);
  310. options.supportPointer && _on(el, 'pointerdown', this._onTapStart);
  311.  
  312. if (this.nativeDraggable) {
  313. _on(el, 'dragover', this);
  314. _on(el, 'dragenter', this);
  315. }
  316.  
  317. touchDragOverListeners.push(this._onDragOver);
  318.  
  319. // Restore sorting
  320. options.store && this.sort(options.store.get(this));
  321. }
  322.  
  323.  
  324. Sortable.prototype = /** @lends Sortable.prototype */ {
  325. constructor: Sortable,
  326.  
  327. _onTapStart: function (/** Event|TouchEvent */evt) {
  328. var _this = this,
  329. el = this.el,
  330. options = this.options,
  331. preventOnFilter = options.preventOnFilter,
  332. type = evt.type,
  333. touch = evt.touches && evt.touches[0],
  334. target = (touch || evt).target,
  335. originalTarget = evt.target.shadowRoot && (evt.path && evt.path[0]) || target,
  336. filter = options.filter,
  337. startIndex;
  338.  
  339. _saveInputCheckedState(el);
  340.  
  341.  
  342. // Don't trigger start event when an element is been dragged, otherwise the evt.oldindex always wrong when set option.group.
  343. if (dragEl) {
  344. return;
  345. }
  346.  
  347. if (/mousedown|pointerdown/.test(type) && evt.button !== 0 || options.disabled) {
  348. return; // only left button or enabled
  349. }
  350.  
  351. // cancel dnd if original target is content editable
  352. if (originalTarget.isContentEditable) {
  353. return;
  354. }
  355.  
  356. target = _closest(target, options.draggable, el);
  357.  
  358. if (!target) {
  359. return;
  360. }
  361.  
  362. if (lastDownEl === target) {
  363. // Ignoring duplicate `down`
  364. return;
  365. }
  366.  
  367. // Get the index of the dragged element within its parent
  368. startIndex = _index(target, options.draggable);
  369.  
  370. // Check filter
  371. if (typeof filter === 'function') {
  372. if (filter.call(this, evt, target, this)) {
  373. _dispatchEvent(_this, originalTarget, 'filter', target, el, el, startIndex);
  374. preventOnFilter && evt.preventDefault();
  375. return; // cancel dnd
  376. }
  377. }
  378. else if (filter) {
  379. filter = filter.split(',').some(function (criteria) {
  380. criteria = _closest(originalTarget, criteria.trim(), el);
  381.  
  382. if (criteria) {
  383. _dispatchEvent(_this, criteria, 'filter', target, el, el, startIndex);
  384. return true;
  385. }
  386. });
  387.  
  388. if (filter) {
  389. preventOnFilter && evt.preventDefault();
  390. return; // cancel dnd
  391. }
  392. }
  393.  
  394. if (options.handle && !_closest(originalTarget, options.handle, el)) {
  395. return;
  396. }
  397.  
  398. // Prepare `dragstart`
  399. this._prepareDragStart(evt, touch, target, startIndex);
  400. },
  401.  
  402. _prepareDragStart: function (/** Event */evt, /** Touch */touch, /** HTMLElement */target, /** Number */startIndex) {
  403. var _this = this,
  404. el = _this.el,
  405. options = _this.options,
  406. ownerDocument = el.ownerDocument,
  407. dragStartFn;
  408.  
  409. if (target && !dragEl && (target.parentNode === el)) {
  410. tapEvt = evt;
  411.  
  412. rootEl = el;
  413. dragEl = target;
  414. parentEl = dragEl.parentNode;
  415. nextEl = dragEl.nextSibling;
  416. lastDownEl = target;
  417. activeGroup = options.group;
  418. oldIndex = startIndex;
  419.  
  420. this._lastX = (touch || evt).clientX;
  421. this._lastY = (touch || evt).clientY;
  422.  
  423. dragEl.style['will-change'] = 'all';
  424.  
  425. dragStartFn = function () {
  426. // Delayed drag has been triggered
  427. // we can re-enable the events: touchmove/mousemove
  428. _this._disableDelayedDrag();
  429.  
  430. // Make the element draggable
  431. dragEl.draggable = _this.nativeDraggable;
  432.  
  433. // Chosen item
  434. _toggleClass(dragEl, options.chosenClass, true);
  435.  
  436. // Bind the events: dragstart/dragend
  437. _this._triggerDragStart(evt, touch);
  438.  
  439. // Drag start event
  440. _dispatchEvent(_this, rootEl, 'choose', dragEl, rootEl, rootEl, oldIndex);
  441. };
  442.  
  443. // Disable "draggable"
  444. options.ignore.split(',').forEach(function (criteria) {
  445. _find(dragEl, criteria.trim(), _disableDraggable);
  446. });
  447.  
  448. _on(ownerDocument, 'mouseup', _this._onDrop);
  449. _on(ownerDocument, 'touchend', _this._onDrop);
  450. _on(ownerDocument, 'touchcancel', _this._onDrop);
  451. _on(ownerDocument, 'selectstart', _this);
  452. options.supportPointer && _on(ownerDocument, 'pointercancel', _this._onDrop);
  453.  
  454. if (options.delay) {
  455. // If the user moves the pointer or let go the click or touch
  456. // before the delay has been reached:
  457. // disable the delayed drag
  458. _on(ownerDocument, 'mouseup', _this._disableDelayedDrag);
  459. _on(ownerDocument, 'touchend', _this._disableDelayedDrag);
  460. _on(ownerDocument, 'touchcancel', _this._disableDelayedDrag);
  461. _on(ownerDocument, 'mousemove', _this._disableDelayedDrag);
  462. _on(ownerDocument, 'touchmove', _this._disableDelayedDrag);
  463. options.supportPointer && _on(ownerDocument, 'pointermove', _this._disableDelayedDrag);
  464.  
  465. _this._dragStartTimer = setTimeout(dragStartFn, options.delay);
  466. } else {
  467. dragStartFn();
  468. }
  469.  
  470.  
  471. }
  472. },
  473.  
  474. _disableDelayedDrag: function () {
  475. var ownerDocument = this.el.ownerDocument;
  476.  
  477. clearTimeout(this._dragStartTimer);
  478. _off(ownerDocument, 'mouseup', this._disableDelayedDrag);
  479. _off(ownerDocument, 'touchend', this._disableDelayedDrag);
  480. _off(ownerDocument, 'touchcancel', this._disableDelayedDrag);
  481. _off(ownerDocument, 'mousemove', this._disableDelayedDrag);
  482. _off(ownerDocument, 'touchmove', this._disableDelayedDrag);
  483. _off(ownerDocument, 'pointermove', this._disableDelayedDrag);
  484. },
  485.  
  486. _triggerDragStart: function (/** Event */evt, /** Touch */touch) {
  487. touch = touch || (evt.pointerType == 'touch' ? evt : null);
  488.  
  489. if (touch) {
  490. // Touch device support
  491. tapEvt = {
  492. target: dragEl,
  493. clientX: touch.clientX,
  494. clientY: touch.clientY
  495. };
  496.  
  497. this._onDragStart(tapEvt, 'touch');
  498. }
  499. else if (!this.nativeDraggable) {
  500. this._onDragStart(tapEvt, true);
  501. }
  502. else {
  503. _on(dragEl, 'dragend', this);
  504. _on(rootEl, 'dragstart', this._onDragStart);
  505. }
  506.  
  507. try {
  508. if (document.selection) {
  509. // Timeout neccessary for IE9
  510. _nextTick(function () {
  511. document.selection.empty();
  512. });
  513. } else {
  514. window.getSelection().removeAllRanges();
  515. }
  516. } catch (err) {
  517. }
  518. },
  519.  
  520. _dragStarted: function () {
  521. if (rootEl && dragEl) {
  522. var options = this.options;
  523.  
  524. // Apply effect
  525. _toggleClass(dragEl, options.ghostClass, true);
  526. _toggleClass(dragEl, options.dragClass, false);
  527.  
  528. Sortable.active = this;
  529.  
  530. // Drag start event
  531. _dispatchEvent(this, rootEl, 'start', dragEl, rootEl, rootEl, oldIndex);
  532. } else {
  533. this._nulling();
  534. }
  535. },
  536.  
  537. _emulateDragOver: function () {
  538. if (touchEvt) {
  539. if (this._lastX === touchEvt.clientX && this._lastY === touchEvt.clientY) {
  540. return;
  541. }
  542.  
  543. this._lastX = touchEvt.clientX;
  544. this._lastY = touchEvt.clientY;
  545.  
  546. if (!supportCssPointerEvents) {
  547. _css(ghostEl, 'display', 'none');
  548. }
  549.  
  550. var target = document.elementFromPoint(touchEvt.clientX, touchEvt.clientY);
  551. var parent = target;
  552. var i = touchDragOverListeners.length;
  553.  
  554. if (target && target.shadowRoot) {
  555. target = target.shadowRoot.elementFromPoint(touchEvt.clientX, touchEvt.clientY);
  556. parent = target;
  557. }
  558.  
  559. if (parent) {
  560. do {
  561. if (parent[expando]) {
  562. while (i--) {
  563. touchDragOverListeners[i]({
  564. clientX: touchEvt.clientX,
  565. clientY: touchEvt.clientY,
  566. target: target,
  567. rootEl: parent
  568. });
  569. }
  570.  
  571. break;
  572. }
  573.  
  574. target = parent; // store last element
  575. }
  576. /* jshint boss:true */
  577. while (parent = parent.parentNode);
  578. }
  579.  
  580. if (!supportCssPointerEvents) {
  581. _css(ghostEl, 'display', '');
  582. }
  583. }
  584. },
  585.  
  586.  
  587. _onTouchMove: function (/**TouchEvent*/evt) {
  588. if (tapEvt) {
  589. var options = this.options,
  590. fallbackTolerance = options.fallbackTolerance,
  591. fallbackOffset = options.fallbackOffset,
  592. touch = evt.touches ? evt.touches[0] : evt,
  593. dx = (touch.clientX - tapEvt.clientX) + fallbackOffset.x,
  594. dy = (touch.clientY - tapEvt.clientY) + fallbackOffset.y,
  595. translate3d = evt.touches ? 'translate3d(' + dx + 'px,' + dy + 'px,0)' : 'translate(' + dx + 'px,' + dy + 'px)';
  596.  
  597. // only set the status to dragging, when we are actually dragging
  598. if (!Sortable.active) {
  599. if (fallbackTolerance &&
  600. min(abs(touch.clientX - this._lastX), abs(touch.clientY - this._lastY)) < fallbackTolerance
  601. ) {
  602. return;
  603. }
  604.  
  605. this._dragStarted();
  606. }
  607.  
  608. // as well as creating the ghost element on the document body
  609. this._appendGhost();
  610.  
  611. moved = true;
  612. touchEvt = touch;
  613.  
  614. _css(ghostEl, 'webkitTransform', translate3d);
  615. _css(ghostEl, 'mozTransform', translate3d);
  616. _css(ghostEl, 'msTransform', translate3d);
  617. _css(ghostEl, 'transform', translate3d);
  618.  
  619. evt.preventDefault();
  620. }
  621. },
  622.  
  623. _appendGhost: function () {
  624. if (!ghostEl) {
  625. var rect = dragEl.getBoundingClientRect(),
  626. css = _css(dragEl),
  627. options = this.options,
  628. ghostRect;
  629.  
  630. ghostEl = dragEl.cloneNode(true);
  631.  
  632. _toggleClass(ghostEl, options.ghostClass, false);
  633. _toggleClass(ghostEl, options.fallbackClass, true);
  634. _toggleClass(ghostEl, options.dragClass, true);
  635.  
  636. _css(ghostEl, 'top', rect.top - parseInt(css.marginTop, 10));
  637. _css(ghostEl, 'left', rect.left - parseInt(css.marginLeft, 10));
  638. _css(ghostEl, 'width', rect.width);
  639. _css(ghostEl, 'height', rect.height);
  640. _css(ghostEl, 'opacity', '0.8');
  641. _css(ghostEl, 'position', 'fixed');
  642. _css(ghostEl, 'zIndex', '100000');
  643. _css(ghostEl, 'pointerEvents', 'none');
  644.  
  645. options.fallbackOnBody && document.body.appendChild(ghostEl) || rootEl.appendChild(ghostEl);
  646.  
  647. // Fixing dimensions.
  648. ghostRect = ghostEl.getBoundingClientRect();
  649. _css(ghostEl, 'width', rect.width * 2 - ghostRect.width);
  650. _css(ghostEl, 'height', rect.height * 2 - ghostRect.height);
  651. }
  652. },
  653.  
  654. _onDragStart: function (/**Event*/evt, /**boolean*/useFallback) {
  655. var _this = this;
  656. var dataTransfer = evt.dataTransfer;
  657. var options = _this.options;
  658.  
  659. _this._offUpEvents();
  660.  
  661. if (activeGroup.checkPull(_this, _this, dragEl, evt)) {
  662. cloneEl = _clone(dragEl);
  663.  
  664. cloneEl.draggable = false;
  665. cloneEl.style['will-change'] = '';
  666.  
  667. _css(cloneEl, 'display', 'none');
  668. _toggleClass(cloneEl, _this.options.chosenClass, false);
  669.  
  670. // #1143: IFrame support workaround
  671. _this._cloneId = _nextTick(function () {
  672. rootEl.insertBefore(cloneEl, dragEl);
  673. _dispatchEvent(_this, rootEl, 'clone', dragEl);
  674. });
  675. }
  676.  
  677. _toggleClass(dragEl, options.dragClass, true);
  678.  
  679. if (useFallback) {
  680. if (useFallback === 'touch') {
  681. // Bind touch events
  682. _on(document, 'touchmove', _this._onTouchMove);
  683. _on(document, 'touchend', _this._onDrop);
  684. _on(document, 'touchcancel', _this._onDrop);
  685.  
  686. if (options.supportPointer) {
  687. _on(document, 'pointermove', _this._onTouchMove);
  688. _on(document, 'pointerup', _this._onDrop);
  689. }
  690. } else {
  691. // Old brwoser
  692. _on(document, 'mousemove', _this._onTouchMove);
  693. _on(document, 'mouseup', _this._onDrop);
  694. }
  695.  
  696. _this._loopId = setInterval(_this._emulateDragOver, 50);
  697. }
  698. else {
  699. if (dataTransfer) {
  700. dataTransfer.effectAllowed = 'move';
  701. options.setData && options.setData.call(_this, dataTransfer, dragEl);
  702. }
  703.  
  704. _on(document, 'drop', _this);
  705.  
  706. // #1143: Бывает элемент с IFrame внутри блокирует `drop`,
  707. // поэтому если вызвался `mouseover`, значит надо отменять весь d'n'd.
  708. // Breaking Chrome 62+
  709. // _on(document, 'mouseover', _this);
  710.  
  711. _this._dragStartId = _nextTick(_this._dragStarted);
  712. }
  713. },
  714.  
  715. _onDragOver: function (/**Event*/evt) {
  716. var el = this.el,
  717. target,
  718. dragRect,
  719. targetRect,
  720. revert,
  721. options = this.options,
  722. group = options.group,
  723. activeSortable = Sortable.active,
  724. isOwner = (activeGroup === group),
  725. isMovingBetweenSortable = false,
  726. canSort = options.sort;
  727.  
  728. if (evt.preventDefault !== void 0) {
  729. evt.preventDefault();
  730. !options.dragoverBubble && evt.stopPropagation();
  731. }
  732.  
  733. if (dragEl.animated) {
  734. return;
  735. }
  736.  
  737. moved = true;
  738.  
  739. if (activeSortable && !options.disabled &&
  740. (isOwner
  741. ? canSort || (revert = !rootEl.contains(dragEl)) // Reverting item into the original list
  742. : (
  743. putSortable === this ||
  744. (
  745. (activeSortable.lastPullMode = activeGroup.checkPull(this, activeSortable, dragEl, evt)) &&
  746. group.checkPut(this, activeSortable, dragEl, evt)
  747. )
  748. )
  749. ) &&
  750. (evt.rootEl === void 0 || evt.rootEl === this.el) // touch fallback
  751. ) {
  752. // Smart auto-scrolling
  753. _autoScroll(evt, options, this.el);
  754.  
  755. if (_silent) {
  756. return;
  757. }
  758.  
  759. target = _closest(evt.target, options.draggable, el);
  760. dragRect = dragEl.getBoundingClientRect();
  761.  
  762. if (putSortable !== this) {
  763. putSortable = this;
  764. isMovingBetweenSortable = true;
  765. }
  766.  
  767. if (revert) {
  768. _cloneHide(activeSortable, true);
  769. parentEl = rootEl; // actualization
  770.  
  771. if (cloneEl || nextEl) {
  772. rootEl.insertBefore(dragEl, cloneEl || nextEl);
  773. }
  774. else if (!canSort) {
  775. rootEl.appendChild(dragEl);
  776. }
  777.  
  778. return;
  779. }
  780.  
  781.  
  782. if ((el.children.length === 0) || (el.children[0] === ghostEl) ||
  783. (el === evt.target) && (_ghostIsLast(el, evt))
  784. ) {
  785. //assign target only if condition is true
  786. if (el.children.length !== 0 && el.children[0] !== ghostEl && el === evt.target) {
  787. target = el.lastElementChild;
  788. }
  789.  
  790. if (target) {
  791. if (target.animated) {
  792. return;
  793. }
  794.  
  795. targetRect = target.getBoundingClientRect();
  796. }
  797.  
  798. _cloneHide(activeSortable, isOwner);
  799.  
  800. if (_onMove(rootEl, el, dragEl, dragRect, target, targetRect, evt) !== false) {
  801. if (!dragEl.contains(el)) {
  802. el.appendChild(dragEl);
  803. parentEl = el; // actualization
  804. }
  805.  
  806. this._animate(dragRect, dragEl);
  807. target && this._animate(targetRect, target);
  808. }
  809. }
  810. else if (target && !target.animated && target !== dragEl && (target.parentNode[expando] !== void 0)) {
  811. if (lastEl !== target) {
  812. lastEl = target;
  813. lastCSS = _css(target);
  814. lastParentCSS = _css(target.parentNode);
  815. }
  816.  
  817. targetRect = target.getBoundingClientRect();
  818.  
  819. var width = targetRect.right - targetRect.left,
  820. height = targetRect.bottom - targetRect.top,
  821. floating = R_FLOAT.test(lastCSS.cssFloat + lastCSS.display)
  822. || (lastParentCSS.display == 'flex' && lastParentCSS['flex-direction'].indexOf('row') === 0),
  823. isWide = (target.offsetWidth > dragEl.offsetWidth),
  824. isLong = (target.offsetHeight > dragEl.offsetHeight),
  825. halfway = (floating ? (evt.clientX - targetRect.left) / width : (evt.clientY - targetRect.top) / height) > 0.5,
  826. nextSibling = target.nextElementSibling,
  827. after = false
  828. ;
  829.  
  830. if (floating) {
  831. var elTop = dragEl.offsetTop,
  832. tgTop = target.offsetTop;
  833.  
  834. if (elTop === tgTop) {
  835. after = (target.previousElementSibling === dragEl) && !isWide || halfway && isWide;
  836. }
  837. else if (target.previousElementSibling === dragEl || dragEl.previousElementSibling === target) {
  838. after = (evt.clientY - targetRect.top) / height > 0.5;
  839. } else {
  840. after = tgTop > elTop;
  841. }
  842. } else if (!isMovingBetweenSortable) {
  843. after = (nextSibling !== dragEl) && !isLong || halfway && isLong;
  844. }
  845.  
  846. var moveVector = _onMove(rootEl, el, dragEl, dragRect, target, targetRect, evt, after);
  847.  
  848. if (moveVector !== false) {
  849. if (moveVector === 1 || moveVector === -1) {
  850. after = (moveVector === 1);
  851. }
  852.  
  853. _silent = true;
  854. setTimeout(_unsilent, 30);
  855.  
  856. _cloneHide(activeSortable, isOwner);
  857.  
  858. if (!dragEl.contains(el)) {
  859. if (after && !nextSibling) {
  860. el.appendChild(dragEl);
  861. } else {
  862. target.parentNode.insertBefore(dragEl, after ? nextSibling : target);
  863. }
  864. }
  865.  
  866. parentEl = dragEl.parentNode; // actualization
  867.  
  868. this._animate(dragRect, dragEl);
  869. this._animate(targetRect, target);
  870. }
  871. }
  872. }
  873. },
  874.  
  875. _animate: function (prevRect, target) {
  876. var ms = this.options.animation;
  877.  
  878. if (ms) {
  879. var currentRect = target.getBoundingClientRect();
  880.  
  881. if (prevRect.nodeType === 1) {
  882. prevRect = prevRect.getBoundingClientRect();
  883. }
  884.  
  885. _css(target, 'transition', 'none');
  886. _css(target, 'transform', 'translate3d('
  887. + (prevRect.left - currentRect.left) + 'px,'
  888. + (prevRect.top - currentRect.top) + 'px,0)'
  889. );
  890.  
  891. target.offsetWidth; // repaint
  892.  
  893. _css(target, 'transition', 'all ' + ms + 'ms');
  894. _css(target, 'transform', 'translate3d(0,0,0)');
  895.  
  896. clearTimeout(target.animated);
  897. target.animated = setTimeout(function () {
  898. _css(target, 'transition', '');
  899. _css(target, 'transform', '');
  900. target.animated = false;
  901. }, ms);
  902. }
  903. },
  904.  
  905. _offUpEvents: function () {
  906. var ownerDocument = this.el.ownerDocument;
  907.  
  908. _off(document, 'touchmove', this._onTouchMove);
  909. _off(document, 'pointermove', this._onTouchMove);
  910. _off(ownerDocument, 'mouseup', this._onDrop);
  911. _off(ownerDocument, 'touchend', this._onDrop);
  912. _off(ownerDocument, 'pointerup', this._onDrop);
  913. _off(ownerDocument, 'touchcancel', this._onDrop);
  914. _off(ownerDocument, 'pointercancel', this._onDrop);
  915. _off(ownerDocument, 'selectstart', this);
  916. },
  917.  
  918. _onDrop: function (/**Event*/evt) {
  919. var el = this.el,
  920. options = this.options;
  921.  
  922. clearInterval(this._loopId);
  923. clearInterval(autoScroll.pid);
  924. clearTimeout(this._dragStartTimer);
  925.  
  926. _cancelNextTick(this._cloneId);
  927. _cancelNextTick(this._dragStartId);
  928.  
  929. // Unbind events
  930. _off(document, 'mouseover', this);
  931. _off(document, 'mousemove', this._onTouchMove);
  932.  
  933. if (this.nativeDraggable) {
  934. _off(document, 'drop', this);
  935. _off(el, 'dragstart', this._onDragStart);
  936. }
  937.  
  938. this._offUpEvents();
  939.  
  940. if (evt) {
  941. if (moved) {
  942. evt.preventDefault();
  943. !options.dropBubble && evt.stopPropagation();
  944. }
  945.  
  946. ghostEl && ghostEl.parentNode && ghostEl.parentNode.removeChild(ghostEl);
  947.  
  948. if (rootEl === parentEl || Sortable.active.lastPullMode !== 'clone') {
  949. // Remove clone
  950. cloneEl && cloneEl.parentNode && cloneEl.parentNode.removeChild(cloneEl);
  951. }
  952.  
  953. if (dragEl) {
  954. if (this.nativeDraggable) {
  955. _off(dragEl, 'dragend', this);
  956. }
  957.  
  958. _disableDraggable(dragEl);
  959. dragEl.style['will-change'] = '';
  960.  
  961. // Remove class's
  962. _toggleClass(dragEl, this.options.ghostClass, false);
  963. _toggleClass(dragEl, this.options.chosenClass, false);
  964.  
  965. // Drag stop event
  966. _dispatchEvent(this, rootEl, 'unchoose', dragEl, parentEl, rootEl, oldIndex);
  967.  
  968. if (rootEl !== parentEl) {
  969. newIndex = _index(dragEl, options.draggable);
  970.  
  971. if (newIndex >= 0) {
  972. // Add event
  973. _dispatchEvent(null, parentEl, 'add', dragEl, parentEl, rootEl, oldIndex, newIndex);
  974.  
  975. // Remove event
  976. _dispatchEvent(this, rootEl, 'remove', dragEl, parentEl, rootEl, oldIndex, newIndex);
  977.  
  978. // drag from one list and drop into another
  979. _dispatchEvent(null, parentEl, 'sort', dragEl, parentEl, rootEl, oldIndex, newIndex);
  980. _dispatchEvent(this, rootEl, 'sort', dragEl, parentEl, rootEl, oldIndex, newIndex);
  981. }
  982. }
  983. else {
  984. if (dragEl.nextSibling !== nextEl) {
  985. // Get the index of the dragged element within its parent
  986. newIndex = _index(dragEl, options.draggable);
  987.  
  988. if (newIndex >= 0) {
  989. // drag & drop within the same list
  990. _dispatchEvent(this, rootEl, 'update', dragEl, parentEl, rootEl, oldIndex, newIndex);
  991. _dispatchEvent(this, rootEl, 'sort', dragEl, parentEl, rootEl, oldIndex, newIndex);
  992. }
  993. }
  994. }
  995.  
  996. if (Sortable.active) {
  997. /* jshint eqnull:true */
  998. if (newIndex == null || newIndex === -1) {
  999. newIndex = oldIndex;
  1000. }
  1001.  
  1002. _dispatchEvent(this, rootEl, 'end', dragEl, parentEl, rootEl, oldIndex, newIndex);
  1003.  
  1004. // Save sorting
  1005. this.save();
  1006. }
  1007. }
  1008.  
  1009. }
  1010.  
  1011. this._nulling();
  1012. },
  1013.  
  1014. _nulling: function() {
  1015. rootEl =
  1016. dragEl =
  1017. parentEl =
  1018. ghostEl =
  1019. nextEl =
  1020. cloneEl =
  1021. lastDownEl =
  1022.  
  1023. scrollEl =
  1024. scrollParentEl =
  1025.  
  1026. tapEvt =
  1027. touchEvt =
  1028.  
  1029. moved =
  1030. newIndex =
  1031.  
  1032. lastEl =
  1033. lastCSS =
  1034.  
  1035. putSortable =
  1036. activeGroup =
  1037. Sortable.active = null;
  1038.  
  1039. savedInputChecked.forEach(function (el) {
  1040. el.checked = true;
  1041. });
  1042. savedInputChecked.length = 0;
  1043. },
  1044.  
  1045. handleEvent: function (/**Event*/evt) {
  1046. switch (evt.type) {
  1047. case 'drop':
  1048. case 'dragend':
  1049. this._onDrop(evt);
  1050. break;
  1051.  
  1052. case 'dragover':
  1053. case 'dragenter':
  1054. if (dragEl) {
  1055. this._onDragOver(evt);
  1056. _globalDragOver(evt);
  1057. }
  1058. break;
  1059.  
  1060. case 'mouseover':
  1061. this._onDrop(evt);
  1062. break;
  1063.  
  1064. case 'selectstart':
  1065. evt.preventDefault();
  1066. break;
  1067. }
  1068. },
  1069.  
  1070.  
  1071. /**
  1072. * Serializes the item into an array of string.
  1073. * @returns {String[]}
  1074. */
  1075. toArray: function () {
  1076. var order = [],
  1077. el,
  1078. children = this.el.children,
  1079. i = 0,
  1080. n = children.length,
  1081. options = this.options;
  1082.  
  1083. for (; i < n; i++) {
  1084. el = children[i];
  1085. if (_closest(el, options.draggable, this.el)) {
  1086. order.push(el.getAttribute(options.dataIdAttr) || _generateId(el));
  1087. }
  1088. }
  1089.  
  1090. return order;
  1091. },
  1092.  
  1093.  
  1094. /**
  1095. * Sorts the elements according to the array.
  1096. * @param {String[]} order order of the items
  1097. */
  1098. sort: function (order) {
  1099. var items = {}, rootEl = this.el;
  1100.  
  1101. this.toArray().forEach(function (id, i) {
  1102. var el = rootEl.children[i];
  1103.  
  1104. if (_closest(el, this.options.draggable, rootEl)) {
  1105. items[id] = el;
  1106. }
  1107. }, this);
  1108.  
  1109. order.forEach(function (id) {
  1110. if (items[id]) {
  1111. rootEl.removeChild(items[id]);
  1112. rootEl.appendChild(items[id]);
  1113. }
  1114. });
  1115. },
  1116.  
  1117.  
  1118. /**
  1119. * Save the current sorting
  1120. */
  1121. save: function () {
  1122. var store = this.options.store;
  1123. store && store.set(this);
  1124. },
  1125.  
  1126.  
  1127. /**
  1128. * For each element in the set, get the first element that matches the selector by testing the element itself and traversing up through its ancestors in the DOM tree.
  1129. * @param {HTMLElement} el
  1130. * @param {String} [selector] default: `options.draggable`
  1131. * @returns {HTMLElement|null}
  1132. */
  1133. closest: function (el, selector) {
  1134. return _closest(el, selector || this.options.draggable, this.el);
  1135. },
  1136.  
  1137.  
  1138. /**
  1139. * Set/get option
  1140. * @param {string} name
  1141. * @param {*} [value]
  1142. * @returns {*}
  1143. */
  1144. option: function (name, value) {
  1145. var options = this.options;
  1146.  
  1147. if (value === void 0) {
  1148. return options[name];
  1149. } else {
  1150. options[name] = value;
  1151.  
  1152. if (name === 'group') {
  1153. _prepareGroup(options);
  1154. }
  1155. }
  1156. },
  1157.  
  1158.  
  1159. /**
  1160. * Destroy
  1161. */
  1162. destroy: function () {
  1163. var el = this.el;
  1164.  
  1165. el[expando] = null;
  1166.  
  1167. _off(el, 'mousedown', this._onTapStart);
  1168. _off(el, 'touchstart', this._onTapStart);
  1169. _off(el, 'pointerdown', this._onTapStart);
  1170.  
  1171. if (this.nativeDraggable) {
  1172. _off(el, 'dragover', this);
  1173. _off(el, 'dragenter', this);
  1174. }
  1175.  
  1176. // Remove draggable attributes
  1177. Array.prototype.forEach.call(el.querySelectorAll('[draggable]'), function (el) {
  1178. el.removeAttribute('draggable');
  1179. });
  1180.  
  1181. touchDragOverListeners.splice(touchDragOverListeners.indexOf(this._onDragOver), 1);
  1182.  
  1183. this._onDrop();
  1184.  
  1185. this.el = el = null;
  1186. }
  1187. };
  1188.  
  1189.  
  1190. function _cloneHide(sortable, state) {
  1191. if (sortable.lastPullMode !== 'clone') {
  1192. state = true;
  1193. }
  1194.  
  1195. if (cloneEl && (cloneEl.state !== state)) {
  1196. _css(cloneEl, 'display', state ? 'none' : '');
  1197.  
  1198. if (!state) {
  1199. if (cloneEl.state) {
  1200. if (sortable.options.group.revertClone) {
  1201. rootEl.insertBefore(cloneEl, nextEl);
  1202. sortable._animate(dragEl, cloneEl);
  1203. } else {
  1204. rootEl.insertBefore(cloneEl, dragEl);
  1205. }
  1206. }
  1207. }
  1208.  
  1209. cloneEl.state = state;
  1210. }
  1211. }
  1212.  
  1213.  
  1214. function _closest(/**HTMLElement*/el, /**String*/selector, /**HTMLElement*/ctx) {
  1215. if (el) {
  1216. ctx = ctx || document;
  1217.  
  1218. do {
  1219. if ((selector === '>*' && el.parentNode === ctx) || _matches(el, selector)) {
  1220. return el;
  1221. }
  1222. /* jshint boss:true */
  1223. } while (el = _getParentOrHost(el));
  1224. }
  1225.  
  1226. return null;
  1227. }
  1228.  
  1229.  
  1230. function _getParentOrHost(el) {
  1231. var parent = el.host;
  1232.  
  1233. return (parent && parent.nodeType) ? parent : el.parentNode;
  1234. }
  1235.  
  1236.  
  1237. function _globalDragOver(/**Event*/evt) {
  1238. if (evt.dataTransfer) {
  1239. evt.dataTransfer.dropEffect = 'move';
  1240. }
  1241. evt.preventDefault();
  1242. }
  1243.  
  1244.  
  1245. function _on(el, event, fn) {
  1246. el.addEventListener(event, fn, captureMode);
  1247. }
  1248.  
  1249.  
  1250. function _off(el, event, fn) {
  1251. el.removeEventListener(event, fn, captureMode);
  1252. }
  1253.  
  1254.  
  1255. function _toggleClass(el, name, state) {
  1256. if (el) {
  1257. if (el.classList) {
  1258. el.classList[state ? 'add' : 'remove'](name);
  1259. }
  1260. else {
  1261. var className = (' ' + el.className + ' ').replace(R_SPACE, ' ').replace(' ' + name + ' ', ' ');
  1262. el.className = (className + (state ? ' ' + name : '')).replace(R_SPACE, ' ');
  1263. }
  1264. }
  1265. }
  1266.  
  1267.  
  1268. function _css(el, prop, val) {
  1269. var style = el && el.style;
  1270.  
  1271. if (style) {
  1272. if (val === void 0) {
  1273. if (document.defaultView && document.defaultView.getComputedStyle) {
  1274. val = document.defaultView.getComputedStyle(el, '');
  1275. }
  1276. else if (el.currentStyle) {
  1277. val = el.currentStyle;
  1278. }
  1279.  
  1280. return prop === void 0 ? val : val[prop];
  1281. }
  1282. else {
  1283. if (!(prop in style)) {
  1284. prop = '-webkit-' + prop;
  1285. }
  1286.  
  1287. style[prop] = val + (typeof val === 'string' ? '' : 'px');
  1288. }
  1289. }
  1290. }
  1291.  
  1292.  
  1293. function _find(ctx, tagName, iterator) {
  1294. if (ctx) {
  1295. var list = ctx.getElementsByTagName(tagName), i = 0, n = list.length;
  1296.  
  1297. if (iterator) {
  1298. for (; i < n; i++) {
  1299. iterator(list[i], i);
  1300. }
  1301. }
  1302.  
  1303. return list;
  1304. }
  1305.  
  1306. return [];
  1307. }
  1308.  
  1309.  
  1310.  
  1311. function _dispatchEvent(sortable, rootEl, name, targetEl, toEl, fromEl, startIndex, newIndex) {
  1312. sortable = (sortable || rootEl[expando]);
  1313.  
  1314. var evt = document.createEvent('Event'),
  1315. options = sortable.options,
  1316. onName = 'on' + name.charAt(0).toUpperCase() + name.substr(1);
  1317.  
  1318. evt.initEvent(name, true, true);
  1319.  
  1320. evt.to = toEl || rootEl;
  1321. evt.from = fromEl || rootEl;
  1322. evt.item = targetEl || rootEl;
  1323. evt.clone = cloneEl;
  1324.  
  1325. evt.oldIndex = startIndex;
  1326. evt.newIndex = newIndex;
  1327.  
  1328. rootEl.dispatchEvent(evt);
  1329.  
  1330. if (options[onName]) {
  1331. options[onName].call(sortable, evt);
  1332. }
  1333. }
  1334.  
  1335.  
  1336. function _onMove(fromEl, toEl, dragEl, dragRect, targetEl, targetRect, originalEvt, willInsertAfter) {
  1337. var evt,
  1338. sortable = fromEl[expando],
  1339. onMoveFn = sortable.options.onMove,
  1340. retVal;
  1341.  
  1342. evt = document.createEvent('Event');
  1343. evt.initEvent('move', true, true);
  1344.  
  1345. evt.to = toEl;
  1346. evt.from = fromEl;
  1347. evt.dragged = dragEl;
  1348. evt.draggedRect = dragRect;
  1349. evt.related = targetEl || toEl;
  1350. evt.relatedRect = targetRect || toEl.getBoundingClientRect();
  1351. evt.willInsertAfter = willInsertAfter;
  1352.  
  1353. fromEl.dispatchEvent(evt);
  1354.  
  1355. if (onMoveFn) {
  1356. retVal = onMoveFn.call(sortable, evt, originalEvt);
  1357. }
  1358.  
  1359. return retVal;
  1360. }
  1361.  
  1362.  
  1363. function _disableDraggable(el) {
  1364. el.draggable = false;
  1365. }
  1366.  
  1367.  
  1368. function _unsilent() {
  1369. _silent = false;
  1370. }
  1371.  
  1372.  
  1373. /** @returns {HTMLElement|false} */
  1374. function _ghostIsLast(el, evt) {
  1375. var lastEl = el.lastElementChild,
  1376. rect = lastEl.getBoundingClientRect();
  1377.  
  1378. // 5 — min delta
  1379. // abs — нельзя добавлять, а то глюки при наведении сверху
  1380. return (evt.clientY - (rect.top + rect.height) > 5) ||
  1381. (evt.clientX - (rect.left + rect.width) > 5);
  1382. }
  1383.  
  1384.  
  1385. /**
  1386. * Generate id
  1387. * @param {HTMLElement} el
  1388. * @returns {String}
  1389. * @private
  1390. */
  1391. function _generateId(el) {
  1392. var str = el.tagName + el.className + el.src + el.href + el.textContent,
  1393. i = str.length,
  1394. sum = 0;
  1395.  
  1396. while (i--) {
  1397. sum += str.charCodeAt(i);
  1398. }
  1399.  
  1400. return sum.toString(36);
  1401. }
  1402.  
  1403. /**
  1404. * Returns the index of an element within its parent for a selected set of
  1405. * elements
  1406. * @param {HTMLElement} el
  1407. * @param {selector} selector
  1408. * @return {number}
  1409. */
  1410. function _index(el, selector) {
  1411. var index = 0;
  1412.  
  1413. if (!el || !el.parentNode) {
  1414. return -1;
  1415. }
  1416.  
  1417. while (el && (el = el.previousElementSibling)) {
  1418. if ((el.nodeName.toUpperCase() !== 'TEMPLATE') && (selector === '>*' || _matches(el, selector))) {
  1419. index++;
  1420. }
  1421. }
  1422.  
  1423. return index;
  1424. }
  1425.  
  1426. function _matches(/**HTMLElement*/el, /**String*/selector) {
  1427. if (el) {
  1428. selector = selector.split('.');
  1429.  
  1430. var tag = selector.shift().toUpperCase(),
  1431. re = new RegExp('\\s(' + selector.join('|') + ')(?=\\s)', 'g');
  1432.  
  1433. return (
  1434. (tag === '' || el.nodeName.toUpperCase() == tag) &&
  1435. (!selector.length || ((' ' + el.className + ' ').match(re) || []).length == selector.length)
  1436. );
  1437. }
  1438.  
  1439. return false;
  1440. }
  1441.  
  1442. function _throttle(callback, ms) {
  1443. var args, _this;
  1444.  
  1445. return function () {
  1446. if (args === void 0) {
  1447. args = arguments;
  1448. _this = this;
  1449.  
  1450. setTimeout(function () {
  1451. if (args.length === 1) {
  1452. callback.call(_this, args[0]);
  1453. } else {
  1454. callback.apply(_this, args);
  1455. }
  1456.  
  1457. args = void 0;
  1458. }, ms);
  1459. }
  1460. };
  1461. }
  1462.  
  1463. function _extend(dst, src) {
  1464. if (dst && src) {
  1465. for (var key in src) {
  1466. if (src.hasOwnProperty(key)) {
  1467. dst[key] = src[key];
  1468. }
  1469. }
  1470. }
  1471.  
  1472. return dst;
  1473. }
  1474.  
  1475. function _clone(el) {
  1476. if (Polymer && Polymer.dom) {
  1477. return Polymer.dom(el).cloneNode(true);
  1478. }
  1479. else if ($) {
  1480. return $(el).clone(true)[0];
  1481. }
  1482. else {
  1483. return el.cloneNode(true);
  1484. }
  1485. }
  1486.  
  1487. function _saveInputCheckedState(root) {
  1488. savedInputChecked.length = 0;
  1489. var inputs = root.getElementsByTagName('input');
  1490. var idx = inputs.length;
  1491.  
  1492. while (idx--) {
  1493. var el = inputs[idx];
  1494. el.checked && savedInputChecked.push(el);
  1495. }
  1496. }
  1497.  
  1498. function _nextTick(fn) {
  1499. return setTimeout(fn, 0);
  1500. }
  1501.  
  1502. function _cancelNextTick(id) {
  1503. return clearTimeout(id);
  1504. }
  1505.  
  1506. // Fixed #973:
  1507. _on(document, 'touchmove', function (evt) {
  1508. if (Sortable.active) {
  1509. evt.preventDefault();
  1510. }
  1511. });
  1512.  
  1513. // Export utils
  1514. Sortable.utils = {
  1515. on: _on,
  1516. off: _off,
  1517. css: _css,
  1518. find: _find,
  1519. is: function (el, selector) {
  1520. return !!_closest(el, selector, el);
  1521. },
  1522. extend: _extend,
  1523. throttle: _throttle,
  1524. closest: _closest,
  1525. toggleClass: _toggleClass,
  1526. clone: _clone,
  1527. index: _index,
  1528. nextTick: _nextTick,
  1529. cancelNextTick: _cancelNextTick
  1530. };
  1531.  
  1532.  
  1533. /**
  1534. * Create sortable instance
  1535. * @param {HTMLElement} el
  1536. * @param {Object} [options]
  1537. */
  1538. Sortable.create = function (el, options) {
  1539. return new Sortable(el, options);
  1540. };
  1541.  
  1542.  
  1543. // Export
  1544. Sortable.version = '1.7.0';
  1545. return Sortable;
  1546. });