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