作者 郭文星

123

正在显示 49 个修改的文件 包含 3621 行增加18 行删除

要显示太多修改。

为保证性能只显示 49 of 49+ 个文件。

1 -/nbproject/  
2 -/thinkphp/  
3 -/vendor/  
4 -/runtime/*  
5 -/addons/*  
6 -/public/assets/libs/  
7 -/public/assets/addons/*  
8 -/public/uploads/*  
9 -.idea  
10 -composer.lock  
11 -*.log  
12 -*.css.map  
13 -!.gitkeep  
14 -.env  
15 -.svn  
16 -.vscode  
17 -node_modules  
18 -.user.ini  
  1 +deny from all
  1 +{
  2 + "name": "Sortable",
  3 + "main": [
  4 + "Sortable.js"
  5 + ],
  6 + "homepage": "http://SortableJS.github.io/Sortable/",
  7 + "authors": [
  8 + "RubaXa <ibnRubaXa@gmail.com>",
  9 + "owenm <owen23355@gmail.com>"
  10 + ],
  11 + "description": "JavaScript library for reorderable drag-and-drop lists on modern browsers and touch devices. No jQuery required. Supports Meteor, AngularJS, React, Polymer, Vue, Knockout and any CSS library, e.g. Bootstrap.",
  12 + "keywords": [
  13 + "sortable",
  14 + "reorder",
  15 + "list",
  16 + "html5",
  17 + "drag",
  18 + "and",
  19 + "drop",
  20 + "dnd",
  21 + "web-components"
  22 + ],
  23 + "license": "MIT",
  24 + "ignore": [
  25 + "node_modules",
  26 + "bower_components",
  27 + "test",
  28 + "tests"
  29 + ],
  30 + "version": "1.10.2",
  31 + "_release": "1.10.2",
  32 + "_resolution": {
  33 + "type": "version",
  34 + "tag": "1.10.2",
  35 + "commit": "2addddd67387b6e4b6b5e51806eb698f0a3eee88"
  36 + },
  37 + "_source": "https://github.com/RubaXa/Sortable.git",
  38 + "_target": "~1.10.0",
  39 + "_originalSource": "Sortable"
  40 +}
  1 +version: 2.0
  2 +jobs:
  3 + build:
  4 + docker:
  5 + - image: circleci/node:10.16-browsers
  6 + steps:
  7 + - checkout
  8 +
  9 + - restore_cache:
  10 + keys:
  11 + - v1-dependencies-{{ checksum "package.json" }}
  12 + - v1-dependencies-
  13 +
  14 + - run: npm install
  15 +
  16 + - save_cache:
  17 + paths:
  18 + - node_modules
  19 + key: v1-dependencies-{{ checksum "package.json" }}
  20 +
  21 + - run: npm run build:umd
  22 +
  23 + - run:
  24 + name: Compatibility Test
  25 + command: |
  26 + if [ -z "$CIRCLE_PR_NUMBER" ];
  27 + then
  28 + npm run test:compat
  29 + fi
  30 + - run: npm run test
  31 +
  32 + - store_test_results:
  33 + path: /tmp/test-results
  1 +# editorconfig.org
  2 +root = true
  3 +
  4 +[*]
  5 +indent_style = tab
  6 +end_of_line = lf
  7 +charset = utf-8
  8 +trim_trailing_whitespace = true
  9 +insert_final_newline = true
  10 +
  11 +[*.md]
  12 +trim_trailing_whitespace = false
  13 +
  14 +[*.yml]
  15 +indent_style = space
  1 +node_modules
  2 +mock.png
  3 +.*.sw*
  4 +.build*
  5 +jquery.fn.*
  6 +.idea/
  1 +{
  2 + "strict": false,
  3 + "newcap": false,
  4 + "node": true,
  5 + "expr": true,
  6 + "supernew": true,
  7 + "laxbreak": true,
  8 + "esversion": 9,
  9 + "white": true,
  10 + "globals": {
  11 + "define": true,
  12 + "test": true,
  13 + "expect": true,
  14 + "module": true,
  15 + "asyncTest": true,
  16 + "start": true,
  17 + "ok": true,
  18 + "equal": true,
  19 + "notEqual": true,
  20 + "deepEqual": true,
  21 + "window": true,
  22 + "document": true,
  23 + "performance": true
  24 + }
  25 +}
  1 +{
  2 + "speed": 0.4,
  3 + "reporter": {
  4 + "name": "xunit",
  5 + "output": "/tmp/test-results/res.xml"
  6 + }
  7 +}
  1 +# Contribution Guidelines
  2 +
  3 +### Issue
  4 +
  5 + 1. Try [master](https://github.com/SortableJS/Sortable/tree/master/)-branch, perhaps the problem has been solved;
  6 + 2. [Use the search](https://github.com/SortableJS/Sortable/search?type=Issues&q=problem), maybe already have an answer;
  7 + 3. If not found, create example on [jsbin.com (draft)](https://jsbin.com/kamiwez/edit?html,js,output) and describe the problem.
  8 +
  9 +---
  10 +
  11 +### Pull Request
  12 +
  13 + 1. Only request to merge with the [master](https://github.com/SortableJS/Sortable/tree/master/)-branch.
  14 + 2. Only modify source files, **do not commit the resulting build**
  15 +
  16 +### Setup
  17 +
  18 + 1. Fork the repo on [github](https://github.com)
  19 + 2. Clone locally
  20 + 3. Run `npm i` in the local repo
  21 +
  22 +### Building
  23 +
  24 + - For development, build the `./Sortable.js` file using the command `npm run build:umd:watch`
  25 + - To build everything and minify it, run `npm run build`
  26 + - Do not commit the resulting builds in any pull request – they will be generated at release
  1 +#### Problem:
  2 +
  3 +
  4 +
  5 +#### JSBin/JSFiddle demonstrating the problem:
  6 +
  7 +
  8 +---
  9 +Before you create an issue, check it:
  10 +
  11 + 1. Try [master](https://github.com/SortableJS/Sortable/tree/master/)-branch, perhaps the problem has been solved;
  12 + 2. [Use the search](https://github.com/SortableJS/Sortable/search?q=problem), maybe we already have an answer;
  13 + 3. If not found, create an example on [jsbin.com (draft)](http://jsbin.com/vojixek/edit?html,js,output) and describe the problem.
  14 +
  15 +Bindings:
  16 + - Angular
  17 + - 2.0+: https://github.com/SortableJS/angular-sortablejs/issues
  18 + - legacy: https://github.com/SortableJS/angular-legacy-sortablejs/issues
  19 + - React
  20 + - ES2015+: https://github.com/SortableJS/react-sortablejs/issues
  21 + - mixin: https://github.com/SortableJS/react-mixin-sortablejs/issues
  22 + - Polymer: https://github.com/SortableJS/polymer-sortablejs/issues
  23 + - Knockout: https://github.com/SortableJS/knockout-sortablejs/issues
  24 + - Meteor: https://github.com/SortableJS/meteor-sortablejs/issues
  1 +MIT License
  2 +
  3 +Copyright (c) 2019 All contributors to Sortable
  4 +
  5 +Permission is hereby granted, free of charge, to any person obtaining a copy
  6 +of this software and associated documentation files (the "Software"), to deal
  7 +in the Software without restriction, including without limitation the rights
  8 +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  9 +copies of the Software, and to permit persons to whom the Software is
  10 +furnished to do so, subject to the following conditions:
  11 +
  12 +The above copyright notice and this permission notice shall be included in all
  13 +copies or substantial portions of the Software.
  14 +
  15 +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  16 +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  17 +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  18 +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  19 +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  20 +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
  21 +SOFTWARE.
  1 +# Sortable &nbsp; [![Financial Contributors on Open Collective](https://opencollective.com/Sortable/all/badge.svg?label=financial+contributors)](https://opencollective.com/Sortable) [![CircleCI](https://circleci.com/gh/SortableJS/Sortable.svg?style=svg)](https://circleci.com/gh/SortableJS/Sortable) [![DeepScan grade](https://deepscan.io/api/teams/3901/projects/5666/branches/43977/badge/grade.svg)](https://deepscan.io/dashboard#view=project&tid=3901&pid=5666&bid=43977) [![](https://data.jsdelivr.com/v1/package/npm/sortablejs/badge)](https://www.jsdelivr.com/package/npm/sortablejs) [![npm](https://img.shields.io/npm/v/sortablejs.svg)](https://www.npmjs.com/package/sortablejs)
  2 +
  3 +Sortable is a JavaScript library for reorderable drag-and-drop lists.
  4 +
  5 +Demo: http://sortablejs.github.io/Sortable/
  6 +
  7 +[<img width="250px" src="https://raw.githubusercontent.com/SortableJS/Sortable/HEAD/st/saucelabs.svg?sanitize=true">](https://saucelabs.com/)
  8 +
  9 +## Features
  10 +
  11 + * Supports touch devices and [modern](http://caniuse.com/#search=drag) browsers (including IE9)
  12 + * Can drag from one list to another or within the same list
  13 + * CSS animation when moving items
  14 + * Supports drag handles *and selectable text* (better than voidberg's html5sortable)
  15 + * Smart auto-scrolling
  16 + * Advanced swap detection
  17 + * Smooth animations
  18 + * [Multi-drag](https://github.com/SortableJS/Sortable/tree/master/plugins/MultiDrag) support
  19 + * Support for CSS transforms
  20 + * Built using native HTML5 drag and drop API
  21 + * Supports
  22 + * [Meteor](https://github.com/SortableJS/meteor-sortablejs)
  23 + * Angular
  24 + * [2.0+](https://github.com/SortableJS/angular-sortablejs)
  25 + * [1.&ast;](https://github.com/SortableJS/angular-legacy-sortablejs)
  26 + * React
  27 + * [ES2015+](https://github.com/SortableJS/react-sortablejs)
  28 + * [Mixin](https://github.com/SortableJS/react-mixin-sortablejs)
  29 + * [Knockout](https://github.com/SortableJS/knockout-sortablejs)
  30 + * [Polymer](https://github.com/SortableJS/polymer-sortablejs)
  31 + * [Vue](https://github.com/SortableJS/Vue.Draggable)
  32 + * [Ember](https://github.com/SortableJS/ember-sortablejs)
  33 + * Supports any CSS library, e.g. [Bootstrap](#bs)
  34 + * Simple API
  35 + * Support for [plugins](#plugins)
  36 + * [CDN](#cdn)
  37 + * No jQuery required (but there is [support](https://github.com/SortableJS/jquery-sortablejs))
  38 + * Typescript definitions at `@types/sortablejs`
  39 +
  40 +
  41 +<br/>
  42 +
  43 +
  44 +### Articles
  45 +
  46 + * [Dragging Multiple Items in Sortable](https://github.com/SortableJS/Sortable/wiki/Dragging-Multiple-Items-in-Sortable) (April 26, 2019)
  47 + * [Swap Thresholds and Direction](https://github.com/SortableJS/Sortable/wiki/Swap-Thresholds-and-Direction) (December 2, 2018)
  48 + * [Sortable v1.0 — New capabilities](https://github.com/SortableJS/Sortable/wiki/Sortable-v1.0-—-New-capabilities/) (December 22, 2014)
  49 + * [Sorting with the help of HTML5 Drag'n'Drop API](https://github.com/SortableJS/Sortable/wiki/Sorting-with-the-help-of-HTML5-Drag'n'Drop-API/) (December 23, 2013)
  50 +
  51 +<br/>
  52 +
  53 +### Getting Started
  54 +
  55 +Install with NPM:
  56 +```bash
  57 +$ npm install sortablejs --save
  58 +```
  59 +
  60 +Install with Bower:
  61 +```bash
  62 +$ bower install --save sortablejs
  63 +```
  64 +
  65 +Import into your project:
  66 +```js
  67 +// Default SortableJS
  68 +import Sortable from 'sortablejs';
  69 +
  70 +// Core SortableJS (without default plugins)
  71 +import Sortable from 'sortablejs/modular/sortable.core.esm.js';
  72 +
  73 +// Complete SortableJS (with all plugins)
  74 +import Sortable from 'sortablejs/modular/sortable.complete.esm.js';
  75 +```
  76 +
  77 +Cherrypick plugins:
  78 +```js
  79 +// Cherrypick extra plugins
  80 +import Sortable, { MultiDrag, Swap } from 'sortablejs';
  81 +
  82 +Sortable.mount(new MultiDrag(), new Swap());
  83 +
  84 +
  85 +// Cherrypick default plugins
  86 +import Sortable, { AutoScroll } from 'sortablejs/modular/sortable.core.esm.js';
  87 +
  88 +Sortable.mount(new AutoScroll());
  89 +```
  90 +
  91 +
  92 +---
  93 +
  94 +
  95 +### Usage
  96 +```html
  97 +<ul id="items">
  98 + <li>item 1</li>
  99 + <li>item 2</li>
  100 + <li>item 3</li>
  101 +</ul>
  102 +```
  103 +
  104 +```js
  105 +var el = document.getElementById('items');
  106 +var sortable = Sortable.create(el);
  107 +```
  108 +
  109 +You can use any element for the list and its elements, not just `ul`/`li`. Here is an [example with `div`s](https://jsbin.com/visimub/edit?html,js,output).
  110 +
  111 +
  112 +---
  113 +
  114 +
  115 +### Options
  116 +```js
  117 +var sortable = new Sortable(el, {
  118 + group: "name", // or { name: "...", pull: [true, false, 'clone', array], put: [true, false, array] }
  119 + sort: true, // sorting inside list
  120 + delay: 0, // time in milliseconds to define when the sorting should start
  121 + delayOnTouchOnly: false, // only delay if user is using touch
  122 + touchStartThreshold: 0, // px, how many pixels the point should move before cancelling a delayed drag event
  123 + disabled: false, // Disables the sortable if set to true.
  124 + store: null, // @see Store
  125 + animation: 150, // ms, animation speed moving items when sorting, `0` — without animation
  126 + easing: "cubic-bezier(1, 0, 0, 1)", // Easing for animation. Defaults to null. See https://easings.net/ for examples.
  127 + handle: ".my-handle", // Drag handle selector within list items
  128 + filter: ".ignore-elements", // Selectors that do not lead to dragging (String or Function)
  129 + preventOnFilter: true, // Call `event.preventDefault()` when triggered `filter`
  130 + draggable: ".item", // Specifies which items inside the element should be draggable
  131 +
  132 + dataIdAttr: 'data-id',
  133 +
  134 + ghostClass: "sortable-ghost", // Class name for the drop placeholder
  135 + chosenClass: "sortable-chosen", // Class name for the chosen item
  136 + dragClass: "sortable-drag", // Class name for the dragging item
  137 +
  138 + swapThreshold: 1, // Threshold of the swap zone
  139 + invertSwap: false, // Will always use inverted swap zone if set to true
  140 + invertedSwapThreshold: 1, // Threshold of the inverted swap zone (will be set to swapThreshold value by default)
  141 + direction: 'horizontal', // Direction of Sortable (will be detected automatically if not given)
  142 +
  143 + forceFallback: false, // ignore the HTML5 DnD behaviour and force the fallback to kick in
  144 +
  145 + fallbackClass: "sortable-fallback", // Class name for the cloned DOM Element when using forceFallback
  146 + fallbackOnBody: false, // Appends the cloned DOM Element into the Document's Body
  147 + fallbackTolerance: 0, // Specify in pixels how far the mouse should move before it's considered as a drag.
  148 +
  149 + dragoverBubble: false,
  150 + removeCloneOnHide: true, // Remove the clone element when it is not showing, rather than just hiding it
  151 + emptyInsertThreshold: 5, // px, distance mouse must be from empty sortable to insert drag element into it
  152 +
  153 +
  154 + setData: function (/** DataTransfer */dataTransfer, /** HTMLElement*/dragEl) {
  155 + dataTransfer.setData('Text', dragEl.textContent); // `dataTransfer` object of HTML5 DragEvent
  156 + },
  157 +
  158 + // Element is chosen
  159 + onChoose: function (/**Event*/evt) {
  160 + evt.oldIndex; // element index within parent
  161 + },
  162 +
  163 + // Element is unchosen
  164 + onUnchoose: function(/**Event*/evt) {
  165 + // same properties as onEnd
  166 + },
  167 +
  168 + // Element dragging started
  169 + onStart: function (/**Event*/evt) {
  170 + evt.oldIndex; // element index within parent
  171 + },
  172 +
  173 + // Element dragging ended
  174 + onEnd: function (/**Event*/evt) {
  175 + var itemEl = evt.item; // dragged HTMLElement
  176 + evt.to; // target list
  177 + evt.from; // previous list
  178 + evt.oldIndex; // element's old index within old parent
  179 + evt.newIndex; // element's new index within new parent
  180 + evt.oldDraggableIndex; // element's old index within old parent, only counting draggable elements
  181 + evt.newDraggableIndex; // element's new index within new parent, only counting draggable elements
  182 + evt.clone // the clone element
  183 + evt.pullMode; // when item is in another sortable: `"clone"` if cloning, `true` if moving
  184 + },
  185 +
  186 + // Element is dropped into the list from another list
  187 + onAdd: function (/**Event*/evt) {
  188 + // same properties as onEnd
  189 + },
  190 +
  191 + // Changed sorting within list
  192 + onUpdate: function (/**Event*/evt) {
  193 + // same properties as onEnd
  194 + },
  195 +
  196 + // Called by any change to the list (add / update / remove)
  197 + onSort: function (/**Event*/evt) {
  198 + // same properties as onEnd
  199 + },
  200 +
  201 + // Element is removed from the list into another list
  202 + onRemove: function (/**Event*/evt) {
  203 + // same properties as onEnd
  204 + },
  205 +
  206 + // Attempt to drag a filtered element
  207 + onFilter: function (/**Event*/evt) {
  208 + var itemEl = evt.item; // HTMLElement receiving the `mousedown|tapstart` event.
  209 + },
  210 +
  211 + // Event when you move an item in the list or between lists
  212 + onMove: function (/**Event*/evt, /**Event*/originalEvent) {
  213 + // Example: https://jsbin.com/nawahef/edit?js,output
  214 + evt.dragged; // dragged HTMLElement
  215 + evt.draggedRect; // DOMRect {left, top, right, bottom}
  216 + evt.related; // HTMLElement on which have guided
  217 + evt.relatedRect; // DOMRect
  218 + evt.willInsertAfter; // Boolean that is true if Sortable will insert drag element after target by default
  219 + originalEvent.clientY; // mouse position
  220 + // return false; — for cancel
  221 + // return -1; — insert before target
  222 + // return 1; — insert after target
  223 + },
  224 +
  225 + // Called when creating a clone of element
  226 + onClone: function (/**Event*/evt) {
  227 + var origEl = evt.item;
  228 + var cloneEl = evt.clone;
  229 + },
  230 +
  231 + // Called when dragging element changes position
  232 + onChange: function(/**Event*/evt) {
  233 + evt.newIndex // most likely why this event is used is to get the dragging element's current index
  234 + // same properties as onEnd
  235 + }
  236 +});
  237 +```
  238 +
  239 +
  240 +---
  241 +
  242 +
  243 +#### `group` option
  244 +To drag elements from one list into another, both lists must have the same `group` value.
  245 +You can also define whether lists can give away, give and keep a copy (`clone`), and receive elements.
  246 +
  247 + * name: `String` — group name
  248 + * pull: `true|false|["foo", "bar"]|'clone'|function` — ability to move from the list. `clone` — copy the item, rather than move. Or an array of group names which the elements may be put in. Defaults to `true`.
  249 + * put: `true|false|["baz", "qux"]|function` — whether elements can be added from other lists, or an array of group names from which elements can be added.
  250 + * revertClone: `boolean` — revert cloned element to initial position after moving to a another list.
  251 +
  252 +
  253 +Demo:
  254 + - https://jsbin.com/hijetos/edit?js,output
  255 + - https://jsbin.com/nacoyah/edit?js,output — use of complex logic in the `pull` and` put`
  256 + - https://jsbin.com/bifuyab/edit?js,output — use `revertClone: true`
  257 +
  258 +
  259 +---
  260 +
  261 +
  262 +#### `sort` option
  263 +Allow sorting inside list.
  264 +
  265 +Demo: https://jsbin.com/jayedig/edit?js,output
  266 +
  267 +
  268 +---
  269 +
  270 +
  271 +#### `delay` option
  272 +Time in milliseconds to define when the sorting should start.
  273 +Unfortunately, due to browser restrictions, delaying is not possible on IE or Edge with native drag & drop.
  274 +
  275 +Demo: https://jsbin.com/zosiwah/edit?js,output
  276 +
  277 +
  278 +---
  279 +
  280 +
  281 +#### `delayOnTouchOnly` option
  282 +Whether or not the delay should be applied only if the user is using touch (eg. on a mobile device). No delay will be applied in any other case. Defaults to `false`.
  283 +
  284 +
  285 +---
  286 +
  287 +
  288 +#### `swapThreshold` option
  289 +Percentage of the target that the swap zone will take up, as a float between `0` and `1`.
  290 +
  291 +[Read more](https://github.com/SortableJS/Sortable/wiki/Swap-Thresholds-and-Direction#swap-threshold)
  292 +
  293 +Demo: http://sortablejs.github.io/Sortable#thresholds
  294 +
  295 +
  296 +---
  297 +
  298 +
  299 +#### `invertSwap` option
  300 +Set to `true` to set the swap zone to the sides of the target, for the effect of sorting "in between" items.
  301 +
  302 +[Read more](https://github.com/SortableJS/Sortable/wiki/Swap-Thresholds-and-Direction#forcing-inverted-swap-zone)
  303 +
  304 +Demo: http://sortablejs.github.io/Sortable#thresholds
  305 +
  306 +
  307 +---
  308 +
  309 +
  310 +#### `invertedSwapThreshold` option
  311 +Percentage of the target that the inverted swap zone will take up, as a float between `0` and `1`. If not given, will default to `swapThreshold`.
  312 +
  313 +[Read more](https://github.com/SortableJS/Sortable/wiki/Swap-Thresholds-and-Direction#dealing-with-swap-glitching)
  314 +
  315 +
  316 +---
  317 +
  318 +
  319 +#### `direction` option
  320 +Direction that the Sortable should sort in. Can be set to `'vertical'`, `'horizontal'`, or a function, which will be called whenever a target is dragged over. Must return `'vertical'` or `'horizontal'`.
  321 +
  322 +[Read more](https://github.com/SortableJS/Sortable/wiki/Swap-Thresholds-and-Direction#direction)
  323 +
  324 +
  325 +Example of direction detection for vertical list that includes full column and half column elements:
  326 +
  327 +```js
  328 +Sortable.create(el, {
  329 + direction: function(evt, target, dragEl) {
  330 + if (target !== null && target.className.includes('half-column') && dragEl.className.includes('half-column')) {
  331 + return 'horizontal';
  332 + }
  333 + return 'vertical';
  334 + }
  335 +});
  336 +```
  337 +
  338 +
  339 +---
  340 +
  341 +
  342 +#### `touchStartThreshold` option
  343 +This option is similar to `fallbackTolerance` option.
  344 +
  345 +When the `delay` option is set, some phones with very sensitive touch displays like the Samsung Galaxy S8 will fire
  346 +unwanted touchmove events even when your finger is not moving, resulting in the sort not triggering.
  347 +
  348 +This option sets the minimum pointer movement that must occur before the delayed sorting is cancelled.
  349 +
  350 +Values between 3 to 5 are good.
  351 +
  352 +
  353 +---
  354 +
  355 +
  356 +#### `disabled` options
  357 +Disables the sortable if set to `true`.
  358 +
  359 +Demo: https://jsbin.com/sewokud/edit?js,output
  360 +
  361 +```js
  362 +var sortable = Sortable.create(list);
  363 +
  364 +document.getElementById("switcher").onclick = function () {
  365 + var state = sortable.option("disabled"); // get
  366 +
  367 + sortable.option("disabled", !state); // set
  368 +};
  369 +```
  370 +
  371 +
  372 +---
  373 +
  374 +
  375 +#### `handle` option
  376 +To make list items draggable, Sortable disables text selection by the user.
  377 +That's not always desirable. To allow text selection, define a drag handler,
  378 +which is an area of every list element that allows it to be dragged around.
  379 +
  380 +Demo: https://jsbin.com/numakuh/edit?html,js,output
  381 +
  382 +```js
  383 +Sortable.create(el, {
  384 + handle: ".my-handle"
  385 +});
  386 +```
  387 +
  388 +```html
  389 +<ul>
  390 + <li><span class="my-handle">::</span> list item text one
  391 + <li><span class="my-handle">::</span> list item text two
  392 +</ul>
  393 +```
  394 +
  395 +```css
  396 +.my-handle {
  397 + cursor: move;
  398 + cursor: -webkit-grabbing;
  399 +}
  400 +```
  401 +
  402 +
  403 +---
  404 +
  405 +
  406 +#### `filter` option
  407 +
  408 +
  409 +```js
  410 +Sortable.create(list, {
  411 + filter: ".js-remove, .js-edit",
  412 + onFilter: function (evt) {
  413 + var item = evt.item,
  414 + ctrl = evt.target;
  415 +
  416 + if (Sortable.utils.is(ctrl, ".js-remove")) { // Click on remove button
  417 + item.parentNode.removeChild(item); // remove sortable item
  418 + }
  419 + else if (Sortable.utils.is(ctrl, ".js-edit")) { // Click on edit link
  420 + // ...
  421 + }
  422 + }
  423 +})
  424 +```
  425 +
  426 +
  427 +---
  428 +
  429 +
  430 +#### `ghostClass` option
  431 +Class name for the drop placeholder (default `sortable-ghost`).
  432 +
  433 +Demo: https://jsbin.com/henuyiw/edit?css,js,output
  434 +
  435 +```css
  436 +.ghost {
  437 + opacity: 0.4;
  438 +}
  439 +```
  440 +
  441 +```js
  442 +Sortable.create(list, {
  443 + ghostClass: "ghost"
  444 +});
  445 +```
  446 +
  447 +
  448 +---
  449 +
  450 +
  451 +#### `chosenClass` option
  452 +Class name for the chosen item (default `sortable-chosen`).
  453 +
  454 +Demo: https://jsbin.com/hoqufox/edit?css,js,output
  455 +
  456 +```css
  457 +.chosen {
  458 + color: #fff;
  459 + background-color: #c00;
  460 +}
  461 +```
  462 +
  463 +```js
  464 +Sortable.create(list, {
  465 + delay: 500,
  466 + chosenClass: "chosen"
  467 +});
  468 +```
  469 +
  470 +
  471 +---
  472 +
  473 +
  474 +#### `forceFallback` option
  475 +If set to `true`, the Fallback for non HTML5 Browser will be used, even if we are using an HTML5 Browser.
  476 +This gives us the possibility to test the behaviour for older Browsers even in newer Browser, or make the Drag 'n Drop feel more consistent between Desktop , Mobile and old Browsers.
  477 +
  478 +On top of that, the Fallback always generates a copy of that DOM Element and appends the class `fallbackClass` defined in the options. This behaviour controls the look of this 'dragged' Element.
  479 +
  480 +Demo: https://jsbin.com/sibiput/edit?html,css,js,output
  481 +
  482 +
  483 +---
  484 +
  485 +
  486 +#### `fallbackTolerance` option
  487 +Emulates the native drag threshold. Specify in pixels how far the mouse should move before it's considered as a drag.
  488 +Useful if the items are also clickable like in a list of links.
  489 +
  490 +When the user clicks inside a sortable element, it's not uncommon for your hand to move a little between the time you press and the time you release.
  491 +Dragging only starts if you move the pointer past a certain tolerance, so that you don't accidentally start dragging every time you click.
  492 +
  493 +3 to 5 are probably good values.
  494 +
  495 +
  496 +---
  497 +
  498 +
  499 +#### `dragoverBubble` option
  500 +If set to `true`, the dragover event will bubble to parent sortables. Works on both fallback and native dragover event.
  501 +By default, it is false, but Sortable will only stop bubbling the event once the element has been inserted into a parent Sortable, or *can* be inserted into a parent Sortable, but isn't at that specific time (due to animation, etc).
  502 +
  503 +Since 1.8.0, you will probably want to leave this option as false. Before 1.8.0, it may need to be `true` for nested sortables to work.
  504 +
  505 +
  506 +---
  507 +
  508 +
  509 +#### `removeCloneOnHide` option
  510 +If set to `false`, the clone is hidden by having it's CSS `display` property set to `none`.
  511 +By default, this option is `true`, meaning Sortable will remove the cloned element from the DOM when it is supposed to be hidden.
  512 +
  513 +
  514 +---
  515 +
  516 +
  517 +#### `emptyInsertThreshold` option
  518 +The distance (in pixels) the mouse must be from an empty sortable while dragging for the drag element to be inserted into that sortable. Defaults to `5`. Set to `0` to disable this feature.
  519 +
  520 +Demo: https://jsbin.com/becavoj/edit?js,output
  521 +
  522 +
  523 +---
  524 +### Event object ([demo](https://jsbin.com/fogujiv/edit?js,output))
  525 +
  526 + - to:`HTMLElement` — list, in which moved element
  527 + - from:`HTMLElement` — previous list
  528 + - item:`HTMLElement` — dragged element
  529 + - clone:`HTMLElement`
  530 + - oldIndex:`Number|undefined` — old index within parent
  531 + - newIndex:`Number|undefined` — new index within parent
  532 + - oldDraggableIndex: `Number|undefined` — old index within parent, only counting draggable elements
  533 + - newDraggableIndex: `Number|undefined` — new index within parent, only counting draggable elements
  534 + - pullMode:`String|Boolean|undefined` — Pull mode if dragging into another sortable (`"clone"`, `true`, or `false`), otherwise undefined
  535 +
  536 +
  537 +#### `move` event object
  538 + - to:`HTMLElement`
  539 + - from:`HTMLElement`
  540 + - dragged:`HTMLElement`
  541 + - draggedRect:`DOMRect`
  542 + - related:`HTMLElement` — element on which have guided
  543 + - relatedRect:`DOMRect`
  544 + - willInsertAfter:`Boolean` — `true` if will element be inserted after target (or `false` if before)
  545 +
  546 +
  547 +---
  548 +
  549 +
  550 +### Method
  551 +
  552 +
  553 +##### option(name:`String`[, value:`*`]):`*`
  554 +Get or set the option.
  555 +
  556 +
  557 +
  558 +##### closest(el:`String`[, selector:`HTMLElement`]):`HTMLElement|null`
  559 +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.
  560 +
  561 +
  562 +##### toArray():`String[]`
  563 +Serializes the sortable's item `data-id`'s (`dataIdAttr` option) into an array of string.
  564 +
  565 +
  566 +##### sort(order:`String[]`)
  567 +Sorts the elements according to the array.
  568 +
  569 +```js
  570 +var order = sortable.toArray();
  571 +sortable.sort(order.reverse()); // apply
  572 +```
  573 +
  574 +
  575 +##### save()
  576 +Save the current sorting (see [store](#store))
  577 +
  578 +
  579 +##### destroy()
  580 +Removes the sortable functionality completely.
  581 +
  582 +
  583 +---
  584 +
  585 +
  586 +<a name="store"></a>
  587 +### Store
  588 +Saving and restoring of the sort.
  589 +
  590 +```html
  591 +<ul>
  592 + <li data-id="1">order</li>
  593 + <li data-id="2">save</li>
  594 + <li data-id="3">restore</li>
  595 +</ul>
  596 +```
  597 +
  598 +```js
  599 +Sortable.create(el, {
  600 + group: "localStorage-example",
  601 + store: {
  602 + /**
  603 + * Get the order of elements. Called once during initialization.
  604 + * @param {Sortable} sortable
  605 + * @returns {Array}
  606 + */
  607 + get: function (sortable) {
  608 + var order = localStorage.getItem(sortable.options.group.name);
  609 + return order ? order.split('|') : [];
  610 + },
  611 +
  612 + /**
  613 + * Save the order of elements. Called onEnd (when the item is dropped).
  614 + * @param {Sortable} sortable
  615 + */
  616 + set: function (sortable) {
  617 + var order = sortable.toArray();
  618 + localStorage.setItem(sortable.options.group.name, order.join('|'));
  619 + }
  620 + }
  621 +})
  622 +```
  623 +
  624 +
  625 +---
  626 +
  627 +
  628 +<a name="bs"></a>
  629 +### Bootstrap
  630 +Demo: https://jsbin.com/visimub/edit?html,js,output
  631 +
  632 +```html
  633 +<!-- Latest compiled and minified CSS -->
  634 +<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.1/css/bootstrap.min.css"/>
  635 +
  636 +
  637 +<!-- Latest Sortable -->
  638 +<script src="http://SortableJS.github.io/Sortable/Sortable.js"></script>
  639 +
  640 +
  641 +<!-- Simple List -->
  642 +<ul id="simpleList" class="list-group">
  643 + <li class="list-group-item">This is <a href="http://SortableJS.github.io/Sortable/">Sortable</a></li>
  644 + <li class="list-group-item">It works with Bootstrap...</li>
  645 + <li class="list-group-item">...out of the box.</li>
  646 + <li class="list-group-item">It has support for touch devices.</li>
  647 + <li class="list-group-item">Just drag some elements around.</li>
  648 +</ul>
  649 +
  650 +<script>
  651 + // Simple list
  652 + Sortable.create(simpleList, { /* options */ });
  653 +</script>
  654 +```
  655 +
  656 +
  657 +---
  658 +
  659 +
  660 +### Static methods & properties
  661 +
  662 +
  663 +
  664 +##### Sortable.create(el:`HTMLElement`[, options:`Object`]):`Sortable`
  665 +Create new instance.
  666 +
  667 +
  668 +---
  669 +
  670 +
  671 +##### Sortable.active:`Sortable`
  672 +The active Sortable instance.
  673 +
  674 +
  675 +---
  676 +
  677 +
  678 +##### Sortable.dragged:`HTMLElement`
  679 +The element being dragged.
  680 +
  681 +
  682 +---
  683 +
  684 +
  685 +##### Sortable.ghost:`HTMLElement`
  686 +The ghost element.
  687 +
  688 +
  689 +---
  690 +
  691 +
  692 +##### Sortable.clone:`HTMLElement`
  693 +The clone element.
  694 +
  695 +
  696 +---
  697 +
  698 +
  699 +##### Sortable.get(element:`HTMLElement`):`Sortable`
  700 +Get the Sortable instance on an element.
  701 +
  702 +
  703 +---
  704 +
  705 +
  706 +##### Sortable.mount(plugin:`...SortablePlugin|SortablePlugin[]`)
  707 +Mounts a plugin to Sortable.
  708 +
  709 +
  710 +---
  711 +
  712 +
  713 +##### Sortable.utils
  714 +* on(el`:HTMLElement`, event`:String`, fn`:Function`) — attach an event handler function
  715 +* off(el`:HTMLElement`, event`:String`, fn`:Function`) — remove an event handler
  716 +* css(el`:HTMLElement`)`:Object` — get the values of all the CSS properties
  717 +* css(el`:HTMLElement`, prop`:String`)`:Mixed` — get the value of style properties
  718 +* css(el`:HTMLElement`, prop`:String`, value`:String`) — set one CSS properties
  719 +* css(el`:HTMLElement`, props`:Object`) — set more CSS properties
  720 +* find(ctx`:HTMLElement`, tagName`:String`[, iterator`:Function`])`:Array` — get elements by tag name
  721 +* bind(ctx`:Mixed`, fn`:Function`)`:Function` — Takes a function and returns a new one that will always have a particular context
  722 +* is(el`:HTMLElement`, selector`:String`)`:Boolean` — check the current matched set of elements against a selector
  723 +* closest(el`:HTMLElement`, selector`:String`[, ctx`:HTMLElement`])`:HTMLElement|Null` — 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
  724 +* clone(el`:HTMLElement`)`:HTMLElement` — create a deep copy of the set of matched elements
  725 +* toggleClass(el`:HTMLElement`, name`:String`, state`:Boolean`) — add or remove one classes from each element
  726 +* detectDirection(el`:HTMLElement`)`:String` — automatically detect the [direction](https://github.com/SortableJS/Sortable/wiki/Swap-Thresholds-and-Direction#direction) of the element as either `'vertical'` or `'horizontal'`
  727 +
  728 +
  729 +---
  730 +
  731 +
  732 +### Plugins
  733 +#### Extra Plugins (included in complete versions)
  734 + - [MultiDrag](https://github.com/SortableJS/Sortable/tree/master/plugins/MultiDrag)
  735 + - [Swap](https://github.com/SortableJS/Sortable/tree/master/plugins/Swap)
  736 +
  737 +#### Default Plugins (included in default versions)
  738 + - [AutoScroll](https://github.com/SortableJS/Sortable/tree/master/plugins/AutoScroll)
  739 + - [OnSpill](https://github.com/SortableJS/Sortable/tree/master/plugins/OnSpill)
  740 +
  741 +
  742 +---
  743 +
  744 +
  745 +<a name="cdn"></a>
  746 +### CDN
  747 +
  748 +```html
  749 +<!-- jsDelivr :: Sortable :: Latest (https://www.jsdelivr.com/package/npm/sortablejs) -->
  750 +<script src="https://cdn.jsdelivr.net/npm/sortablejs@latest/Sortable.min.js"></script>
  751 +```
  752 +
  753 +
  754 +---
  755 +
  756 +
  757 +### Contributing (Issue/PR)
  758 +
  759 +Please, [read this](CONTRIBUTING.md).
  760 +
  761 +
  762 +---
  763 +
  764 +
  765 +## Contributors
  766 +
  767 +### Code Contributors
  768 +
  769 +This project exists thanks to all the people who contribute. [[Contribute](CONTRIBUTING.md)].
  770 +<a href="https://github.com/SortableJS/Sortable/graphs/contributors"><img src="https://opencollective.com/Sortable/contributors.svg?width=890&button=false" /></a>
  771 +
  772 +### Financial Contributors
  773 +
  774 +Become a financial contributor and help us sustain our community. [[Contribute](https://opencollective.com/Sortable/contribute)]
  775 +
  776 +#### Individuals
  777 +
  778 +<a href="https://opencollective.com/Sortable"><img src="https://opencollective.com/Sortable/individuals.svg?width=890"></a>
  779 +
  780 +#### Organizations
  781 +
  782 +Support this project with your organization. Your logo will show up here with a link to your website. [[Contribute](https://opencollective.com/Sortable/contribute)]
  783 +
  784 +<a href="https://opencollective.com/Sortable/organization/0/website"><img src="https://opencollective.com/Sortable/organization/0/avatar.svg"></a>
  785 +<a href="https://opencollective.com/Sortable/organization/1/website"><img src="https://opencollective.com/Sortable/organization/1/avatar.svg"></a>
  786 +<a href="https://opencollective.com/Sortable/organization/2/website"><img src="https://opencollective.com/Sortable/organization/2/avatar.svg"></a>
  787 +<a href="https://opencollective.com/Sortable/organization/3/website"><img src="https://opencollective.com/Sortable/organization/3/avatar.svg"></a>
  788 +<a href="https://opencollective.com/Sortable/organization/4/website"><img src="https://opencollective.com/Sortable/organization/4/avatar.svg"></a>
  789 +<a href="https://opencollective.com/Sortable/organization/5/website"><img src="https://opencollective.com/Sortable/organization/5/avatar.svg"></a>
  790 +<a href="https://opencollective.com/Sortable/organization/6/website"><img src="https://opencollective.com/Sortable/organization/6/avatar.svg"></a>
  791 +<a href="https://opencollective.com/Sortable/organization/7/website"><img src="https://opencollective.com/Sortable/organization/7/avatar.svg"></a>
  792 +<a href="https://opencollective.com/Sortable/organization/8/website"><img src="https://opencollective.com/Sortable/organization/8/avatar.svg"></a>
  793 +<a href="https://opencollective.com/Sortable/organization/9/website"><img src="https://opencollective.com/Sortable/organization/9/avatar.svg"></a>
  794 +
  795 +## MIT LICENSE
  796 +Permission is hereby granted, free of charge, to any person obtaining
  797 +a copy of this software and associated documentation files (the
  798 +"Software"), to deal in the Software without restriction, including
  799 +without limitation the rights to use, copy, modify, merge, publish,
  800 +distribute, sublicense, and/or sell copies of the Software, and to
  801 +permit persons to whom the Software is furnished to do so, subject to
  802 +the following conditions:
  803 +
  804 +The above copyright notice and this permission notice shall be
  805 +included in all copies or substantial portions of the Software.
  806 +
  807 +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
  808 +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
  809 +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
  810 +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
  811 +LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
  812 +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
  813 +WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
此 diff 太大无法显示。
  1 +/*! Sortable 1.10.2 - MIT | git://github.com/SortableJS/Sortable.git */
  2 +!function(t,e){"object"==typeof exports&&"undefined"!=typeof module?module.exports=e():"function"==typeof define&&define.amd?define(e):(t=t||self).Sortable=e()}(this,function(){"use strict";function o(t){return(o="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(t){return typeof t}:function(t){return t&&"function"==typeof Symbol&&t.constructor===Symbol&&t!==Symbol.prototype?"symbol":typeof t})(t)}function a(){return(a=Object.assign||function(t){for(var e=1;e<arguments.length;e++){var n=arguments[e];for(var o in n)Object.prototype.hasOwnProperty.call(n,o)&&(t[o]=n[o])}return t}).apply(this,arguments)}function I(i){for(var t=1;t<arguments.length;t++){var r=null!=arguments[t]?arguments[t]:{},e=Object.keys(r);"function"==typeof Object.getOwnPropertySymbols&&(e=e.concat(Object.getOwnPropertySymbols(r).filter(function(t){return Object.getOwnPropertyDescriptor(r,t).enumerable}))),e.forEach(function(t){var e,n,o;e=i,o=r[n=t],n in e?Object.defineProperty(e,n,{value:o,enumerable:!0,configurable:!0,writable:!0}):e[n]=o})}return i}function l(t,e){if(null==t)return{};var n,o,i=function(t,e){if(null==t)return{};var n,o,i={},r=Object.keys(t);for(o=0;o<r.length;o++)n=r[o],0<=e.indexOf(n)||(i[n]=t[n]);return i}(t,e);if(Object.getOwnPropertySymbols){var r=Object.getOwnPropertySymbols(t);for(o=0;o<r.length;o++)n=r[o],0<=e.indexOf(n)||Object.prototype.propertyIsEnumerable.call(t,n)&&(i[n]=t[n])}return i}function e(t){return function(t){if(Array.isArray(t)){for(var e=0,n=new Array(t.length);e<t.length;e++)n[e]=t[e];return n}}(t)||function(t){if(Symbol.iterator in Object(t)||"[object Arguments]"===Object.prototype.toString.call(t))return Array.from(t)}(t)||function(){throw new TypeError("Invalid attempt to spread non-iterable instance")}()}function t(t){if("undefined"!=typeof window&&window.navigator)return!!navigator.userAgent.match(t)}var w=t(/(?:Trident.*rv[ :]?11\.|msie|iemobile|Windows Phone)/i),E=t(/Edge/i),c=t(/firefox/i),s=t(/safari/i)&&!t(/chrome/i)&&!t(/android/i),n=t(/iP(ad|od|hone)/i),i=t(/chrome/i)&&t(/android/i),r={capture:!1,passive:!1};function u(t,e,n){t.addEventListener(e,n,!w&&r)}function d(t,e,n){t.removeEventListener(e,n,!w&&r)}function h(t,e){if(e){if(">"===e[0]&&(e=e.substring(1)),t)try{if(t.matches)return t.matches(e);if(t.msMatchesSelector)return t.msMatchesSelector(e);if(t.webkitMatchesSelector)return t.webkitMatchesSelector(e)}catch(t){return!1}return!1}}function P(t,e,n,o){if(t){n=n||document;do{if(null!=e&&(">"===e[0]?t.parentNode===n&&h(t,e):h(t,e))||o&&t===n)return t;if(t===n)break}while(t=(i=t).host&&i!==document&&i.host.nodeType?i.host:i.parentNode)}var i;return null}var f,p=/\s+/g;function k(t,e,n){if(t&&e)if(t.classList)t.classList[n?"add":"remove"](e);else{var o=(" "+t.className+" ").replace(p," ").replace(" "+e+" "," ");t.className=(o+(n?" "+e:"")).replace(p," ")}}function R(t,e,n){var o=t&&t.style;if(o){if(void 0===n)return document.defaultView&&document.defaultView.getComputedStyle?n=document.defaultView.getComputedStyle(t,""):t.currentStyle&&(n=t.currentStyle),void 0===e?n:n[e];e in o||-1!==e.indexOf("webkit")||(e="-webkit-"+e),o[e]=n+("string"==typeof n?"":"px")}}function v(t,e){var n="";if("string"==typeof t)n=t;else do{var o=R(t,"transform");o&&"none"!==o&&(n=o+" "+n)}while(!e&&(t=t.parentNode));var i=window.DOMMatrix||window.WebKitCSSMatrix||window.CSSMatrix||window.MSCSSMatrix;return i&&new i(n)}function g(t,e,n){if(t){var o=t.getElementsByTagName(e),i=0,r=o.length;if(n)for(;i<r;i++)n(o[i],i);return o}return[]}function N(){var t=document.scrollingElement;return t||document.documentElement}function X(t,e,n,o,i){if(t.getBoundingClientRect||t===window){var r,a,l,s,c,u,d;if(d=t!==window&&t!==N()?(a=(r=t.getBoundingClientRect()).top,l=r.left,s=r.bottom,c=r.right,u=r.height,r.width):(l=a=0,s=window.innerHeight,c=window.innerWidth,u=window.innerHeight,window.innerWidth),(e||n)&&t!==window&&(i=i||t.parentNode,!w))do{if(i&&i.getBoundingClientRect&&("none"!==R(i,"transform")||n&&"static"!==R(i,"position"))){var h=i.getBoundingClientRect();a-=h.top+parseInt(R(i,"border-top-width")),l-=h.left+parseInt(R(i,"border-left-width")),s=a+r.height,c=l+r.width;break}}while(i=i.parentNode);if(o&&t!==window){var f=v(i||t),p=f&&f.a,g=f&&f.d;f&&(s=(a/=g)+(u/=g),c=(l/=p)+(d/=p))}return{top:a,left:l,bottom:s,right:c,width:d,height:u}}}function Y(t,e,n){for(var o=H(t,!0),i=X(t)[e];o;){var r=X(o)[n];if(!("top"===n||"left"===n?r<=i:i<=r))return o;if(o===N())break;o=H(o,!1)}return!1}function m(t,e,n){for(var o=0,i=0,r=t.children;i<r.length;){if("none"!==r[i].style.display&&r[i]!==Rt.ghost&&r[i]!==Rt.dragged&&P(r[i],n.draggable,t,!1)){if(o===e)return r[i];o++}i++}return null}function B(t,e){for(var n=t.lastElementChild;n&&(n===Rt.ghost||"none"===R(n,"display")||e&&!h(n,e));)n=n.previousElementSibling;return n||null}function F(t,e){var n=0;if(!t||!t.parentNode)return-1;for(;t=t.previousElementSibling;)"TEMPLATE"===t.nodeName.toUpperCase()||t===Rt.clone||e&&!h(t,e)||n++;return n}function b(t){var e=0,n=0,o=N();if(t)do{var i=v(t),r=i.a,a=i.d;e+=t.scrollLeft*r,n+=t.scrollTop*a}while(t!==o&&(t=t.parentNode));return[e,n]}function H(t,e){if(!t||!t.getBoundingClientRect)return N();var n=t,o=!1;do{if(n.clientWidth<n.scrollWidth||n.clientHeight<n.scrollHeight){var i=R(n);if(n.clientWidth<n.scrollWidth&&("auto"==i.overflowX||"scroll"==i.overflowX)||n.clientHeight<n.scrollHeight&&("auto"==i.overflowY||"scroll"==i.overflowY)){if(!n.getBoundingClientRect||n===document.body)return N();if(o||e)return n;o=!0}}}while(n=n.parentNode);return N()}function y(t,e){return Math.round(t.top)===Math.round(e.top)&&Math.round(t.left)===Math.round(e.left)&&Math.round(t.height)===Math.round(e.height)&&Math.round(t.width)===Math.round(e.width)}function D(e,n){return function(){if(!f){var t=arguments;1===t.length?e.call(this,t[0]):e.apply(this,t),f=setTimeout(function(){f=void 0},n)}}}function L(t,e,n){t.scrollLeft+=e,t.scrollTop+=n}function S(t){var e=window.Polymer,n=window.jQuery||window.Zepto;return e&&e.dom?e.dom(t).cloneNode(!0):n?n(t).clone(!0)[0]:t.cloneNode(!0)}function _(t,e){R(t,"position","absolute"),R(t,"top",e.top),R(t,"left",e.left),R(t,"width",e.width),R(t,"height",e.height)}function C(t){R(t,"position",""),R(t,"top",""),R(t,"left",""),R(t,"width",""),R(t,"height","")}var j="Sortable"+(new Date).getTime();function T(){var e,o=[];return{captureAnimationState:function(){o=[],this.options.animation&&[].slice.call(this.el.children).forEach(function(t){if("none"!==R(t,"display")&&t!==Rt.ghost){o.push({target:t,rect:X(t)});var e=I({},o[o.length-1].rect);if(t.thisAnimationDuration){var n=v(t,!0);n&&(e.top-=n.f,e.left-=n.e)}t.fromRect=e}})},addAnimationState:function(t){o.push(t)},removeAnimationState:function(t){o.splice(function(t,e){for(var n in t)if(t.hasOwnProperty(n))for(var o in e)if(e.hasOwnProperty(o)&&e[o]===t[n][o])return Number(n);return-1}(o,{target:t}),1)},animateAll:function(t){var c=this;if(!this.options.animation)return clearTimeout(e),void("function"==typeof t&&t());var u=!1,d=0;o.forEach(function(t){var e=0,n=t.target,o=n.fromRect,i=X(n),r=n.prevFromRect,a=n.prevToRect,l=t.rect,s=v(n,!0);s&&(i.top-=s.f,i.left-=s.e),n.toRect=i,n.thisAnimationDuration&&y(r,i)&&!y(o,i)&&(l.top-i.top)/(l.left-i.left)==(o.top-i.top)/(o.left-i.left)&&(e=function(t,e,n,o){return Math.sqrt(Math.pow(e.top-t.top,2)+Math.pow(e.left-t.left,2))/Math.sqrt(Math.pow(e.top-n.top,2)+Math.pow(e.left-n.left,2))*o.animation}(l,r,a,c.options)),y(i,o)||(n.prevFromRect=o,n.prevToRect=i,e||(e=c.options.animation),c.animate(n,l,i,e)),e&&(u=!0,d=Math.max(d,e),clearTimeout(n.animationResetTimer),n.animationResetTimer=setTimeout(function(){n.animationTime=0,n.prevFromRect=null,n.fromRect=null,n.prevToRect=null,n.thisAnimationDuration=null},e),n.thisAnimationDuration=e)}),clearTimeout(e),u?e=setTimeout(function(){"function"==typeof t&&t()},d):"function"==typeof t&&t(),o=[]},animate:function(t,e,n,o){if(o){R(t,"transition",""),R(t,"transform","");var i=v(this.el),r=i&&i.a,a=i&&i.d,l=(e.left-n.left)/(r||1),s=(e.top-n.top)/(a||1);t.animatingX=!!l,t.animatingY=!!s,R(t,"transform","translate3d("+l+"px,"+s+"px,0)"),function(t){t.offsetWidth}(t),R(t,"transition","transform "+o+"ms"+(this.options.easing?" "+this.options.easing:"")),R(t,"transform","translate3d(0,0,0)"),"number"==typeof t.animated&&clearTimeout(t.animated),t.animated=setTimeout(function(){R(t,"transition",""),R(t,"transform",""),t.animated=!1,t.animatingX=!1,t.animatingY=!1},o)}}}}var x=[],M={initializeByDefault:!0},O={mount:function(t){for(var e in M)!M.hasOwnProperty(e)||e in t||(t[e]=M[e]);x.push(t)},pluginEvent:function(e,n,o){var t=this;this.eventCanceled=!1,o.cancel=function(){t.eventCanceled=!0};var i=e+"Global";x.forEach(function(t){n[t.pluginName]&&(n[t.pluginName][i]&&n[t.pluginName][i](I({sortable:n},o)),n.options[t.pluginName]&&n[t.pluginName][e]&&n[t.pluginName][e](I({sortable:n},o)))})},initializePlugins:function(o,i,r,t){for(var e in x.forEach(function(t){var e=t.pluginName;if(o.options[e]||t.initializeByDefault){var n=new t(o,i,o.options);n.sortable=o,n.options=o.options,o[e]=n,a(r,n.defaults)}}),o.options)if(o.options.hasOwnProperty(e)){var n=this.modifyOption(o,e,o.options[e]);void 0!==n&&(o.options[e]=n)}},getEventProperties:function(e,n){var o={};return x.forEach(function(t){"function"==typeof t.eventProperties&&a(o,t.eventProperties.call(n[t.pluginName],e))}),o},modifyOption:function(e,n,o){var i;return x.forEach(function(t){e[t.pluginName]&&t.optionListeners&&"function"==typeof t.optionListeners[n]&&(i=t.optionListeners[n].call(e[t.pluginName],o))}),i}};function A(t){var e=t.sortable,n=t.rootEl,o=t.name,i=t.targetEl,r=t.cloneEl,a=t.toEl,l=t.fromEl,s=t.oldIndex,c=t.newIndex,u=t.oldDraggableIndex,d=t.newDraggableIndex,h=t.originalEvent,f=t.putSortable,p=t.extraEventProperties;if(e=e||n&&n[j]){var g,v=e.options,m="on"+o.charAt(0).toUpperCase()+o.substr(1);!window.CustomEvent||w||E?(g=document.createEvent("Event")).initEvent(o,!0,!0):g=new CustomEvent(o,{bubbles:!0,cancelable:!0}),g.to=a||n,g.from=l||n,g.item=i||n,g.clone=r,g.oldIndex=s,g.newIndex=c,g.oldDraggableIndex=u,g.newDraggableIndex=d,g.originalEvent=h,g.pullMode=f?f.lastPutMode:void 0;var b=I({},p,O.getEventProperties(o,e));for(var y in b)g[y]=b[y];n&&n.dispatchEvent(g),v[m]&&v[m].call(e,g)}}function K(t,e,n){var o=2<arguments.length&&void 0!==n?n:{},i=o.evt,r=l(o,["evt"]);O.pluginEvent.bind(Rt)(t,e,I({dragEl:z,parentEl:G,ghostEl:U,rootEl:q,nextEl:V,lastDownEl:Z,cloneEl:Q,cloneHidden:$,dragStarted:dt,putSortable:it,activeSortable:Rt.active,originalEvent:i,oldIndex:J,oldDraggableIndex:et,newIndex:tt,newDraggableIndex:nt,hideGhostForTarget:Nt,unhideGhostForTarget:It,cloneNowHidden:function(){$=!0},cloneNowShown:function(){$=!1},dispatchSortableEvent:function(t){W({sortable:e,name:t,originalEvent:i})}},r))}function W(t){A(I({putSortable:it,cloneEl:Q,targetEl:z,rootEl:q,oldIndex:J,oldDraggableIndex:et,newIndex:tt,newDraggableIndex:nt},t))}var z,G,U,q,V,Z,Q,$,J,tt,et,nt,ot,it,rt,at,lt,st,ct,ut,dt,ht,ft,pt,gt,vt=!1,mt=!1,bt=[],yt=!1,wt=!1,Et=[],Dt=!1,St=[],_t="undefined"!=typeof document,Ct=n,Tt=E||w?"cssFloat":"float",xt=_t&&!i&&!n&&"draggable"in document.createElement("div"),Mt=function(){if(_t){if(w)return!1;var t=document.createElement("x");return t.style.cssText="pointer-events:auto","auto"===t.style.pointerEvents}}(),Ot=function(t,e){var n=R(t),o=parseInt(n.width)-parseInt(n.paddingLeft)-parseInt(n.paddingRight)-parseInt(n.borderLeftWidth)-parseInt(n.borderRightWidth),i=m(t,0,e),r=m(t,1,e),a=i&&R(i),l=r&&R(r),s=a&&parseInt(a.marginLeft)+parseInt(a.marginRight)+X(i).width,c=l&&parseInt(l.marginLeft)+parseInt(l.marginRight)+X(r).width;if("flex"===n.display)return"column"===n.flexDirection||"column-reverse"===n.flexDirection?"vertical":"horizontal";if("grid"===n.display)return n.gridTemplateColumns.split(" ").length<=1?"vertical":"horizontal";if(i&&a.float&&"none"!==a.float){var u="left"===a.float?"left":"right";return!r||"both"!==l.clear&&l.clear!==u?"horizontal":"vertical"}return i&&("block"===a.display||"flex"===a.display||"table"===a.display||"grid"===a.display||o<=s&&"none"===n[Tt]||r&&"none"===n[Tt]&&o<s+c)?"vertical":"horizontal"},At=function(t){function s(a,l){return function(t,e,n,o){var i=t.options.group.name&&e.options.group.name&&t.options.group.name===e.options.group.name;if(null==a&&(l||i))return!0;if(null==a||!1===a)return!1;if(l&&"clone"===a)return a;if("function"==typeof a)return s(a(t,e,n,o),l)(t,e,n,o);var r=(l?t:e).options.group.name;return!0===a||"string"==typeof a&&a===r||a.join&&-1<a.indexOf(r)}}var e={},n=t.group;n&&"object"==o(n)||(n={name:n}),e.name=n.name,e.checkPull=s(n.pull,!0),e.checkPut=s(n.put),e.revertClone=n.revertClone,t.group=e},Nt=function(){!Mt&&U&&R(U,"display","none")},It=function(){!Mt&&U&&R(U,"display","")};_t&&document.addEventListener("click",function(t){if(mt)return t.preventDefault(),t.stopPropagation&&t.stopPropagation(),t.stopImmediatePropagation&&t.stopImmediatePropagation(),mt=!1},!0);function Pt(t){if(z){var e=function(r,a){var l;return bt.some(function(t){if(!B(t)){var e=X(t),n=t[j].options.emptyInsertThreshold,o=r>=e.left-n&&r<=e.right+n,i=a>=e.top-n&&a<=e.bottom+n;return n&&o&&i?l=t:void 0}}),l}((t=t.touches?t.touches[0]:t).clientX,t.clientY);if(e){var n={};for(var o in t)t.hasOwnProperty(o)&&(n[o]=t[o]);n.target=n.rootEl=e,n.preventDefault=void 0,n.stopPropagation=void 0,e[j]._onDragOver(n)}}}function kt(t){z&&z.parentNode[j]._isOutsideThisEl(t.target)}function Rt(t,e){if(!t||!t.nodeType||1!==t.nodeType)throw"Sortable: `el` must be an HTMLElement, not ".concat({}.toString.call(t));this.el=t,this.options=e=a({},e),t[j]=this;var n={group:null,sort:!0,disabled:!1,store:null,handle:null,draggable:/^[uo]l$/i.test(t.nodeName)?">li":">*",swapThreshold:1,invertSwap:!1,invertedSwapThreshold:null,removeCloneOnHide:!0,direction:function(){return Ot(t,this.options)},ghostClass:"sortable-ghost",chosenClass:"sortable-chosen",dragClass:"sortable-drag",ignore:"a, img",filter:null,preventOnFilter:!0,animation:0,easing:null,setData:function(t,e){t.setData("Text",e.textContent)},dropBubble:!1,dragoverBubble:!1,dataIdAttr:"data-id",delay:0,delayOnTouchOnly:!1,touchStartThreshold:(Number.parseInt?Number:window).parseInt(window.devicePixelRatio,10)||1,forceFallback:!1,fallbackClass:"sortable-fallback",fallbackOnBody:!1,fallbackTolerance:0,fallbackOffset:{x:0,y:0},supportPointer:!1!==Rt.supportPointer&&"PointerEvent"in window,emptyInsertThreshold:5};for(var o in O.initializePlugins(this,t,n),n)o in e||(e[o]=n[o]);for(var i in At(e),this)"_"===i.charAt(0)&&"function"==typeof this[i]&&(this[i]=this[i].bind(this));this.nativeDraggable=!e.forceFallback&&xt,this.nativeDraggable&&(this.options.touchStartThreshold=1),e.supportPointer?u(t,"pointerdown",this._onTapStart):(u(t,"mousedown",this._onTapStart),u(t,"touchstart",this._onTapStart)),this.nativeDraggable&&(u(t,"dragover",this),u(t,"dragenter",this)),bt.push(this.el),e.store&&e.store.get&&this.sort(e.store.get(this)||[]),a(this,T())}function Xt(t,e,n,o,i,r,a,l){var s,c,u=t[j],d=u.options.onMove;return!window.CustomEvent||w||E?(s=document.createEvent("Event")).initEvent("move",!0,!0):s=new CustomEvent("move",{bubbles:!0,cancelable:!0}),s.to=e,s.from=t,s.dragged=n,s.draggedRect=o,s.related=i||e,s.relatedRect=r||X(e),s.willInsertAfter=l,s.originalEvent=a,t.dispatchEvent(s),d&&(c=d.call(u,s,a)),c}function Yt(t){t.draggable=!1}function Bt(){Dt=!1}function Ft(t){for(var e=t.tagName+t.className+t.src+t.href+t.textContent,n=e.length,o=0;n--;)o+=e.charCodeAt(n);return o.toString(36)}function Ht(t){return setTimeout(t,0)}function Lt(t){return clearTimeout(t)}Rt.prototype={constructor:Rt,_isOutsideThisEl:function(t){this.el.contains(t)||t===this.el||(ht=null)},_getDirection:function(t,e){return"function"==typeof this.options.direction?this.options.direction.call(this,t,e,z):this.options.direction},_onTapStart:function(e){if(e.cancelable){var n=this,o=this.el,t=this.options,i=t.preventOnFilter,r=e.type,a=e.touches&&e.touches[0]||e.pointerType&&"touch"===e.pointerType&&e,l=(a||e).target,s=e.target.shadowRoot&&(e.path&&e.path[0]||e.composedPath&&e.composedPath()[0])||l,c=t.filter;if(function(t){St.length=0;var e=t.getElementsByTagName("input"),n=e.length;for(;n--;){var o=e[n];o.checked&&St.push(o)}}(o),!z&&!(/mousedown|pointerdown/.test(r)&&0!==e.button||t.disabled||s.isContentEditable||(l=P(l,t.draggable,o,!1))&&l.animated||Z===l)){if(J=F(l),et=F(l,t.draggable),"function"==typeof c){if(c.call(this,e,l,this))return W({sortable:n,rootEl:s,name:"filter",targetEl:l,toEl:o,fromEl:o}),K("filter",n,{evt:e}),void(i&&e.cancelable&&e.preventDefault())}else if(c&&(c=c.split(",").some(function(t){if(t=P(s,t.trim(),o,!1))return W({sortable:n,rootEl:t,name:"filter",targetEl:l,fromEl:o,toEl:o}),K("filter",n,{evt:e}),!0})))return void(i&&e.cancelable&&e.preventDefault());t.handle&&!P(s,t.handle,o,!1)||this._prepareDragStart(e,a,l)}}},_prepareDragStart:function(t,e,n){var o,i=this,r=i.el,a=i.options,l=r.ownerDocument;if(n&&!z&&n.parentNode===r){var s=X(n);if(q=r,G=(z=n).parentNode,V=z.nextSibling,Z=n,ot=a.group,rt={target:Rt.dragged=z,clientX:(e||t).clientX,clientY:(e||t).clientY},ct=rt.clientX-s.left,ut=rt.clientY-s.top,this._lastX=(e||t).clientX,this._lastY=(e||t).clientY,z.style["will-change"]="all",o=function(){K("delayEnded",i,{evt:t}),Rt.eventCanceled?i._onDrop():(i._disableDelayedDragEvents(),!c&&i.nativeDraggable&&(z.draggable=!0),i._triggerDragStart(t,e),W({sortable:i,name:"choose",originalEvent:t}),k(z,a.chosenClass,!0))},a.ignore.split(",").forEach(function(t){g(z,t.trim(),Yt)}),u(l,"dragover",Pt),u(l,"mousemove",Pt),u(l,"touchmove",Pt),u(l,"mouseup",i._onDrop),u(l,"touchend",i._onDrop),u(l,"touchcancel",i._onDrop),c&&this.nativeDraggable&&(this.options.touchStartThreshold=4,z.draggable=!0),K("delayStart",this,{evt:t}),!a.delay||a.delayOnTouchOnly&&!e||this.nativeDraggable&&(E||w))o();else{if(Rt.eventCanceled)return void this._onDrop();u(l,"mouseup",i._disableDelayedDrag),u(l,"touchend",i._disableDelayedDrag),u(l,"touchcancel",i._disableDelayedDrag),u(l,"mousemove",i._delayedDragTouchMoveHandler),u(l,"touchmove",i._delayedDragTouchMoveHandler),a.supportPointer&&u(l,"pointermove",i._delayedDragTouchMoveHandler),i._dragStartTimer=setTimeout(o,a.delay)}}},_delayedDragTouchMoveHandler:function(t){var e=t.touches?t.touches[0]:t;Math.max(Math.abs(e.clientX-this._lastX),Math.abs(e.clientY-this._lastY))>=Math.floor(this.options.touchStartThreshold/(this.nativeDraggable&&window.devicePixelRatio||1))&&this._disableDelayedDrag()},_disableDelayedDrag:function(){z&&Yt(z),clearTimeout(this._dragStartTimer),this._disableDelayedDragEvents()},_disableDelayedDragEvents:function(){var t=this.el.ownerDocument;d(t,"mouseup",this._disableDelayedDrag),d(t,"touchend",this._disableDelayedDrag),d(t,"touchcancel",this._disableDelayedDrag),d(t,"mousemove",this._delayedDragTouchMoveHandler),d(t,"touchmove",this._delayedDragTouchMoveHandler),d(t,"pointermove",this._delayedDragTouchMoveHandler)},_triggerDragStart:function(t,e){e=e||"touch"==t.pointerType&&t,!this.nativeDraggable||e?this.options.supportPointer?u(document,"pointermove",this._onTouchMove):u(document,e?"touchmove":"mousemove",this._onTouchMove):(u(z,"dragend",this),u(q,"dragstart",this._onDragStart));try{document.selection?Ht(function(){document.selection.empty()}):window.getSelection().removeAllRanges()}catch(t){}},_dragStarted:function(t,e){if(vt=!1,q&&z){K("dragStarted",this,{evt:e}),this.nativeDraggable&&u(document,"dragover",kt);var n=this.options;t||k(z,n.dragClass,!1),k(z,n.ghostClass,!0),Rt.active=this,t&&this._appendGhost(),W({sortable:this,name:"start",originalEvent:e})}else this._nulling()},_emulateDragOver:function(){if(at){this._lastX=at.clientX,this._lastY=at.clientY,Nt();for(var t=document.elementFromPoint(at.clientX,at.clientY),e=t;t&&t.shadowRoot&&(t=t.shadowRoot.elementFromPoint(at.clientX,at.clientY))!==e;)e=t;if(z.parentNode[j]._isOutsideThisEl(t),e)do{if(e[j]){if(e[j]._onDragOver({clientX:at.clientX,clientY:at.clientY,target:t,rootEl:e})&&!this.options.dragoverBubble)break}t=e}while(e=e.parentNode);It()}},_onTouchMove:function(t){if(rt){var e=this.options,n=e.fallbackTolerance,o=e.fallbackOffset,i=t.touches?t.touches[0]:t,r=U&&v(U,!0),a=U&&r&&r.a,l=U&&r&&r.d,s=Ct&&gt&&b(gt),c=(i.clientX-rt.clientX+o.x)/(a||1)+(s?s[0]-Et[0]:0)/(a||1),u=(i.clientY-rt.clientY+o.y)/(l||1)+(s?s[1]-Et[1]:0)/(l||1);if(!Rt.active&&!vt){if(n&&Math.max(Math.abs(i.clientX-this._lastX),Math.abs(i.clientY-this._lastY))<n)return;this._onDragStart(t,!0)}if(U){r?(r.e+=c-(lt||0),r.f+=u-(st||0)):r={a:1,b:0,c:0,d:1,e:c,f:u};var d="matrix(".concat(r.a,",").concat(r.b,",").concat(r.c,",").concat(r.d,",").concat(r.e,",").concat(r.f,")");R(U,"webkitTransform",d),R(U,"mozTransform",d),R(U,"msTransform",d),R(U,"transform",d),lt=c,st=u,at=i}t.cancelable&&t.preventDefault()}},_appendGhost:function(){if(!U){var t=this.options.fallbackOnBody?document.body:q,e=X(z,!0,Ct,!0,t),n=this.options;if(Ct){for(gt=t;"static"===R(gt,"position")&&"none"===R(gt,"transform")&&gt!==document;)gt=gt.parentNode;gt!==document.body&&gt!==document.documentElement?(gt===document&&(gt=N()),e.top+=gt.scrollTop,e.left+=gt.scrollLeft):gt=N(),Et=b(gt)}k(U=z.cloneNode(!0),n.ghostClass,!1),k(U,n.fallbackClass,!0),k(U,n.dragClass,!0),R(U,"transition",""),R(U,"transform",""),R(U,"box-sizing","border-box"),R(U,"margin",0),R(U,"top",e.top),R(U,"left",e.left),R(U,"width",e.width),R(U,"height",e.height),R(U,"opacity","0.8"),R(U,"position",Ct?"absolute":"fixed"),R(U,"zIndex","100000"),R(U,"pointerEvents","none"),Rt.ghost=U,t.appendChild(U),R(U,"transform-origin",ct/parseInt(U.style.width)*100+"% "+ut/parseInt(U.style.height)*100+"%")}},_onDragStart:function(t,e){var n=this,o=t.dataTransfer,i=n.options;K("dragStart",this,{evt:t}),Rt.eventCanceled?this._onDrop():(K("setupClone",this),Rt.eventCanceled||((Q=S(z)).draggable=!1,Q.style["will-change"]="",this._hideClone(),k(Q,this.options.chosenClass,!1),Rt.clone=Q),n.cloneId=Ht(function(){K("clone",n),Rt.eventCanceled||(n.options.removeCloneOnHide||q.insertBefore(Q,z),n._hideClone(),W({sortable:n,name:"clone"}))}),e||k(z,i.dragClass,!0),e?(mt=!0,n._loopId=setInterval(n._emulateDragOver,50)):(d(document,"mouseup",n._onDrop),d(document,"touchend",n._onDrop),d(document,"touchcancel",n._onDrop),o&&(o.effectAllowed="move",i.setData&&i.setData.call(n,o,z)),u(document,"drop",n),R(z,"transform","translateZ(0)")),vt=!0,n._dragStartId=Ht(n._dragStarted.bind(n,e,t)),u(document,"selectstart",n),dt=!0,s&&R(document.body,"user-select","none"))},_onDragOver:function(n){var o,i,r,a,l=this.el,s=n.target,e=this.options,t=e.group,c=Rt.active,u=ot===t,d=e.sort,h=it||c,f=this,p=!1;if(!Dt){if(void 0!==n.preventDefault&&n.cancelable&&n.preventDefault(),s=P(s,e.draggable,l,!0),M("dragOver"),Rt.eventCanceled)return p;if(z.contains(n.target)||s.animated&&s.animatingX&&s.animatingY||f._ignoreWhileAnimating===s)return A(!1);if(mt=!1,c&&!e.disabled&&(u?d||(r=!q.contains(z)):it===this||(this.lastPutMode=ot.checkPull(this,c,z,n))&&t.checkPut(this,c,z,n))){if(a="vertical"===this._getDirection(n,s),o=X(z),M("dragOverValid"),Rt.eventCanceled)return p;if(r)return G=q,O(),this._hideClone(),M("revert"),Rt.eventCanceled||(V?q.insertBefore(z,V):q.appendChild(z)),A(!0);var g=B(l,e.draggable);if(!g||function(t,e,n){var o=X(B(n.el,n.options.draggable));return e?t.clientX>o.right+10||t.clientX<=o.right&&t.clientY>o.bottom&&t.clientX>=o.left:t.clientX>o.right&&t.clientY>o.top||t.clientX<=o.right&&t.clientY>o.bottom+10}(n,a,this)&&!g.animated){if(g===z)return A(!1);if(g&&l===n.target&&(s=g),s&&(i=X(s)),!1!==Xt(q,l,z,o,s,i,n,!!s))return O(),l.appendChild(z),G=l,N(),A(!0)}else if(s.parentNode===l){i=X(s);var v,m,b,y=z.parentNode!==l,w=!function(t,e,n){var o=n?t.left:t.top,i=n?t.right:t.bottom,r=n?t.width:t.height,a=n?e.left:e.top,l=n?e.right:e.bottom,s=n?e.width:e.height;return o===a||i===l||o+r/2===a+s/2}(z.animated&&z.toRect||o,s.animated&&s.toRect||i,a),E=a?"top":"left",D=Y(s,"top","top")||Y(z,"top","top"),S=D?D.scrollTop:void 0;if(ht!==s&&(m=i[E],yt=!1,wt=!w&&e.invertSwap||y),0!==(v=function(t,e,n,o,i,r,a,l){var s=o?t.clientY:t.clientX,c=o?n.height:n.width,u=o?n.top:n.left,d=o?n.bottom:n.right,h=!1;if(!a)if(l&&pt<c*i){if(!yt&&(1===ft?u+c*r/2<s:s<d-c*r/2)&&(yt=!0),yt)h=!0;else if(1===ft?s<u+pt:d-pt<s)return-ft}else if(u+c*(1-i)/2<s&&s<d-c*(1-i)/2)return function(t){return F(z)<F(t)?1:-1}(e);if((h=h||a)&&(s<u+c*r/2||d-c*r/2<s))return u+c/2<s?1:-1;return 0}(n,s,i,a,w?1:e.swapThreshold,null==e.invertedSwapThreshold?e.swapThreshold:e.invertedSwapThreshold,wt,ht===s)))for(var _=F(z);_-=v,(b=G.children[_])&&("none"===R(b,"display")||b===U););if(0===v||b===s)return A(!1);ft=v;var C=(ht=s).nextElementSibling,T=!1,x=Xt(q,l,z,o,s,i,n,T=1===v);if(!1!==x)return 1!==x&&-1!==x||(T=1===x),Dt=!0,setTimeout(Bt,30),O(),T&&!C?l.appendChild(z):s.parentNode.insertBefore(z,T?C:s),D&&L(D,0,S-D.scrollTop),G=z.parentNode,void 0===m||wt||(pt=Math.abs(m-X(s)[E])),N(),A(!0)}if(l.contains(z))return A(!1)}return!1}function M(t,e){K(t,f,I({evt:n,isOwner:u,axis:a?"vertical":"horizontal",revert:r,dragRect:o,targetRect:i,canSort:d,fromSortable:h,target:s,completed:A,onMove:function(t,e){return Xt(q,l,z,o,t,X(t),n,e)},changed:N},e))}function O(){M("dragOverAnimationCapture"),f.captureAnimationState(),f!==h&&h.captureAnimationState()}function A(t){return M("dragOverCompleted",{insertion:t}),t&&(u?c._hideClone():c._showClone(f),f!==h&&(k(z,it?it.options.ghostClass:c.options.ghostClass,!1),k(z,e.ghostClass,!0)),it!==f&&f!==Rt.active?it=f:f===Rt.active&&it&&(it=null),h===f&&(f._ignoreWhileAnimating=s),f.animateAll(function(){M("dragOverAnimationComplete"),f._ignoreWhileAnimating=null}),f!==h&&(h.animateAll(),h._ignoreWhileAnimating=null)),(s===z&&!z.animated||s===l&&!s.animated)&&(ht=null),e.dragoverBubble||n.rootEl||s===document||(z.parentNode[j]._isOutsideThisEl(n.target),t||Pt(n)),!e.dragoverBubble&&n.stopPropagation&&n.stopPropagation(),p=!0}function N(){tt=F(z),nt=F(z,e.draggable),W({sortable:f,name:"change",toEl:l,newIndex:tt,newDraggableIndex:nt,originalEvent:n})}},_ignoreWhileAnimating:null,_offMoveEvents:function(){d(document,"mousemove",this._onTouchMove),d(document,"touchmove",this._onTouchMove),d(document,"pointermove",this._onTouchMove),d(document,"dragover",Pt),d(document,"mousemove",Pt),d(document,"touchmove",Pt)},_offUpEvents:function(){var t=this.el.ownerDocument;d(t,"mouseup",this._onDrop),d(t,"touchend",this._onDrop),d(t,"pointerup",this._onDrop),d(t,"touchcancel",this._onDrop),d(document,"selectstart",this)},_onDrop:function(t){var e=this.el,n=this.options;tt=F(z),nt=F(z,n.draggable),K("drop",this,{evt:t}),G=z&&z.parentNode,tt=F(z),nt=F(z,n.draggable),Rt.eventCanceled||(yt=wt=vt=!1,clearInterval(this._loopId),clearTimeout(this._dragStartTimer),Lt(this.cloneId),Lt(this._dragStartId),this.nativeDraggable&&(d(document,"drop",this),d(e,"dragstart",this._onDragStart)),this._offMoveEvents(),this._offUpEvents(),s&&R(document.body,"user-select",""),R(z,"transform",""),t&&(dt&&(t.cancelable&&t.preventDefault(),n.dropBubble||t.stopPropagation()),U&&U.parentNode&&U.parentNode.removeChild(U),(q===G||it&&"clone"!==it.lastPutMode)&&Q&&Q.parentNode&&Q.parentNode.removeChild(Q),z&&(this.nativeDraggable&&d(z,"dragend",this),Yt(z),z.style["will-change"]="",dt&&!vt&&k(z,it?it.options.ghostClass:this.options.ghostClass,!1),k(z,this.options.chosenClass,!1),W({sortable:this,name:"unchoose",toEl:G,newIndex:null,newDraggableIndex:null,originalEvent:t}),q!==G?(0<=tt&&(W({rootEl:G,name:"add",toEl:G,fromEl:q,originalEvent:t}),W({sortable:this,name:"remove",toEl:G,originalEvent:t}),W({rootEl:G,name:"sort",toEl:G,fromEl:q,originalEvent:t}),W({sortable:this,name:"sort",toEl:G,originalEvent:t})),it&&it.save()):tt!==J&&0<=tt&&(W({sortable:this,name:"update",toEl:G,originalEvent:t}),W({sortable:this,name:"sort",toEl:G,originalEvent:t})),Rt.active&&(null!=tt&&-1!==tt||(tt=J,nt=et),W({sortable:this,name:"end",toEl:G,originalEvent:t}),this.save())))),this._nulling()},_nulling:function(){K("nulling",this),q=z=G=U=V=Q=Z=$=rt=at=dt=tt=nt=J=et=ht=ft=it=ot=Rt.dragged=Rt.ghost=Rt.clone=Rt.active=null,St.forEach(function(t){t.checked=!0}),St.length=lt=st=0},handleEvent:function(t){switch(t.type){case"drop":case"dragend":this._onDrop(t);break;case"dragenter":case"dragover":z&&(this._onDragOver(t),function(t){t.dataTransfer&&(t.dataTransfer.dropEffect="move");t.cancelable&&t.preventDefault()}(t));break;case"selectstart":t.preventDefault()}},toArray:function(){for(var t,e=[],n=this.el.children,o=0,i=n.length,r=this.options;o<i;o++)P(t=n[o],r.draggable,this.el,!1)&&e.push(t.getAttribute(r.dataIdAttr)||Ft(t));return e},sort:function(t){var o={},i=this.el;this.toArray().forEach(function(t,e){var n=i.children[e];P(n,this.options.draggable,i,!1)&&(o[t]=n)},this),t.forEach(function(t){o[t]&&(i.removeChild(o[t]),i.appendChild(o[t]))})},save:function(){var t=this.options.store;t&&t.set&&t.set(this)},closest:function(t,e){return P(t,e||this.options.draggable,this.el,!1)},option:function(t,e){var n=this.options;if(void 0===e)return n[t];var o=O.modifyOption(this,t,e);n[t]=void 0!==o?o:e,"group"===t&&At(n)},destroy:function(){K("destroy",this);var t=this.el;t[j]=null,d(t,"mousedown",this._onTapStart),d(t,"touchstart",this._onTapStart),d(t,"pointerdown",this._onTapStart),this.nativeDraggable&&(d(t,"dragover",this),d(t,"dragenter",this)),Array.prototype.forEach.call(t.querySelectorAll("[draggable]"),function(t){t.removeAttribute("draggable")}),this._onDrop(),this._disableDelayedDragEvents(),bt.splice(bt.indexOf(this.el),1),this.el=t=null},_hideClone:function(){if(!$){if(K("hideClone",this),Rt.eventCanceled)return;R(Q,"display","none"),this.options.removeCloneOnHide&&Q.parentNode&&Q.parentNode.removeChild(Q),$=!0}},_showClone:function(t){if("clone"===t.lastPutMode){if($){if(K("showClone",this),Rt.eventCanceled)return;q.contains(z)&&!this.options.group.revertClone?q.insertBefore(Q,z):V?q.insertBefore(Q,V):q.appendChild(Q),this.options.group.revertClone&&this.animate(z,Q),R(Q,"display",""),$=!1}}else this._hideClone()}},_t&&u(document,"touchmove",function(t){(Rt.active||vt)&&t.cancelable&&t.preventDefault()}),Rt.utils={on:u,off:d,css:R,find:g,is:function(t,e){return!!P(t,e,t,!1)},extend:function(t,e){if(t&&e)for(var n in e)e.hasOwnProperty(n)&&(t[n]=e[n]);return t},throttle:D,closest:P,toggleClass:k,clone:S,index:F,nextTick:Ht,cancelNextTick:Lt,detectDirection:Ot,getChild:m},Rt.get=function(t){return t[j]},Rt.mount=function(){for(var t=arguments.length,e=new Array(t),n=0;n<t;n++)e[n]=arguments[n];e[0].constructor===Array&&(e=e[0]),e.forEach(function(t){if(!t.prototype||!t.prototype.constructor)throw"Sortable: Mounted plugin must be a constructor function, not ".concat({}.toString.call(t));t.utils&&(Rt.utils=I({},Rt.utils,t.utils)),O.mount(t)})},Rt.create=function(t,e){return new Rt(t,e)};var jt,Kt,Wt,zt,Gt,Ut,qt=[],Vt=!(Rt.version="1.10.2");function Zt(){qt.forEach(function(t){clearInterval(t.pid)}),qt=[]}function Qt(){clearInterval(Ut)}function $t(t){var e=t.originalEvent,n=t.putSortable,o=t.dragEl,i=t.activeSortable,r=t.dispatchSortableEvent,a=t.hideGhostForTarget,l=t.unhideGhostForTarget;if(e){var s=n||i;a();var c=e.changedTouches&&e.changedTouches.length?e.changedTouches[0]:e,u=document.elementFromPoint(c.clientX,c.clientY);l(),s&&!s.el.contains(u)&&(r("spill"),this.onSpill({dragEl:o,putSortable:n}))}}var Jt,te=D(function(n,t,e,o){if(t.scroll){var i,r=(n.touches?n.touches[0]:n).clientX,a=(n.touches?n.touches[0]:n).clientY,l=t.scrollSensitivity,s=t.scrollSpeed,c=N(),u=!1;Kt!==e&&(Kt=e,Zt(),jt=t.scroll,i=t.scrollFn,!0===jt&&(jt=H(e,!0)));var d=0,h=jt;do{var f=h,p=X(f),g=p.top,v=p.bottom,m=p.left,b=p.right,y=p.width,w=p.height,E=void 0,D=void 0,S=f.scrollWidth,_=f.scrollHeight,C=R(f),T=f.scrollLeft,x=f.scrollTop;D=f===c?(E=y<S&&("auto"===C.overflowX||"scroll"===C.overflowX||"visible"===C.overflowX),w<_&&("auto"===C.overflowY||"scroll"===C.overflowY||"visible"===C.overflowY)):(E=y<S&&("auto"===C.overflowX||"scroll"===C.overflowX),w<_&&("auto"===C.overflowY||"scroll"===C.overflowY));var M=E&&(Math.abs(b-r)<=l&&T+y<S)-(Math.abs(m-r)<=l&&!!T),O=D&&(Math.abs(v-a)<=l&&x+w<_)-(Math.abs(g-a)<=l&&!!x);if(!qt[d])for(var A=0;A<=d;A++)qt[A]||(qt[A]={});qt[d].vx==M&&qt[d].vy==O&&qt[d].el===f||(qt[d].el=f,qt[d].vx=M,qt[d].vy=O,clearInterval(qt[d].pid),0==M&&0==O||(u=!0,qt[d].pid=setInterval(function(){o&&0===this.layer&&Rt.active._onTouchMove(Gt);var t=qt[this.layer].vy?qt[this.layer].vy*s:0,e=qt[this.layer].vx?qt[this.layer].vx*s:0;"function"==typeof i&&"continue"!==i.call(Rt.dragged.parentNode[j],e,t,n,Gt,qt[this.layer].el)||L(qt[this.layer].el,e,t)}.bind({layer:d}),24))),d++}while(t.bubbleScroll&&h!==c&&(h=H(h,!1)));Vt=u}},30);function ee(){}function ne(){}ee.prototype={startIndex:null,dragStart:function(t){var e=t.oldDraggableIndex;this.startIndex=e},onSpill:function(t){var e=t.dragEl,n=t.putSortable;this.sortable.captureAnimationState(),n&&n.captureAnimationState();var o=m(this.sortable.el,this.startIndex,this.options);o?this.sortable.el.insertBefore(e,o):this.sortable.el.appendChild(e),this.sortable.animateAll(),n&&n.animateAll()},drop:$t},a(ee,{pluginName:"revertOnSpill"}),ne.prototype={onSpill:function(t){var e=t.dragEl,n=t.putSortable||this.sortable;n.captureAnimationState(),e.parentNode&&e.parentNode.removeChild(e),n.animateAll()},drop:$t},a(ne,{pluginName:"removeOnSpill"});var oe,ie,re,ae,le,se=[],ce=[],ue=!1,de=!1,he=!1;function fe(o,i){ce.forEach(function(t,e){var n=i.children[t.sortableIndex+(o?Number(e):0)];n?i.insertBefore(t,n):i.appendChild(t)})}function pe(){se.forEach(function(t){t!==re&&t.parentNode&&t.parentNode.removeChild(t)})}return Rt.mount(new function(){function t(){for(var t in this.defaults={scroll:!0,scrollSensitivity:30,scrollSpeed:10,bubbleScroll:!0},this)"_"===t.charAt(0)&&"function"==typeof this[t]&&(this[t]=this[t].bind(this))}return t.prototype={dragStarted:function(t){var e=t.originalEvent;this.sortable.nativeDraggable?u(document,"dragover",this._handleAutoScroll):this.options.supportPointer?u(document,"pointermove",this._handleFallbackAutoScroll):e.touches?u(document,"touchmove",this._handleFallbackAutoScroll):u(document,"mousemove",this._handleFallbackAutoScroll)},dragOverCompleted:function(t){var e=t.originalEvent;this.options.dragOverBubble||e.rootEl||this._handleAutoScroll(e)},drop:function(){this.sortable.nativeDraggable?d(document,"dragover",this._handleAutoScroll):(d(document,"pointermove",this._handleFallbackAutoScroll),d(document,"touchmove",this._handleFallbackAutoScroll),d(document,"mousemove",this._handleFallbackAutoScroll)),Qt(),Zt(),clearTimeout(f),f=void 0},nulling:function(){Gt=Kt=jt=Vt=Ut=Wt=zt=null,qt.length=0},_handleFallbackAutoScroll:function(t){this._handleAutoScroll(t,!0)},_handleAutoScroll:function(e,n){var o=this,i=(e.touches?e.touches[0]:e).clientX,r=(e.touches?e.touches[0]:e).clientY,t=document.elementFromPoint(i,r);if(Gt=e,n||E||w||s){te(e,this.options,t,n);var a=H(t,!0);!Vt||Ut&&i===Wt&&r===zt||(Ut&&Qt(),Ut=setInterval(function(){var t=H(document.elementFromPoint(i,r),!0);t!==a&&(a=t,Zt()),te(e,o.options,t,n)},10),Wt=i,zt=r)}else{if(!this.options.bubbleScroll||H(t,!0)===N())return void Zt();te(e,this.options,H(t,!1),!1)}}},a(t,{pluginName:"scroll",initializeByDefault:!0})}),Rt.mount(ne,ee),Rt.mount(new function(){function t(){this.defaults={swapClass:"sortable-swap-highlight"}}return t.prototype={dragStart:function(t){var e=t.dragEl;Jt=e},dragOverValid:function(t){var e=t.completed,n=t.target,o=t.onMove,i=t.activeSortable,r=t.changed,a=t.cancel;if(i.options.swap){var l=this.sortable.el,s=this.options;if(n&&n!==l){var c=Jt;Jt=!1!==o(n)?(k(n,s.swapClass,!0),n):null,c&&c!==Jt&&k(c,s.swapClass,!1)}r(),e(!0),a()}},drop:function(t){var e=t.activeSortable,n=t.putSortable,o=t.dragEl,i=n||this.sortable,r=this.options;Jt&&k(Jt,r.swapClass,!1),Jt&&(r.swap||n&&n.options.swap)&&o!==Jt&&(i.captureAnimationState(),i!==e&&e.captureAnimationState(),function(t,e){var n,o,i=t.parentNode,r=e.parentNode;if(!i||!r||i.isEqualNode(e)||r.isEqualNode(t))return;n=F(t),o=F(e),i.isEqualNode(r)&&n<o&&o++;i.insertBefore(e,i.children[n]),r.insertBefore(t,r.children[o])}(o,Jt),i.animateAll(),i!==e&&e.animateAll())},nulling:function(){Jt=null}},a(t,{pluginName:"swap",eventProperties:function(){return{swapItem:Jt}}})}),Rt.mount(new function(){function t(o){for(var t in this)"_"===t.charAt(0)&&"function"==typeof this[t]&&(this[t]=this[t].bind(this));o.options.supportPointer?u(document,"pointerup",this._deselectMultiDrag):(u(document,"mouseup",this._deselectMultiDrag),u(document,"touchend",this._deselectMultiDrag)),u(document,"keydown",this._checkKeyDown),u(document,"keyup",this._checkKeyUp),this.defaults={selectedClass:"sortable-selected",multiDragKey:null,setData:function(t,e){var n="";se.length&&ie===o?se.forEach(function(t,e){n+=(e?", ":"")+t.textContent}):n=e.textContent,t.setData("Text",n)}}}return t.prototype={multiDragKeyDown:!1,isMultiDrag:!1,delayStartGlobal:function(t){var e=t.dragEl;re=e},delayEnded:function(){this.isMultiDrag=~se.indexOf(re)},setupClone:function(t){var e=t.sortable,n=t.cancel;if(this.isMultiDrag){for(var o=0;o<se.length;o++)ce.push(S(se[o])),ce[o].sortableIndex=se[o].sortableIndex,ce[o].draggable=!1,ce[o].style["will-change"]="",k(ce[o],this.options.selectedClass,!1),se[o]===re&&k(ce[o],this.options.chosenClass,!1);e._hideClone(),n()}},clone:function(t){var e=t.sortable,n=t.rootEl,o=t.dispatchSortableEvent,i=t.cancel;this.isMultiDrag&&(this.options.removeCloneOnHide||se.length&&ie===e&&(fe(!0,n),o("clone"),i()))},showClone:function(t){var e=t.cloneNowShown,n=t.rootEl,o=t.cancel;this.isMultiDrag&&(fe(!1,n),ce.forEach(function(t){R(t,"display","")}),e(),le=!1,o())},hideClone:function(t){var e=this,n=(t.sortable,t.cloneNowHidden),o=t.cancel;this.isMultiDrag&&(ce.forEach(function(t){R(t,"display","none"),e.options.removeCloneOnHide&&t.parentNode&&t.parentNode.removeChild(t)}),n(),le=!0,o())},dragStartGlobal:function(t){t.sortable;!this.isMultiDrag&&ie&&ie.multiDrag._deselectMultiDrag(),se.forEach(function(t){t.sortableIndex=F(t)}),se=se.sort(function(t,e){return t.sortableIndex-e.sortableIndex}),he=!0},dragStarted:function(t){var e=this,n=t.sortable;if(this.isMultiDrag){if(this.options.sort&&(n.captureAnimationState(),this.options.animation)){se.forEach(function(t){t!==re&&R(t,"position","absolute")});var o=X(re,!1,!0,!0);se.forEach(function(t){t!==re&&_(t,o)}),ue=de=!0}n.animateAll(function(){ue=de=!1,e.options.animation&&se.forEach(function(t){C(t)}),e.options.sort&&pe()})}},dragOver:function(t){var e=t.target,n=t.completed,o=t.cancel;de&&~se.indexOf(e)&&(n(!1),o())},revert:function(t){var e=t.fromSortable,n=t.rootEl,o=t.sortable,i=t.dragRect;1<se.length&&(se.forEach(function(t){o.addAnimationState({target:t,rect:de?X(t):i}),C(t),t.fromRect=i,e.removeAnimationState(t)}),de=!1,function(o,i){se.forEach(function(t,e){var n=i.children[t.sortableIndex+(o?Number(e):0)];n?i.insertBefore(t,n):i.appendChild(t)})}(!this.options.removeCloneOnHide,n))},dragOverCompleted:function(t){var e=t.sortable,n=t.isOwner,o=t.insertion,i=t.activeSortable,r=t.parentEl,a=t.putSortable,l=this.options;if(o){if(n&&i._hideClone(),ue=!1,l.animation&&1<se.length&&(de||!n&&!i.options.sort&&!a)){var s=X(re,!1,!0,!0);se.forEach(function(t){t!==re&&(_(t,s),r.appendChild(t))}),de=!0}if(!n)if(de||pe(),1<se.length){var c=le;i._showClone(e),i.options.animation&&!le&&c&&ce.forEach(function(t){i.addAnimationState({target:t,rect:ae}),t.fromRect=ae,t.thisAnimationDuration=null})}else i._showClone(e)}},dragOverAnimationCapture:function(t){var e=t.dragRect,n=t.isOwner,o=t.activeSortable;if(se.forEach(function(t){t.thisAnimationDuration=null}),o.options.animation&&!n&&o.multiDrag.isMultiDrag){ae=a({},e);var i=v(re,!0);ae.top-=i.f,ae.left-=i.e}},dragOverAnimationComplete:function(){de&&(de=!1,pe())},drop:function(t){var e=t.originalEvent,n=t.rootEl,o=t.parentEl,i=t.sortable,r=t.dispatchSortableEvent,a=t.oldIndex,l=t.putSortable,s=l||this.sortable;if(e){var c=this.options,u=o.children;if(!he)if(c.multiDragKey&&!this.multiDragKeyDown&&this._deselectMultiDrag(),k(re,c.selectedClass,!~se.indexOf(re)),~se.indexOf(re))se.splice(se.indexOf(re),1),oe=null,A({sortable:i,rootEl:n,name:"deselect",targetEl:re,originalEvt:e});else{if(se.push(re),A({sortable:i,rootEl:n,name:"select",targetEl:re,originalEvt:e}),e.shiftKey&&oe&&i.el.contains(oe)){var d,h,f=F(oe),p=F(re);if(~f&&~p&&f!==p)for(d=f<p?(h=f,p):(h=p,f+1);h<d;h++)~se.indexOf(u[h])||(k(u[h],c.selectedClass,!0),se.push(u[h]),A({sortable:i,rootEl:n,name:"select",targetEl:u[h],originalEvt:e}))}else oe=re;ie=s}if(he&&this.isMultiDrag){if((o[j].options.sort||o!==n)&&1<se.length){var g=X(re),v=F(re,":not(."+this.options.selectedClass+")");if(!ue&&c.animation&&(re.thisAnimationDuration=null),s.captureAnimationState(),!ue&&(c.animation&&(re.fromRect=g,se.forEach(function(t){if(t.thisAnimationDuration=null,t!==re){var e=de?X(t):g;t.fromRect=e,s.addAnimationState({target:t,rect:e})}})),pe(),se.forEach(function(t){u[v]?o.insertBefore(t,u[v]):o.appendChild(t),v++}),a===F(re))){var m=!1;se.forEach(function(t){t.sortableIndex===F(t)||(m=!0)}),m&&r("update")}se.forEach(function(t){C(t)}),s.animateAll()}ie=s}(n===o||l&&"clone"!==l.lastPutMode)&&ce.forEach(function(t){t.parentNode&&t.parentNode.removeChild(t)})}},nullingGlobal:function(){this.isMultiDrag=he=!1,ce.length=0},destroyGlobal:function(){this._deselectMultiDrag(),d(document,"pointerup",this._deselectMultiDrag),d(document,"mouseup",this._deselectMultiDrag),d(document,"touchend",this._deselectMultiDrag),d(document,"keydown",this._checkKeyDown),d(document,"keyup",this._checkKeyUp)},_deselectMultiDrag:function(t){if(!(void 0!==he&&he||ie!==this.sortable||t&&P(t.target,this.options.draggable,this.sortable.el,!1)||t&&0!==t.button))for(;se.length;){var e=se[0];k(e,this.options.selectedClass,!1),se.shift(),A({sortable:this.sortable,rootEl:this.sortable.el,name:"deselect",targetEl:e,originalEvt:t})}},_checkKeyDown:function(t){t.key===this.options.multiDragKey&&(this.multiDragKeyDown=!0)},_checkKeyUp:function(t){t.key===this.options.multiDragKey&&(this.multiDragKeyDown=!1)}},a(t,{pluginName:"multiDrag",utils:{select:function(t){var e=t.parentNode[j];e&&e.options.multiDrag&&!~se.indexOf(t)&&(ie&&ie!==e&&(ie.multiDrag._deselectMultiDrag(),ie=e),k(t,e.options.selectedClass,!0),se.push(t))},deselect:function(t){var e=t.parentNode[j],n=se.indexOf(t);e&&e.options.multiDrag&&~n&&(k(t,e.options.selectedClass,!1),se.splice(n,1))}},eventProperties:function(){var n=this,o=[],i=[];return se.forEach(function(t){var e;o.push({multiDragElement:t,index:t.sortableIndex}),e=de&&t!==re?-1:de?F(t,":not(."+n.options.selectedClass+")"):F(t),i.push({multiDragElement:t,index:e})}),{items:e(se),clones:[].concat(ce),oldIndicies:o,newIndicies:i}},optionListeners:{multiDragKey:function(t){return"ctrl"===(t=t.toLowerCase())?t="Control":1<t.length&&(t=t.charAt(0).toUpperCase()+t.substr(1)),t}}})}),Rt});
  1 +module.exports = function(api) {
  2 + api.cache(true);
  3 +
  4 + let presets;
  5 +
  6 + if (process.env.NODE_ENV === 'es') {
  7 + presets = [
  8 + [
  9 + "@babel/preset-env",
  10 + {
  11 + "modules": false
  12 + }
  13 + ]
  14 + ];
  15 + } else if (process.env.NODE_ENV === 'umd') {
  16 + presets = [
  17 + [
  18 + "@babel/preset-env"
  19 + ]
  20 + ];
  21 + }
  22 +
  23 + return {
  24 + plugins: ['@babel/plugin-transform-object-assign'],
  25 + presets
  26 + };
  27 +};
  1 +{
  2 + "name": "Sortable",
  3 + "main": [
  4 + "Sortable.js"
  5 + ],
  6 + "homepage": "http://SortableJS.github.io/Sortable/",
  7 + "authors": [
  8 + "RubaXa <ibnRubaXa@gmail.com>",
  9 + "owenm <owen23355@gmail.com>"
  10 + ],
  11 + "description": "JavaScript library for reorderable drag-and-drop lists on modern browsers and touch devices. No jQuery required. Supports Meteor, AngularJS, React, Polymer, Vue, Knockout and any CSS library, e.g. Bootstrap.",
  12 + "keywords": [
  13 + "sortable",
  14 + "reorder",
  15 + "list",
  16 + "html5",
  17 + "drag",
  18 + "and",
  19 + "drop",
  20 + "dnd",
  21 + "web-components"
  22 + ],
  23 + "license": "MIT",
  24 + "ignore": [
  25 + "node_modules",
  26 + "bower_components",
  27 + "test",
  28 + "tests"
  29 + ]
  30 +}
  1 +import Sortable from './entry-defaults.js';
  2 +import Swap from '../plugins/Swap';
  3 +import MultiDrag from '../plugins/MultiDrag';
  4 +
  5 +Sortable.mount(new Swap());
  6 +Sortable.mount(new MultiDrag());
  7 +
  8 +export default Sortable;
  1 +import Sortable from '../src/Sortable.js';
  2 +import AutoScroll from '../plugins/AutoScroll';
  3 +import OnSpill from '../plugins/OnSpill';
  4 +import Swap from '../plugins/Swap';
  5 +import MultiDrag from '../plugins/MultiDrag';
  6 +
  7 +export default Sortable;
  8 +
  9 +export {
  10 + Sortable,
  11 +
  12 + // Default
  13 + AutoScroll,
  14 + OnSpill,
  15 +
  16 + // Extra
  17 + Swap,
  18 + MultiDrag
  19 +};
  1 +import Sortable from '../src/Sortable.js';
  2 +import AutoScroll from '../plugins/AutoScroll';
  3 +import { RemoveOnSpill, RevertOnSpill } from '../plugins/OnSpill';
  4 +// Extra
  5 +import Swap from '../plugins/Swap';
  6 +import MultiDrag from '../plugins/MultiDrag';
  7 +
  8 +Sortable.mount(new AutoScroll());
  9 +Sortable.mount(RemoveOnSpill, RevertOnSpill);
  10 +
  11 +export default Sortable;
  12 +
  13 +export {
  14 + Sortable,
  15 +
  16 + // Extra
  17 + Swap,
  18 + MultiDrag
  19 +};
  1 +<!DOCTYPE html>
  2 +<html>
  3 +<head>
  4 + <link rel="icon" type="image/png" href="st/og-image.png">
  5 + <title>SortableJS</title>
  6 + <link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.2.1/css/bootstrap.min.css" integrity="sha384-GJzZqFGwb1QTTN6wy59ffF1BuGJpLSa9DkKMp0DgiMDm4iYMj70gZWKYbI706tWS" crossorigin="anonymous">
  7 + <link rel="stylesheet" href="https://use.fontawesome.com/releases/v5.6.3/css/all.css" integrity="sha384-UHRtZLI+pbxtHCWp1t77Bi1L4ZtiqrqD80Kn4Z8NTSRyMA2Fd33n5dQ8lWUE00s/" crossorigin="anonymous">
  8 + <link rel="stylesheet" type="text/css" href="st/theme.css">
  9 +
  10 + <meta charset="utf-8"/>
  11 + <meta http-equiv="X-UA-Compatible" content="IE=edge"/>
  12 + <meta property="og:image" content="/st/og-image.png"/>
  13 + <meta name="keywords" content="sortable, reorder, list, javascript, html5, drag and drop, dnd, animation, groups, dnd, sortableJS"/>
  14 + <meta name="description" content="Sortable — is a JavaScript library for reorderable drag-and-drop lists on modern browsers and touch devices. No jQuery required. Supports Meteor, AngularJS, React, Polymer, Vue, Knockout and any CSS library, e.g. Bootstrap."/>
  15 + <meta name="viewport" content="width=device-width, initial-scale=0.5"/>
  16 +</head>
  17 +<body>
  18 +
  19 + <a href="https://github.com/SortableJS/Sortable"><img style="position: fixed; top: 0; right: 0; border: 0; z-index:99999" src="https://s3.amazonaws.com/github/ribbons/forkme_right_gray_6d6d6d.png" alt="Fork me on GitHub"></a>
  20 +
  21 + <div class="container">
  22 + <div class="text-center row header">
  23 + <img class="mx-auto d-block" src="st/og-image.png" style="max-width: 200px; max-height: 200px" />
  24 + <h1 class="col-12">SortableJS</h1>
  25 + <h3 class="col-12 text-center">JavaScript library for reorderable drag-and-drop lists</h3>
  26 +
  27 + <div class="toc col-12 col-md-5 mt-3">
  28 + <h5>Features</h5>
  29 + <li><a href="#simple-list">Simple list</a></li>
  30 + <li><a href="#shared-lists">Shared lists</a></li>
  31 + <li><a href="#cloning">Cloning</a></li>
  32 + <li><a href="#sorting-disabled">Disabling sorting</a></li>
  33 + <li><a href="#handle">Handles</a></li>
  34 + <li><a href="#filter">Filter</a></li>
  35 + <li><a href="#thresholds">Thresholds</a></li>
  36 + <h5>Examples</h5>
  37 + <li><a href="#grid">Grid</a></li>
  38 + <li><a href="#nested">Nested sortables</a></li>
  39 + <h5>Plugins</h5>
  40 + <li><a href="#multi-drag">MultiDrag</a></li>
  41 + <li><a href="#swap">Swap</a></li>
  42 + <h5><a href="#comparisons">Comparisons</a></h5>
  43 + <h5><a href="#frameworks">Framework Support</a></h5>
  44 + </div>
  45 + </div>
  46 +
  47 + <div class="row">
  48 + <h2 class="col-12">Features</h2>
  49 + </div>
  50 + <hr />
  51 + <div id="simple-list" class="row">
  52 + <h4 class="col-12">Simple list example</h4>
  53 + <div id="example1" class="list-group col">
  54 + <div class="list-group-item">Item 1</div>
  55 + <div class="list-group-item">Item 2</div>
  56 + <div class="list-group-item">Item 3</div>
  57 + <div class="list-group-item">Item 4</div>
  58 + <div class="list-group-item">Item 5</div>
  59 + <div class="list-group-item">Item 6</div>
  60 + </div>
  61 + <div style="padding: 0" class="col-12">
  62 +<pre class="prettyprint">new Sortable(example1, {
  63 + animation: 150,
  64 + ghostClass: 'blue-background-class'
  65 +});</pre>
  66 + </div>
  67 + </div>
  68 + <hr />
  69 +
  70 + <div id="shared-lists" class="row">
  71 + <h4 class="col-12">Shared lists</h4>
  72 + <div id="example2-left" class="list-group col">
  73 + <div class="list-group-item">Item 1</div>
  74 + <div class="list-group-item">Item 2</div>
  75 + <div class="list-group-item">Item 3</div>
  76 + <div class="list-group-item">Item 4</div>
  77 + <div class="list-group-item">Item 5</div>
  78 + <div class="list-group-item">Item 6</div>
  79 + </div>
  80 +
  81 + <div id="example2-right" class="list-group col">
  82 + <div class="list-group-item tinted">Item 1</div>
  83 + <div class="list-group-item tinted">Item 2</div>
  84 + <div class="list-group-item tinted">Item 3</div>
  85 + <div class="list-group-item tinted">Item 4</div>
  86 + <div class="list-group-item tinted">Item 5</div>
  87 + <div class="list-group-item tinted">Item 6</div>
  88 + </div>
  89 + <div style="padding: 0" class="col-12">
  90 +<pre class="prettyprint">new Sortable(example2Left, {
  91 + group: 'shared', // set both lists to same group
  92 + animation: 150
  93 +});
  94 +
  95 +new Sortable(example2Right, {
  96 + group: 'shared',
  97 + animation: 150
  98 +});</pre>
  99 + </div>
  100 + </div>
  101 + <hr />
  102 +
  103 + <div id="cloning" class="row">
  104 + <h4 class="col-12">Cloning</h4>
  105 + <p class="col-12">Try dragging from one list to another. The item you drag will be cloned and the clone will stay in the original list.</p>
  106 + <div id="example3-left" class="list-group col">
  107 + <div class="list-group-item">Item 1</div>
  108 + <div class="list-group-item">Item 2</div>
  109 + <div class="list-group-item">Item 3</div>
  110 + <div class="list-group-item">Item 4</div>
  111 + <div class="list-group-item">Item 5</div>
  112 + <div class="list-group-item">Item 6</div>
  113 + </div>
  114 +
  115 + <div id="example3-right" class="list-group col">
  116 + <div class="list-group-item tinted">Item 1</div>
  117 + <div class="list-group-item tinted">Item 2</div>
  118 + <div class="list-group-item tinted">Item 3</div>
  119 + <div class="list-group-item tinted">Item 4</div>
  120 + <div class="list-group-item tinted">Item 5</div>
  121 + <div class="list-group-item tinted">Item 6</div>
  122 + </div>
  123 + <div style="padding: 0" class="col-12">
  124 +<pre class="prettyprint">new Sortable(example3Left, {
  125 + group: {
  126 + name: 'shared',
  127 + pull: 'clone' // To clone: set pull to 'clone'
  128 + },
  129 + animation: 150
  130 +});
  131 +
  132 +new Sortable(example3Right, {
  133 + group: {
  134 + name: 'shared',
  135 + pull: 'clone'
  136 + },
  137 + animation: 150
  138 +});</pre>
  139 + </div>
  140 + </div>
  141 + <hr />
  142 +
  143 + <div id="sorting-disabled" class="row">
  144 + <h4 class="col-12">Disabling Sorting</h4>
  145 + <p class="col-12">Try sorting the list on the left. It is not possible because it has it's <code>sort</code> option set to false. However, you can still drag from the list on the left to the list on the right.</p>
  146 + <div id="example4-left" class="list-group col">
  147 + <div class="list-group-item">Item 1</div>
  148 + <div class="list-group-item">Item 2</div>
  149 + <div class="list-group-item">Item 3</div>
  150 + <div class="list-group-item">Item 4</div>
  151 + <div class="list-group-item">Item 5</div>
  152 + <div class="list-group-item">Item 6</div>
  153 + </div>
  154 +
  155 + <div id="example4-right" class="list-group col">
  156 + <div class="list-group-item tinted">Item 1</div>
  157 + <div class="list-group-item tinted">Item 2</div>
  158 + <div class="list-group-item tinted">Item 3</div>
  159 + <div class="list-group-item tinted">Item 4</div>
  160 + <div class="list-group-item tinted">Item 5</div>
  161 + <div class="list-group-item tinted">Item 6</div>
  162 + </div>
  163 + <div style="padding: 0" class="col-12">
  164 +<pre class="prettyprint">new Sortable(example4Left, {
  165 + group: {
  166 + name: 'shared',
  167 + pull: 'clone',
  168 + put: false // Do not allow items to be put into this list
  169 + },
  170 + animation: 150,
  171 + sort: false // To disable sorting: set sort to false
  172 +});
  173 +
  174 +new Sortable(example4Right, {
  175 + group: 'shared',
  176 + animation: 150
  177 +});</pre>
  178 + </div>
  179 + </div>
  180 + <hr />
  181 +
  182 + <div id="handle" class="row">
  183 + <h4 class="col-12">Handle</h4>
  184 + <div id="example5" class="list-group col">
  185 + <div class="list-group-item"><i class="fas fa-arrows-alt handle"></i>&nbsp;&nbsp;Item 1</div>
  186 + <div class="list-group-item"><i class="fas fa-arrows-alt handle"></i>&nbsp;&nbsp;Item 2</div>
  187 + <div class="list-group-item"><i class="fas fa-arrows-alt handle"></i>&nbsp;&nbsp;Item 3</div>
  188 + <div class="list-group-item"><i class="fas fa-arrows-alt handle"></i>&nbsp;&nbsp;Item 4</div>
  189 + <div class="list-group-item"><i class="fas fa-arrows-alt handle"></i>&nbsp;&nbsp;Item 5</div>
  190 + <div class="list-group-item"><i class="fas fa-arrows-alt handle"></i>&nbsp;&nbsp;Item 6</div>
  191 + </div>
  192 + <div style="padding: 0" class="col-12">
  193 +<pre class="prettyprint">new Sortable(example5, {
  194 + handle: '.handle', // handle's class
  195 + animation: 150
  196 +});</pre>
  197 + </div>
  198 + </div>
  199 + <hr />
  200 +
  201 + <div id="filter" class="row">
  202 + <h4 class="col-12">Filter</h4>
  203 + <p class="col-12">Try dragging the item with a red background. It cannot be done, because that item is filtered out using the <code>filter</code> option.</p>
  204 + <div id="example6" class="list-group col">
  205 + <div class="list-group-item">Item 1</div>
  206 + <div class="list-group-item">Item 2</div>
  207 + <div class="list-group-item">Item 3</div>
  208 + <div class="list-group-item bg-danger filtered">Filtered</div>
  209 + <div class="list-group-item">Item 4</div>
  210 + <div class="list-group-item">Item 5</div>
  211 + </div>
  212 + <div style="padding: 0" class="col-12">
  213 +<pre class="prettyprint">new Sortable(example6, {
  214 + filter: '.filtered', // 'filtered' class is not draggable
  215 + animation: 150
  216 +});</pre>
  217 + </div>
  218 + </div>
  219 + <hr />
  220 +
  221 + <div id="thresholds" class="row">
  222 + <h4 class="col-12">Thresholds</h4>
  223 + <p class="col-12">Try modifying the inputs below to affect the swap thresholds. You can see the swap zones of the squares colored in dark blue, while the "dead zones" (that do not cause a swap) are colored in light blue.</p>
  224 + <div id="example7" class="square-section col">
  225 + <div class="square">
  226 + <div style="display: none;" class="inverted-swap-threshold-indicator indicator-left"></div>
  227 + <div class="swap-threshold-indicator"></div>
  228 + <div style="display: none;" class="inverted-swap-threshold-indicator indicator-right"></div>
  229 + <div class="num-indicator">1</div>
  230 + </div><!--
  231 + --><div class="square">
  232 + <div style="display: none;" class="inverted-swap-threshold-indicator indicator-left"></div>
  233 + <div class="swap-threshold-indicator"></div>
  234 + <div style="display: none;" class="inverted-swap-threshold-indicator indicator-right"></div>
  235 + <div class="num-indicator">2</div>
  236 + </div>
  237 + </div>
  238 + <div class="col-12 input-section">
  239 + <form>
  240 + <div class="form-group row">
  241 + <label class="col-sm-2 col-form-label" for="example7SwapThresholdInput">Swap Threshold</label>
  242 + <div class="col-sm-8 col-form-label">
  243 + <input min="0" max="1" value="1" step="0.01" type="range" class="form-control-range" id="example7SwapThresholdInput">
  244 + </div>
  245 + </div>
  246 + <div class="form-group row">
  247 + <div class="col-sm-2">Invert Swap</div>
  248 + <div class="col-sm-10">
  249 + <div class="form-check">
  250 + <input class="form-check-input" type="checkbox" id="example7InvertSwapInput">
  251 + </div>
  252 + </div>
  253 + </div>
  254 + <div class="form-group row">
  255 + <label class="col-sm-2 col-form-label" for="example7DirectionInput">Direction</label>
  256 + <select class="col-sm-4 form-control" id="example7DirectionInput">
  257 + <option value="h" selected>Horizontal</option>
  258 + <option value="v">Vertical</option>
  259 + </select>
  260 + </div>
  261 + </form>
  262 + </div>
  263 + <div style="padding: 0" class="col-12">
  264 +<pre class="prettyprint">new Sortable(example7, {
  265 + swapThreshold: <span id="example7SwapThresholdCode">1</span>,<span id="example7InvertSwapCode" style="display: none">
  266 + invertSwap: true,</span>
  267 + animation: 150
  268 +});</pre>
  269 + </div>
  270 + </div>
  271 +
  272 +
  273 + <div class="row">
  274 + <h2 class="col-12">Examples</h2>
  275 + </div>
  276 + <hr />
  277 +
  278 + <div id="grid" class="row">
  279 + <h4 class="col-12">Grid Example</h4>
  280 + <div id="gridDemo" class="col">
  281 + <div class="grid-square">Item 1</div><!--
  282 + --><div class="grid-square">Item 2</div><!--
  283 + --><div class="grid-square">Item 3</div><!--
  284 + --><div class="grid-square">Item 4</div><!--
  285 + --><div class="grid-square">Item 5</div><!--
  286 + --><div class="grid-square">Item 6</div><!--
  287 + --><div class="grid-square">Item 7</div><!--
  288 + --><div class="grid-square">Item 8</div><!--
  289 + --><div class="grid-square">Item 9</div><!--
  290 + --><div class="grid-square">Item 10</div><!--
  291 + --><div class="grid-square">Item 11</div><!--
  292 + --><div class="grid-square">Item 12</div><!--
  293 + --><div class="grid-square">Item 13</div><!--
  294 + --><div class="grid-square">Item 14</div><!--
  295 + --><div class="grid-square">Item 15</div><!--
  296 + --><div class="grid-square">Item 16</div><!--
  297 + --><div class="grid-square">Item 17</div><!--
  298 + --><div class="grid-square">Item 18</div><!--
  299 + --><div class="grid-square">Item 19</div><!--
  300 + --><div class="grid-square">Item 20</div>
  301 + </div>
  302 + </div>
  303 + <hr />
  304 +
  305 + <div id="nested" class="row">
  306 + <h4 class="col-12">Nested Sortables Example</h4>
  307 + <p class="col-12">NOTE: When using nested Sortables with animation, it is recommended that the <code>fallbackOnBody</code> option is set to true. <br />It is also always recommended that either the <code>invertSwap</code> option is set to true, or the <code>swapThreshold</code> option is lower than the default value of 1 (eg <code>0.65</code>).</p>
  308 + <div id="nestedDemo" class="list-group col nested-sortable">
  309 + <div class="list-group-item nested-1">Item 1.1
  310 + <div class="list-group nested-sortable">
  311 + <div class="list-group-item nested-2">Item 2.1</div>
  312 + <div class="list-group-item nested-2">Item 2.2
  313 + <div class="list-group nested-sortable">
  314 + <div class="list-group-item nested-3">Item 3.1</div>
  315 + <div class="list-group-item nested-3">Item 3.2</div>
  316 + <div class="list-group-item nested-3">Item 3.3</div>
  317 + <div class="list-group-item nested-3">Item 3.4</div>
  318 + </div>
  319 + </div>
  320 + <div class="list-group-item nested-2">Item 2.3</div>
  321 + <div class="list-group-item nested-2">Item 2.4</div>
  322 + </div>
  323 + </div>
  324 + <div class="list-group-item nested-1">Item 1.2</div>
  325 + <div class="list-group-item nested-1">Item 1.3</div>
  326 + <div class="list-group-item nested-1">Item 1.4
  327 + <div class="list-group nested-sortable">
  328 + <div class="list-group-item nested-2">Item 2.1</div>
  329 + <div class="list-group-item nested-2">Item 2.2</div>
  330 + <div class="list-group-item nested-2">Item 2.3</div>
  331 + <div class="list-group-item nested-2">Item 2.4</div>
  332 + </div>
  333 + </div>
  334 + <div class="list-group-item nested-1">Item 1.5</div>
  335 + </div>
  336 + <div style="padding: 0" class="col-12">
  337 +<pre class="prettyprint">// Loop through each nested sortable element
  338 +for (var i = 0; i < nestedSortables.length; i++) {
  339 + new Sortable(nestedSortables[i], {
  340 + group: 'nested',
  341 + animation: 150,
  342 + fallbackOnBody: true,
  343 + swapThreshold: 0.65
  344 + });
  345 +}</pre>
  346 + </div>
  347 + </div>
  348 +
  349 + <div class="row">
  350 + <h2 class="col-12">Plugins</h2>
  351 + </div>
  352 + <hr />
  353 +
  354 + <div id="multi-drag" class="row">
  355 + <h4 class="col-12">MultiDrag</h4>
  356 + <p class="col-12">The <a target="_blank" href="https://github.com/SortableJS/Sortable/tree/master/plugins/MultiDrag">MultiDrag</a> plugin allows for multiple items to be dragged at a time. You can click to "select" multiple items, and then drag them as one item.</p>
  357 + <div id="multiDragDemo" class="list-group col">
  358 + <div class="list-group-item">Item 1</div>
  359 + <div class="list-group-item">Item 2</div>
  360 + <div class="list-group-item">Item 3</div>
  361 + <div class="list-group-item">Item 4</div>
  362 + <div class="list-group-item">Item 5</div>
  363 + <div class="list-group-item">Item 6</div>
  364 + </div>
  365 + <div style="padding: 0" class="col-12">
  366 +<pre class="prettyprint">new Sortable(multiDragDemo, {
  367 + multiDrag: true, // Enable multi-drag
  368 + selectedClass: 'selected', // The class applied to the selected items
  369 + animation: 150
  370 +});</pre>
  371 + </div>
  372 + </div>
  373 + <hr />
  374 +
  375 + <div id="swap" class="row">
  376 + <h4 class="col-12">Swap</h4>
  377 + <p class="col-12">The <a target="_blank" href="https://github.com/SortableJS/Sortable/tree/master/plugins/Swap">Swap</a> plugin changes the behaviour of Sortable to allow for items to be swapped with eachother rather than sorted.</p>
  378 + <div id="swapDemo" class="list-group col">
  379 + <div class="list-group-item">Item 1</div>
  380 + <div class="list-group-item">Item 2</div>
  381 + <div class="list-group-item">Item 3</div>
  382 + <div class="list-group-item">Item 4</div>
  383 + <div class="list-group-item">Item 5</div>
  384 + <div class="list-group-item">Item 6</div>
  385 + </div>
  386 + <div style="padding: 0" class="col-12">
  387 +<pre class="prettyprint">new Sortable(swapDemo, {
  388 + swap: true, // Enable swap plugin
  389 + swapClass: 'highlight', // The class applied to the hovered swap item
  390 + animation: 150
  391 +});</pre>
  392 + </div>
  393 + </div>
  394 + <hr />
  395 +
  396 +
  397 +
  398 + <div class="mt-4"></div>
  399 +
  400 + <div id="comparisons" class="row">
  401 + <h2 class="col-12">Comparisons</h2>
  402 + </div>
  403 + <hr />
  404 +
  405 +
  406 + <div class="row frameworks">
  407 + <h2 class="col-12 text-center">jQuery-UI</h2>
  408 + <iframe class="mx-auto" src="https://player.vimeo.com/video/311581236?title=0&byline=0&portrait=0" width="640" height="361" frameborder="0" webkitallowfullscreen mozallowfullscreen allowfullscreen></iframe>
  409 +
  410 + <h2 class="col-12 text-center mt-5">Dragula</h2>
  411 + <iframe class="mx-auto" src="https://player.vimeo.com/video/311584137?title=0&byline=0&portrait=0" width="640" height="361" frameborder="0" webkitallowfullscreen mozallowfullscreen allowfullscreen></iframe>
  412 + </div>
  413 +
  414 + <div class="mt-4"></div>
  415 +
  416 + <div id="frameworks" class="row">
  417 + <h2 class="col-12">Framework Support</h2>
  418 + </div>
  419 + <hr />
  420 +
  421 + <div class="row frameworks">
  422 +
  423 + <h3 class="col-6">Vue</h3>
  424 + <h3 class="col-6"><a target="_blank" href="https://github.com/SortableJS/Vue.Draggable">Vue.Draggable</a></h3>
  425 +
  426 + <h3 class="col-6">React</h3>
  427 + <h3 class="col-6"><a target="_blank" href="https://github.com/SortableJS/react-sortablejs">react-sortablejs</a></h3>
  428 +
  429 + <h3 class="col-6">Angular</h3>
  430 + <h3 class="col-6"><a target="_blank" href="https://github.com/SortableJS/ngx-sortablejs">ngx-sortablejs</a></h3>
  431 +
  432 + <h3 class="col-6">jQuery</h3>
  433 + <h3 class="col-6"><a target="_blank" href="https://github.com/SortableJS/jquery-sortablejs">jquery-sortablejs</a></h3>
  434 +
  435 + <h3 class="col-6">Knockout</h3>
  436 + <h3 class="col-6"><a target="_blank" href="https://github.com/SortableJS/knockout-sortablejs">knockout-sortablejs</a></h3>
  437 +
  438 + <h3 class="col-6">Meteor</h3>
  439 + <h3 class="col-6"><a target="_blank" href="https://github.com/SortableJS/meteor-sortablejs">meteor-sortablejs</a></h3>
  440 +
  441 + <h3 class="col-6">Polymer</h3>
  442 + <h3 class="col-6"><a target="_blank" href="https://github.com/SortableJS/polymer-sortablejs">polymer-sortablejs</a></h3>
  443 +
  444 + <h3 class="col-6">Ember</h3>
  445 + <h3 class="col-6"><a target="_blank" href="https://github.com/SortableJS/ember-sortablejs">ember-sortablejs</a></h3>
  446 + </div>
  447 +
  448 + </div>
  449 +
  450 +
  451 + <!-- Latest Sortable -->
  452 + <script src="./Sortable.js"></script>
  453 +
  454 + <script type="text/javascript" src="st/prettify/prettify.js"></script>
  455 + <script type="text/javascript" src="st/prettify/run_prettify.js"></script>
  456 +
  457 + <script src="st/app.js"></script>
  458 +</body>
  459 +</html>
  1 +{
  2 + "name": "sortablejs",
  3 + "exportName": "Sortable",
  4 + "version": "1.10.2",
  5 + "devDependencies": {
  6 + "@babel/core": "^7.4.4",
  7 + "@babel/plugin-transform-object-assign": "^7.2.0",
  8 + "@babel/preset-env": "^7.4.4",
  9 + "rollup": "^1.11.3",
  10 + "rollup-plugin-babel": "^4.3.2",
  11 + "rollup-plugin-json": "^4.0.0",
  12 + "rollup-plugin-node-resolve": "^5.0.0",
  13 + "testcafe": "^1.3.1",
  14 + "testcafe-browser-provider-saucelabs": "^1.7.0",
  15 + "testcafe-reporter-xunit": "^2.1.0",
  16 + "uglify-js": "^3.5.12"
  17 + },
  18 + "description": "JavaScript library for reorderable drag-and-drop lists on modern browsers and touch devices. No jQuery required. Supports Meteor, AngularJS, React, Polymer, Vue, Knockout and any CSS library, e.g. Bootstrap.",
  19 + "main": "./Sortable.js",
  20 + "module": "modular/sortable.esm.js",
  21 + "scripts": {
  22 + "build:umd": "NODE_ENV=umd rollup -c ./scripts/umd-build.js",
  23 + "build:umd:watch": "set NODE_ENV=umd&& rollup -w -c ./scripts/umd-build.js",
  24 + "build:es": "set NODE_ENV=es&& rollup -c ./scripts/esm-build.js",
  25 + "build:es:watch": "set NODE_ENV=es&& rollup -w -c ./scripts/esm-build.js",
  26 + "minify": "node ./scripts/minify.js",
  27 + "build": "npm run build:es && npm run build:umd && npm run minify",
  28 + "test:compat": "node ./scripts/test-compat.js",
  29 + "test": "node ./scripts/test.js"
  30 + },
  31 + "maintainers": [
  32 + "Konstantin Lebedev <ibnRubaXa@gmail.com>",
  33 + "Owen Mills <owen23355@gmail.com>"
  34 + ],
  35 + "repository": {
  36 + "type": "git",
  37 + "url": "git://github.com/SortableJS/Sortable.git"
  38 + },
  39 + "files": [
  40 + "Sortable.js",
  41 + "Sortable.min.js",
  42 + "modular/"
  43 + ],
  44 + "keywords": [
  45 + "sortable",
  46 + "reorder",
  47 + "drag",
  48 + "meteor",
  49 + "angular",
  50 + "ng-sortable",
  51 + "react",
  52 + "vue",
  53 + "mixin"
  54 + ],
  55 + "license": "MIT"
  56 +}
  1 +import {
  2 + on,
  3 + off,
  4 + css,
  5 + throttle,
  6 + cancelThrottle,
  7 + scrollBy,
  8 + getParentAutoScrollElement,
  9 + expando,
  10 + getRect,
  11 + getWindowScrollingElement
  12 +} from '../../src/utils.js';
  13 +
  14 +import Sortable from '../../src/Sortable.js';
  15 +
  16 +import { Edge, IE11OrLess, Safari } from '../../src/BrowserInfo.js';
  17 +
  18 +let autoScrolls = [],
  19 + scrollEl,
  20 + scrollRootEl,
  21 + scrolling = false,
  22 + lastAutoScrollX,
  23 + lastAutoScrollY,
  24 + touchEvt,
  25 + pointerElemChangedInterval;
  26 +
  27 +function AutoScrollPlugin() {
  28 +
  29 + function AutoScroll() {
  30 + this.defaults = {
  31 + scroll: true,
  32 + scrollSensitivity: 30,
  33 + scrollSpeed: 10,
  34 + bubbleScroll: true
  35 + };
  36 +
  37 + // Bind all private methods
  38 + for (let fn in this) {
  39 + if (fn.charAt(0) === '_' && typeof this[fn] === 'function') {
  40 + this[fn] = this[fn].bind(this);
  41 + }
  42 + }
  43 + }
  44 +
  45 + AutoScroll.prototype = {
  46 + dragStarted({ originalEvent }) {
  47 + if (this.sortable.nativeDraggable) {
  48 + on(document, 'dragover', this._handleAutoScroll);
  49 + } else {
  50 + if (this.options.supportPointer) {
  51 + on(document, 'pointermove', this._handleFallbackAutoScroll);
  52 + } else if (originalEvent.touches) {
  53 + on(document, 'touchmove', this._handleFallbackAutoScroll);
  54 + } else {
  55 + on(document, 'mousemove', this._handleFallbackAutoScroll);
  56 + }
  57 + }
  58 + },
  59 +
  60 + dragOverCompleted({ originalEvent }) {
  61 + // For when bubbling is canceled and using fallback (fallback 'touchmove' always reached)
  62 + if (!this.options.dragOverBubble && !originalEvent.rootEl) {
  63 + this._handleAutoScroll(originalEvent);
  64 + }
  65 + },
  66 +
  67 + drop() {
  68 + if (this.sortable.nativeDraggable) {
  69 + off(document, 'dragover', this._handleAutoScroll);
  70 + } else {
  71 + off(document, 'pointermove', this._handleFallbackAutoScroll);
  72 + off(document, 'touchmove', this._handleFallbackAutoScroll);
  73 + off(document, 'mousemove', this._handleFallbackAutoScroll);
  74 + }
  75 +
  76 + clearPointerElemChangedInterval();
  77 + clearAutoScrolls();
  78 + cancelThrottle();
  79 + },
  80 +
  81 + nulling() {
  82 + touchEvt =
  83 + scrollRootEl =
  84 + scrollEl =
  85 + scrolling =
  86 + pointerElemChangedInterval =
  87 + lastAutoScrollX =
  88 + lastAutoScrollY = null;
  89 +
  90 + autoScrolls.length = 0;
  91 + },
  92 +
  93 + _handleFallbackAutoScroll(evt) {
  94 + this._handleAutoScroll(evt, true);
  95 + },
  96 +
  97 + _handleAutoScroll(evt, fallback) {
  98 + const x = (evt.touches ? evt.touches[0] : evt).clientX,
  99 + y = (evt.touches ? evt.touches[0] : evt).clientY,
  100 +
  101 + elem = document.elementFromPoint(x, y);
  102 +
  103 + touchEvt = evt;
  104 +
  105 + // IE does not seem to have native autoscroll,
  106 + // Edge's autoscroll seems too conditional,
  107 + // MACOS Safari does not have autoscroll,
  108 + // Firefox and Chrome are good
  109 + if (fallback || Edge || IE11OrLess || Safari) {
  110 + autoScroll(evt, this.options, elem, fallback);
  111 +
  112 + // Listener for pointer element change
  113 + let ogElemScroller = getParentAutoScrollElement(elem, true);
  114 + if (
  115 + scrolling &&
  116 + (
  117 + !pointerElemChangedInterval ||
  118 + x !== lastAutoScrollX ||
  119 + y !== lastAutoScrollY
  120 + )
  121 + ) {
  122 + pointerElemChangedInterval && clearPointerElemChangedInterval();
  123 + // Detect for pointer elem change, emulating native DnD behaviour
  124 + pointerElemChangedInterval = setInterval(() => {
  125 + let newElem = getParentAutoScrollElement(document.elementFromPoint(x, y), true);
  126 + if (newElem !== ogElemScroller) {
  127 + ogElemScroller = newElem;
  128 + clearAutoScrolls();
  129 + }
  130 + autoScroll(evt, this.options, newElem, fallback);
  131 + }, 10);
  132 + lastAutoScrollX = x;
  133 + lastAutoScrollY = y;
  134 + }
  135 + } else {
  136 + // if DnD is enabled (and browser has good autoscrolling), first autoscroll will already scroll, so get parent autoscroll of first autoscroll
  137 + if (!this.options.bubbleScroll || getParentAutoScrollElement(elem, true) === getWindowScrollingElement()) {
  138 + clearAutoScrolls();
  139 + return;
  140 + }
  141 + autoScroll(evt, this.options, getParentAutoScrollElement(elem, false), false);
  142 + }
  143 + }
  144 + };
  145 +
  146 + return Object.assign(AutoScroll, {
  147 + pluginName: 'scroll',
  148 + initializeByDefault: true
  149 + });
  150 +}
  151 +
  152 +function clearAutoScrolls() {
  153 + autoScrolls.forEach(function(autoScroll) {
  154 + clearInterval(autoScroll.pid);
  155 + });
  156 + autoScrolls = [];
  157 +}
  158 +
  159 +function clearPointerElemChangedInterval() {
  160 + clearInterval(pointerElemChangedInterval);
  161 +}
  162 +
  163 +
  164 +const autoScroll = throttle(function(evt, options, rootEl, isFallback) {
  165 + // Bug: https://bugzilla.mozilla.org/show_bug.cgi?id=505521
  166 + if (!options.scroll) return;
  167 + const x = (evt.touches ? evt.touches[0] : evt).clientX,
  168 + y = (evt.touches ? evt.touches[0] : evt).clientY,
  169 + sens = options.scrollSensitivity,
  170 + speed = options.scrollSpeed,
  171 + winScroller = getWindowScrollingElement();
  172 +
  173 + let scrollThisInstance = false,
  174 + scrollCustomFn;
  175 +
  176 + // New scroll root, set scrollEl
  177 + if (scrollRootEl !== rootEl) {
  178 + scrollRootEl = rootEl;
  179 +
  180 + clearAutoScrolls();
  181 +
  182 + scrollEl = options.scroll;
  183 + scrollCustomFn = options.scrollFn;
  184 +
  185 + if (scrollEl === true) {
  186 + scrollEl = getParentAutoScrollElement(rootEl, true);
  187 + }
  188 + }
  189 +
  190 +
  191 + let layersOut = 0;
  192 + let currentParent = scrollEl;
  193 + do {
  194 + let el = currentParent,
  195 + rect = getRect(el),
  196 +
  197 + top = rect.top,
  198 + bottom = rect.bottom,
  199 + left = rect.left,
  200 + right = rect.right,
  201 +
  202 + width = rect.width,
  203 + height = rect.height,
  204 +
  205 + canScrollX,
  206 + canScrollY,
  207 +
  208 + scrollWidth = el.scrollWidth,
  209 + scrollHeight = el.scrollHeight,
  210 +
  211 + elCSS = css(el),
  212 +
  213 + scrollPosX = el.scrollLeft,
  214 + scrollPosY = el.scrollTop;
  215 +
  216 +
  217 + if (el === winScroller) {
  218 + canScrollX = width < scrollWidth && (elCSS.overflowX === 'auto' || elCSS.overflowX === 'scroll' || elCSS.overflowX === 'visible');
  219 + canScrollY = height < scrollHeight && (elCSS.overflowY === 'auto' || elCSS.overflowY === 'scroll' || elCSS.overflowY === 'visible');
  220 + } else {
  221 + canScrollX = width < scrollWidth && (elCSS.overflowX === 'auto' || elCSS.overflowX === 'scroll');
  222 + canScrollY = height < scrollHeight && (elCSS.overflowY === 'auto' || elCSS.overflowY === 'scroll');
  223 + }
  224 +
  225 + let vx = canScrollX && (Math.abs(right - x) <= sens && (scrollPosX + width) < scrollWidth) - (Math.abs(left - x) <= sens && !!scrollPosX);
  226 + let vy = canScrollY && (Math.abs(bottom - y) <= sens && (scrollPosY + height) < scrollHeight) - (Math.abs(top - y) <= sens && !!scrollPosY);
  227 +
  228 +
  229 + if (!autoScrolls[layersOut]) {
  230 + for (let i = 0; i <= layersOut; i++) {
  231 + if (!autoScrolls[i]) {
  232 + autoScrolls[i] = {};
  233 + }
  234 + }
  235 + }
  236 +
  237 + if (autoScrolls[layersOut].vx != vx || autoScrolls[layersOut].vy != vy || autoScrolls[layersOut].el !== el) {
  238 + autoScrolls[layersOut].el = el;
  239 + autoScrolls[layersOut].vx = vx;
  240 + autoScrolls[layersOut].vy = vy;
  241 +
  242 + clearInterval(autoScrolls[layersOut].pid);
  243 +
  244 + if (vx != 0 || vy != 0) {
  245 + scrollThisInstance = true;
  246 + /* jshint loopfunc:true */
  247 + autoScrolls[layersOut].pid = setInterval((function () {
  248 + // emulate drag over during autoscroll (fallback), emulating native DnD behaviour
  249 + if (isFallback && this.layer === 0) {
  250 + Sortable.active._onTouchMove(touchEvt); // To move ghost if it is positioned absolutely
  251 + }
  252 + let scrollOffsetY = autoScrolls[this.layer].vy ? autoScrolls[this.layer].vy * speed : 0;
  253 + let scrollOffsetX = autoScrolls[this.layer].vx ? autoScrolls[this.layer].vx * speed : 0;
  254 +
  255 + if (typeof(scrollCustomFn) === 'function') {
  256 + if (scrollCustomFn.call(Sortable.dragged.parentNode[expando], scrollOffsetX, scrollOffsetY, evt, touchEvt, autoScrolls[this.layer].el) !== 'continue') {
  257 + return;
  258 + }
  259 + }
  260 +
  261 + scrollBy(autoScrolls[this.layer].el, scrollOffsetX, scrollOffsetY);
  262 + }).bind({layer: layersOut}), 24);
  263 + }
  264 + }
  265 + layersOut++;
  266 + } while (options.bubbleScroll && currentParent !== winScroller && (currentParent = getParentAutoScrollElement(currentParent, false)));
  267 + scrolling = scrollThisInstance; // in case another function catches scrolling as false in between when it is not
  268 +}, 30);
  269 +
  270 +export default AutoScrollPlugin;
  1 +## AutoScroll
  2 +This plugin allows for the page to automatically scroll during dragging near a scrollable element's edge on mobile devices and IE9 (or whenever fallback is enabled), and also enhances most browser's native drag-and-drop autoscrolling.
  3 +Demo:
  4 + - `window`: https://jsbin.com/dosilir/edit?js,output
  5 + - `overflow: hidden`: https://jsbin.com/xecihez/edit?html,js,output
  6 +
  7 +**This plugin is a default plugin, and is included in the default UMD and ESM builds of Sortable**
  8 +
  9 +
  10 +---
  11 +
  12 +
  13 +### Mounting
  14 +```js
  15 +import { Sortable, AutoScroll } from 'sortablejs';
  16 +
  17 +Sortable.mount(new AutoScroll());
  18 +```
  19 +
  20 +
  21 +---
  22 +
  23 +
  24 +### Options
  25 +
  26 +```js
  27 +new Sortable(el, {
  28 + scroll: true, // Enable the plugin. Can be HTMLElement.
  29 + scrollFn: function(offsetX, offsetY, originalEvent, touchEvt, hoverTargetEl) { ... }, // if you have custom scrollbar scrollFn may be used for autoscrolling
  30 + scrollSensitivity: 30, // px, how near the mouse must be to an edge to start scrolling.
  31 + scrollSpeed: 10, // px, speed of the scrolling
  32 + bubbleScroll: true // apply autoscroll to all parent elements, allowing for easier movement
  33 +});
  34 +```
  35 +
  36 +
  37 +---
  38 +
  39 +
  40 +#### `scroll` option
  41 +Enables the plugin. Defaults to `true`. May also be set to an HTMLElement which will be where autoscrolling is rooted.
  42 +
  43 +Demo:
  44 + - `window`: https://jsbin.com/dosilir/edit?js,output
  45 + - `overflow: hidden`: https://jsbin.com/xecihez/edit?html,js,output
  46 +
  47 +
  48 +---
  49 +
  50 +
  51 +#### `scrollFn` option
  52 +Defines function that will be used for autoscrolling. el.scrollTop/el.scrollLeft is used by default.
  53 +Useful when you have custom scrollbar with dedicated scroll function.
  54 +This function should return `'continue'` if it wishes to allow Sortable's native autoscrolling.
  55 +
  56 +
  57 +---
  58 +
  59 +
  60 +#### `scrollSensitivity` option
  61 +Defines how near the mouse must be to an edge to start scrolling.
  62 +
  63 +
  64 +---
  65 +
  66 +
  67 +#### `scrollSpeed` option
  68 +The speed at which the window should scroll once the mouse pointer gets within the `scrollSensitivity` distance.
  69 +
  70 +
  71 +---
  72 +
  73 +
  74 +#### `bubbleScroll` option
  75 +If set to `true`, the normal `autoscroll` function will also be applied to all parent elements of the element the user is dragging over.
  76 +
  77 +Demo: https://jsbin.com/kesewor/edit?html,js,output
  78 +
  79 +
  80 +---
  1 +export { default } from './AutoScroll.js';
  1 +import {
  2 + toggleClass,
  3 + getRect,
  4 + index,
  5 + closest,
  6 + on,
  7 + off,
  8 + clone,
  9 + css,
  10 + setRect,
  11 + unsetRect,
  12 + matrix,
  13 + expando
  14 +} from '../../src/utils.js';
  15 +
  16 +import dispatchEvent from '../../src/EventDispatcher.js';
  17 +
  18 +let multiDragElements = [],
  19 + multiDragClones = [],
  20 + lastMultiDragSelect, // for selection with modifier key down (SHIFT)
  21 + multiDragSortable,
  22 + initialFolding = false, // Initial multi-drag fold when drag started
  23 + folding = false, // Folding any other time
  24 + dragStarted = false,
  25 + dragEl,
  26 + clonesFromRect,
  27 + clonesHidden;
  28 +
  29 +function MultiDragPlugin() {
  30 + function MultiDrag(sortable) {
  31 + // Bind all private methods
  32 + for (let fn in this) {
  33 + if (fn.charAt(0) === '_' && typeof this[fn] === 'function') {
  34 + this[fn] = this[fn].bind(this);
  35 + }
  36 + }
  37 +
  38 + if (sortable.options.supportPointer) {
  39 + on(document, 'pointerup', this._deselectMultiDrag);
  40 + } else {
  41 + on(document, 'mouseup', this._deselectMultiDrag);
  42 + on(document, 'touchend', this._deselectMultiDrag);
  43 + }
  44 +
  45 + on(document, 'keydown', this._checkKeyDown);
  46 + on(document, 'keyup', this._checkKeyUp);
  47 +
  48 + this.defaults = {
  49 + selectedClass: 'sortable-selected',
  50 + multiDragKey: null,
  51 + setData(dataTransfer, dragEl) {
  52 + let data = '';
  53 + if (multiDragElements.length && multiDragSortable === sortable) {
  54 + multiDragElements.forEach((multiDragElement, i) => {
  55 + data += (!i ? '' : ', ') + multiDragElement.textContent;
  56 + });
  57 + } else {
  58 + data = dragEl.textContent;
  59 + }
  60 + dataTransfer.setData('Text', data);
  61 + }
  62 + };
  63 + }
  64 +
  65 + MultiDrag.prototype = {
  66 + multiDragKeyDown: false,
  67 + isMultiDrag: false,
  68 +
  69 +
  70 + delayStartGlobal({ dragEl: dragged }) {
  71 + dragEl = dragged;
  72 + },
  73 +
  74 + delayEnded() {
  75 + this.isMultiDrag = ~multiDragElements.indexOf(dragEl);
  76 + },
  77 +
  78 + setupClone({ sortable, cancel }) {
  79 + if (!this.isMultiDrag) return;
  80 + for (let i = 0; i < multiDragElements.length; i++) {
  81 + multiDragClones.push(clone(multiDragElements[i]));
  82 +
  83 + multiDragClones[i].sortableIndex = multiDragElements[i].sortableIndex;
  84 +
  85 + multiDragClones[i].draggable = false;
  86 + multiDragClones[i].style['will-change'] = '';
  87 +
  88 + toggleClass(multiDragClones[i], this.options.selectedClass, false);
  89 + multiDragElements[i] === dragEl && toggleClass(multiDragClones[i], this.options.chosenClass, false);
  90 + }
  91 +
  92 + sortable._hideClone();
  93 + cancel();
  94 + },
  95 +
  96 + clone({ sortable, rootEl, dispatchSortableEvent, cancel }) {
  97 + if (!this.isMultiDrag) return;
  98 + if (!this.options.removeCloneOnHide) {
  99 + if (multiDragElements.length && multiDragSortable === sortable) {
  100 + insertMultiDragClones(true, rootEl);
  101 + dispatchSortableEvent('clone');
  102 +
  103 + cancel();
  104 + }
  105 + }
  106 + },
  107 +
  108 + showClone({ cloneNowShown, rootEl, cancel }) {
  109 + if (!this.isMultiDrag) return;
  110 + insertMultiDragClones(false, rootEl);
  111 + multiDragClones.forEach(clone => {
  112 + css(clone, 'display', '');
  113 + });
  114 +
  115 + cloneNowShown();
  116 + clonesHidden = false;
  117 + cancel();
  118 + },
  119 +
  120 + hideClone({ sortable, cloneNowHidden, cancel }) {
  121 + if (!this.isMultiDrag) return;
  122 + multiDragClones.forEach(clone => {
  123 + css(clone, 'display', 'none');
  124 + if (this.options.removeCloneOnHide && clone.parentNode) {
  125 + clone.parentNode.removeChild(clone);
  126 + }
  127 + });
  128 +
  129 + cloneNowHidden();
  130 + clonesHidden = true;
  131 + cancel();
  132 + },
  133 +
  134 + dragStartGlobal({ sortable }) {
  135 + if (!this.isMultiDrag && multiDragSortable) {
  136 + multiDragSortable.multiDrag._deselectMultiDrag();
  137 + }
  138 +
  139 + multiDragElements.forEach(multiDragElement => {
  140 + multiDragElement.sortableIndex = index(multiDragElement);
  141 + });
  142 +
  143 + // Sort multi-drag elements
  144 + multiDragElements = multiDragElements.sort(function(a, b) {
  145 + return a.sortableIndex - b.sortableIndex;
  146 + });
  147 + dragStarted = true;
  148 + },
  149 +
  150 + dragStarted({ sortable }) {
  151 + if (!this.isMultiDrag) return;
  152 + if (this.options.sort) {
  153 + // Capture rects,
  154 + // hide multi drag elements (by positioning them absolute),
  155 + // set multi drag elements rects to dragRect,
  156 + // show multi drag elements,
  157 + // animate to rects,
  158 + // unset rects & remove from DOM
  159 +
  160 + sortable.captureAnimationState();
  161 +
  162 + if (this.options.animation) {
  163 + multiDragElements.forEach(multiDragElement => {
  164 + if (multiDragElement === dragEl) return;
  165 + css(multiDragElement, 'position', 'absolute');
  166 + });
  167 +
  168 + let dragRect = getRect(dragEl, false, true, true);
  169 +
  170 + multiDragElements.forEach(multiDragElement => {
  171 + if (multiDragElement === dragEl) return;
  172 + setRect(multiDragElement, dragRect);
  173 + });
  174 +
  175 + folding = true;
  176 + initialFolding = true;
  177 + }
  178 + }
  179 +
  180 + sortable.animateAll(() => {
  181 + folding = false;
  182 + initialFolding = false;
  183 +
  184 + if (this.options.animation) {
  185 + multiDragElements.forEach(multiDragElement => {
  186 + unsetRect(multiDragElement);
  187 + });
  188 + }
  189 +
  190 + // Remove all auxiliary multidrag items from el, if sorting enabled
  191 + if (this.options.sort) {
  192 + removeMultiDragElements();
  193 + }
  194 + });
  195 + },
  196 +
  197 + dragOver({ target, completed, cancel }) {
  198 + if (folding && ~multiDragElements.indexOf(target)) {
  199 + completed(false);
  200 + cancel();
  201 + }
  202 + },
  203 +
  204 + revert({ fromSortable, rootEl, sortable, dragRect }) {
  205 + if (multiDragElements.length > 1) {
  206 + // Setup unfold animation
  207 + multiDragElements.forEach(multiDragElement => {
  208 + sortable.addAnimationState({
  209 + target: multiDragElement,
  210 + rect: folding ? getRect(multiDragElement) : dragRect
  211 + });
  212 +
  213 + unsetRect(multiDragElement);
  214 +
  215 + multiDragElement.fromRect = dragRect;
  216 +
  217 + fromSortable.removeAnimationState(multiDragElement);
  218 + });
  219 + folding = false;
  220 + insertMultiDragElements(!this.options.removeCloneOnHide, rootEl);
  221 + }
  222 + },
  223 +
  224 + dragOverCompleted({ sortable, isOwner, insertion, activeSortable, parentEl, putSortable }) {
  225 + let options = this.options;
  226 + if (insertion) {
  227 + // Clones must be hidden before folding animation to capture dragRectAbsolute properly
  228 + if (isOwner) {
  229 + activeSortable._hideClone();
  230 + }
  231 +
  232 + initialFolding = false;
  233 + // If leaving sort:false root, or already folding - Fold to new location
  234 + if (options.animation && multiDragElements.length > 1 && (folding || !isOwner && !activeSortable.options.sort && !putSortable)) {
  235 + // Fold: Set all multi drag elements's rects to dragEl's rect when multi-drag elements are invisible
  236 + let dragRectAbsolute = getRect(dragEl, false, true, true);
  237 +
  238 + multiDragElements.forEach(multiDragElement => {
  239 + if (multiDragElement === dragEl) return;
  240 + setRect(multiDragElement, dragRectAbsolute);
  241 +
  242 + // Move element(s) to end of parentEl so that it does not interfere with multi-drag clones insertion if they are inserted
  243 + // while folding, and so that we can capture them again because old sortable will no longer be fromSortable
  244 + parentEl.appendChild(multiDragElement);
  245 + });
  246 +
  247 + folding = true;
  248 + }
  249 +
  250 + // Clones must be shown (and check to remove multi drags) after folding when interfering multiDragElements are moved out
  251 + if (!isOwner) {
  252 + // Only remove if not folding (folding will remove them anyways)
  253 + if (!folding) {
  254 + removeMultiDragElements();
  255 + }
  256 +
  257 + if (multiDragElements.length > 1) {
  258 + let clonesHiddenBefore = clonesHidden;
  259 + activeSortable._showClone(sortable);
  260 +
  261 + // Unfold animation for clones if showing from hidden
  262 + if (activeSortable.options.animation && !clonesHidden && clonesHiddenBefore) {
  263 + multiDragClones.forEach(clone => {
  264 + activeSortable.addAnimationState({
  265 + target: clone,
  266 + rect: clonesFromRect
  267 + });
  268 +
  269 + clone.fromRect = clonesFromRect;
  270 + clone.thisAnimationDuration = null;
  271 + });
  272 + }
  273 + } else {
  274 + activeSortable._showClone(sortable);
  275 + }
  276 + }
  277 + }
  278 + },
  279 +
  280 + dragOverAnimationCapture({ dragRect, isOwner, activeSortable }) {
  281 + multiDragElements.forEach(multiDragElement => {
  282 + multiDragElement.thisAnimationDuration = null;
  283 + });
  284 +
  285 + if (activeSortable.options.animation && !isOwner && activeSortable.multiDrag.isMultiDrag) {
  286 + clonesFromRect = Object.assign({}, dragRect);
  287 + let dragMatrix = matrix(dragEl, true);
  288 + clonesFromRect.top -= dragMatrix.f;
  289 + clonesFromRect.left -= dragMatrix.e;
  290 + }
  291 + },
  292 +
  293 + dragOverAnimationComplete() {
  294 + if (folding) {
  295 + folding = false;
  296 + removeMultiDragElements();
  297 + }
  298 + },
  299 +
  300 + drop({ originalEvent: evt, rootEl, parentEl, sortable, dispatchSortableEvent, oldIndex, putSortable }) {
  301 + let toSortable = (putSortable || this.sortable);
  302 +
  303 + if (!evt) return;
  304 +
  305 + let options = this.options,
  306 + children = parentEl.children;
  307 +
  308 + // Multi-drag selection
  309 + if (!dragStarted) {
  310 + if (options.multiDragKey && !this.multiDragKeyDown) {
  311 + this._deselectMultiDrag();
  312 + }
  313 + toggleClass(dragEl, options.selectedClass, !~multiDragElements.indexOf(dragEl));
  314 +
  315 + if (!~multiDragElements.indexOf(dragEl)) {
  316 + multiDragElements.push(dragEl);
  317 + dispatchEvent({
  318 + sortable,
  319 + rootEl,
  320 + name: 'select',
  321 + targetEl: dragEl,
  322 + originalEvt: evt
  323 + });
  324 +
  325 + // Modifier activated, select from last to dragEl
  326 + if (evt.shiftKey && lastMultiDragSelect && sortable.el.contains(lastMultiDragSelect)) {
  327 + let lastIndex = index(lastMultiDragSelect),
  328 + currentIndex = index(dragEl);
  329 +
  330 + if (~lastIndex && ~currentIndex && lastIndex !== currentIndex) {
  331 + // Must include lastMultiDragSelect (select it), in case modified selection from no selection
  332 + // (but previous selection existed)
  333 + let n, i;
  334 + if (currentIndex > lastIndex) {
  335 + i = lastIndex;
  336 + n = currentIndex;
  337 + } else {
  338 + i = currentIndex;
  339 + n = lastIndex + 1;
  340 + }
  341 +
  342 + for (; i < n; i++) {
  343 + if (~multiDragElements.indexOf(children[i])) continue;
  344 + toggleClass(children[i], options.selectedClass, true);
  345 + multiDragElements.push(children[i]);
  346 +
  347 + dispatchEvent({
  348 + sortable,
  349 + rootEl,
  350 + name: 'select',
  351 + targetEl: children[i],
  352 + originalEvt: evt
  353 + });
  354 + }
  355 + }
  356 + } else {
  357 + lastMultiDragSelect = dragEl;
  358 + }
  359 +
  360 + multiDragSortable = toSortable;
  361 + } else {
  362 + multiDragElements.splice(multiDragElements.indexOf(dragEl), 1);
  363 + lastMultiDragSelect = null;
  364 + dispatchEvent({
  365 + sortable,
  366 + rootEl,
  367 + name: 'deselect',
  368 + targetEl: dragEl,
  369 + originalEvt: evt
  370 + });
  371 + }
  372 + }
  373 +
  374 + // Multi-drag drop
  375 + if (dragStarted && this.isMultiDrag) {
  376 + // Do not "unfold" after around dragEl if reverted
  377 + if ((parentEl[expando].options.sort || parentEl !== rootEl) && multiDragElements.length > 1) {
  378 + let dragRect = getRect(dragEl),
  379 + multiDragIndex = index(dragEl, ':not(.' + this.options.selectedClass + ')');
  380 +
  381 + if (!initialFolding && options.animation) dragEl.thisAnimationDuration = null;
  382 +
  383 + toSortable.captureAnimationState();
  384 +
  385 + if (!initialFolding) {
  386 + if (options.animation) {
  387 + dragEl.fromRect = dragRect;
  388 + multiDragElements.forEach(multiDragElement => {
  389 + multiDragElement.thisAnimationDuration = null;
  390 + if (multiDragElement !== dragEl) {
  391 + let rect = folding ? getRect(multiDragElement) : dragRect;
  392 + multiDragElement.fromRect = rect;
  393 +
  394 + // Prepare unfold animation
  395 + toSortable.addAnimationState({
  396 + target: multiDragElement,
  397 + rect: rect
  398 + });
  399 + }
  400 + });
  401 + }
  402 +
  403 + // Multi drag elements are not necessarily removed from the DOM on drop, so to reinsert
  404 + // properly they must all be removed
  405 + removeMultiDragElements();
  406 +
  407 + multiDragElements.forEach(multiDragElement => {
  408 + if (children[multiDragIndex]) {
  409 + parentEl.insertBefore(multiDragElement, children[multiDragIndex]);
  410 + } else {
  411 + parentEl.appendChild(multiDragElement);
  412 + }
  413 + multiDragIndex++;
  414 + });
  415 +
  416 + // If initial folding is done, the elements may have changed position because they are now
  417 + // unfolding around dragEl, even though dragEl may not have his index changed, so update event
  418 + // must be fired here as Sortable will not.
  419 + if (oldIndex === index(dragEl)) {
  420 + let update = false;
  421 + multiDragElements.forEach(multiDragElement => {
  422 + if (multiDragElement.sortableIndex !== index(multiDragElement)) {
  423 + update = true;
  424 + return;
  425 + }
  426 + });
  427 +
  428 + if (update) {
  429 + dispatchSortableEvent('update');
  430 + }
  431 + }
  432 + }
  433 +
  434 + // Must be done after capturing individual rects (scroll bar)
  435 + multiDragElements.forEach(multiDragElement => {
  436 + unsetRect(multiDragElement);
  437 + });
  438 +
  439 + toSortable.animateAll();
  440 + }
  441 +
  442 + multiDragSortable = toSortable;
  443 + }
  444 +
  445 + // Remove clones if necessary
  446 + if (rootEl === parentEl || (putSortable && putSortable.lastPutMode !== 'clone')) {
  447 + multiDragClones.forEach(clone => {
  448 + clone.parentNode && clone.parentNode.removeChild(clone);
  449 + });
  450 + }
  451 + },
  452 +
  453 + nullingGlobal() {
  454 + this.isMultiDrag =
  455 + dragStarted = false;
  456 + multiDragClones.length = 0;
  457 + },
  458 +
  459 + destroyGlobal() {
  460 + this._deselectMultiDrag();
  461 + off(document, 'pointerup', this._deselectMultiDrag);
  462 + off(document, 'mouseup', this._deselectMultiDrag);
  463 + off(document, 'touchend', this._deselectMultiDrag);
  464 +
  465 + off(document, 'keydown', this._checkKeyDown);
  466 + off(document, 'keyup', this._checkKeyUp);
  467 + },
  468 +
  469 + _deselectMultiDrag(evt) {
  470 + if (typeof dragStarted !== "undefined" && dragStarted) return;
  471 +
  472 + // Only deselect if selection is in this sortable
  473 + if (multiDragSortable !== this.sortable) return;
  474 +
  475 + // Only deselect if target is not item in this sortable
  476 + if (evt && closest(evt.target, this.options.draggable, this.sortable.el, false)) return;
  477 +
  478 + // Only deselect if left click
  479 + if (evt && evt.button !== 0) return;
  480 +
  481 + while (multiDragElements.length) {
  482 + let el = multiDragElements[0];
  483 + toggleClass(el, this.options.selectedClass, false);
  484 + multiDragElements.shift();
  485 + dispatchEvent({
  486 + sortable: this.sortable,
  487 + rootEl: this.sortable.el,
  488 + name: 'deselect',
  489 + targetEl: el,
  490 + originalEvt: evt
  491 + });
  492 + }
  493 + },
  494 +
  495 + _checkKeyDown(evt) {
  496 + if (evt.key === this.options.multiDragKey) {
  497 + this.multiDragKeyDown = true;
  498 + }
  499 + },
  500 +
  501 + _checkKeyUp(evt) {
  502 + if (evt.key === this.options.multiDragKey) {
  503 + this.multiDragKeyDown = false;
  504 + }
  505 + }
  506 + };
  507 +
  508 + return Object.assign(MultiDrag, {
  509 + // Static methods & properties
  510 + pluginName: 'multiDrag',
  511 + utils: {
  512 + /**
  513 + * Selects the provided multi-drag item
  514 + * @param {HTMLElement} el The element to be selected
  515 + */
  516 + select(el) {
  517 + let sortable = el.parentNode[expando];
  518 + if (!sortable || !sortable.options.multiDrag || ~multiDragElements.indexOf(el)) return;
  519 + if (multiDragSortable && multiDragSortable !== sortable) {
  520 + multiDragSortable.multiDrag._deselectMultiDrag();
  521 + multiDragSortable = sortable;
  522 + }
  523 + toggleClass(el, sortable.options.selectedClass, true);
  524 + multiDragElements.push(el);
  525 + },
  526 + /**
  527 + * Deselects the provided multi-drag item
  528 + * @param {HTMLElement} el The element to be deselected
  529 + */
  530 + deselect(el) {
  531 + let sortable = el.parentNode[expando],
  532 + index = multiDragElements.indexOf(el);
  533 + if (!sortable || !sortable.options.multiDrag || !~index) return;
  534 + toggleClass(el, sortable.options.selectedClass, false);
  535 + multiDragElements.splice(index, 1);
  536 + }
  537 + },
  538 + eventProperties() {
  539 + const oldIndicies = [],
  540 + newIndicies = [];
  541 +
  542 + multiDragElements.forEach(multiDragElement => {
  543 + oldIndicies.push({
  544 + multiDragElement,
  545 + index: multiDragElement.sortableIndex
  546 + });
  547 +
  548 + // multiDragElements will already be sorted if folding
  549 + let newIndex;
  550 + if (folding && multiDragElement !== dragEl) {
  551 + newIndex = -1;
  552 + } else if (folding) {
  553 + newIndex = index(multiDragElement, ':not(.' + this.options.selectedClass + ')');
  554 + } else {
  555 + newIndex = index(multiDragElement);
  556 + }
  557 + newIndicies.push({
  558 + multiDragElement,
  559 + index: newIndex
  560 + });
  561 + });
  562 + return {
  563 + items: [...multiDragElements],
  564 + clones: [...multiDragClones],
  565 + oldIndicies,
  566 + newIndicies
  567 + };
  568 + },
  569 + optionListeners: {
  570 + multiDragKey(key) {
  571 + key = key.toLowerCase();
  572 + if (key === 'ctrl') {
  573 + key = 'Control';
  574 + } else if (key.length > 1) {
  575 + key = key.charAt(0).toUpperCase() + key.substr(1);
  576 + }
  577 + return key;
  578 + }
  579 + }
  580 + });
  581 +}
  582 +
  583 +function insertMultiDragElements(clonesInserted, rootEl) {
  584 + multiDragElements.forEach((multiDragElement, i) => {
  585 + let target = rootEl.children[multiDragElement.sortableIndex + (clonesInserted ? Number(i) : 0)];
  586 + if (target) {
  587 + rootEl.insertBefore(multiDragElement, target);
  588 + } else {
  589 + rootEl.appendChild(multiDragElement);
  590 + }
  591 + });
  592 +}
  593 +
  594 +/**
  595 + * Insert multi-drag clones
  596 + * @param {[Boolean]} elementsInserted Whether the multi-drag elements are inserted
  597 + * @param {HTMLElement} rootEl
  598 + */
  599 +function insertMultiDragClones(elementsInserted, rootEl) {
  600 + multiDragClones.forEach((clone, i) => {
  601 + let target = rootEl.children[clone.sortableIndex + (elementsInserted ? Number(i) : 0)];
  602 + if (target) {
  603 + rootEl.insertBefore(clone, target);
  604 + } else {
  605 + rootEl.appendChild(clone);
  606 + }
  607 + });
  608 +}
  609 +
  610 +function removeMultiDragElements() {
  611 + multiDragElements.forEach(multiDragElement => {
  612 + if (multiDragElement === dragEl) return;
  613 + multiDragElement.parentNode && multiDragElement.parentNode.removeChild(multiDragElement);
  614 + });
  615 +}
  616 +
  617 +export default MultiDragPlugin;
  1 +## MultiDrag Plugin
  2 +This plugin allows users to select multiple items within a sortable at once, and drag them as one item.
  3 +Once placed, the items will unfold into their original order, but all beside each other at the new position.
  4 +[Read More](https://github.com/SortableJS/Sortable/wiki/Dragging-Multiple-Items-in-Sortable)
  5 +
  6 +Demo: https://jsbin.com/wopavom/edit?js,output
  7 +
  8 +
  9 +---
  10 +
  11 +
  12 +### Mounting
  13 +```js
  14 +import { Sortable, MultiDrag } from 'sortablejs';
  15 +
  16 +Sortable.mount(new MultiDrag());
  17 +```
  18 +
  19 +
  20 +---
  21 +
  22 +
  23 +### Options
  24 +
  25 +```js
  26 +new Sortable(el, {
  27 + multiDrag: true, // Enable the plugin
  28 + selectedClass: "sortable-selected", // Class name for selected item
  29 + multiDragKey: null, // Key that must be down for items to be selected
  30 +
  31 + // Called when an item is selected
  32 + onSelect: function(/**Event*/evt) {
  33 + evt.item // The selected item
  34 + },
  35 +
  36 + // Called when an item is deselected
  37 + onDeselect: function(/**Event*/evt) {
  38 + evt.item // The deselected item
  39 + }
  40 +});
  41 +```
  42 +
  43 +
  44 +---
  45 +
  46 +
  47 +#### `multiDragKey` option
  48 +The key that must be down for multiple items to be selected. The default is `null`, meaning no key must be down.
  49 +For special keys, such as the <kbd>CTRL</kbd> key, simply specify the option as `'CTRL'` (casing does not matter).
  50 +
  51 +
  52 +---
  53 +
  54 +
  55 +#### `selectedClass` option
  56 +Class name for the selected item(s) if multiDrag is enabled. Defaults to `sortable-selected`.
  57 +
  58 +```css
  59 +.selected {
  60 + background-color: #f9c7c8;
  61 + border: solid red 1px;
  62 +}
  63 +```
  64 +
  65 +```js
  66 +Sortable.create(list, {
  67 + multiDrag: true,
  68 + selectedClass: "selected"
  69 +});
  70 +```
  71 +
  72 +
  73 +---
  74 +
  75 +
  76 +### Event Properties
  77 + - items:`HTMLElement[]` — Array of selected items, or empty
  78 + - clones:`HTMLElement[]` — Array of clones, or empty
  79 + - oldIndicies:`Index[]` — Array containing information on the old indicies of the selected elements.
  80 + - newIndicies:`Index[]` — Array containing information on the new indicies of the selected elements.
  81 +
  82 +#### Index Object
  83 + - element:`HTMLElement` — The element whose index is being given
  84 + - index:`Number` — The index of the element
  85 +
  86 +#### Note on `newIndicies`
  87 +For any event that is fired during sorting, the index of any selected element that is not the main dragged element is given as `-1`.
  88 +This is because it has either been removed from the DOM, or because it is in a folding animation (folding to the dragged element) and will be removed after this animation is complete.
  89 +
  90 +
  91 +---
  92 +
  93 +
  94 +### Sortable.utils
  95 +* select(el:`HTMLElement`) — select the given multi-drag item
  96 +* deselect(el:`HTMLElement`) — deselect the given multi-drag item
  1 +export { default } from './MultiDrag.js';
  1 +import { getChild } from '../../src/utils.js';
  2 +
  3 +
  4 +const drop = function({
  5 + originalEvent,
  6 + putSortable,
  7 + dragEl,
  8 + activeSortable,
  9 + dispatchSortableEvent,
  10 + hideGhostForTarget,
  11 + unhideGhostForTarget
  12 +}) {
  13 + if (!originalEvent) return;
  14 + let toSortable = putSortable || activeSortable;
  15 + hideGhostForTarget();
  16 + let touch = originalEvent.changedTouches && originalEvent.changedTouches.length ? originalEvent.changedTouches[0] : originalEvent;
  17 + let target = document.elementFromPoint(touch.clientX, touch.clientY);
  18 + unhideGhostForTarget();
  19 + if (toSortable && !toSortable.el.contains(target)) {
  20 + dispatchSortableEvent('spill');
  21 + this.onSpill({ dragEl, putSortable });
  22 + }
  23 +};
  24 +
  25 +function Revert() {}
  26 +
  27 +Revert.prototype = {
  28 + startIndex: null,
  29 + dragStart({ oldDraggableIndex }) {
  30 + this.startIndex = oldDraggableIndex;
  31 + },
  32 + onSpill({ dragEl, putSortable }) {
  33 + this.sortable.captureAnimationState();
  34 + if (putSortable) {
  35 + putSortable.captureAnimationState();
  36 + }
  37 + let nextSibling = getChild(this.sortable.el, this.startIndex, this.options);
  38 +
  39 + if (nextSibling) {
  40 + this.sortable.el.insertBefore(dragEl, nextSibling);
  41 + } else {
  42 + this.sortable.el.appendChild(dragEl);
  43 + }
  44 + this.sortable.animateAll();
  45 + if (putSortable) {
  46 + putSortable.animateAll();
  47 + }
  48 + },
  49 + drop
  50 +};
  51 +
  52 +Object.assign(Revert, {
  53 + pluginName: 'revertOnSpill'
  54 +});
  55 +
  56 +
  57 +function Remove() {}
  58 +
  59 +Remove.prototype = {
  60 + onSpill({ dragEl, putSortable }) {
  61 + const parentSortable = putSortable || this.sortable;
  62 + parentSortable.captureAnimationState();
  63 + dragEl.parentNode && dragEl.parentNode.removeChild(dragEl);
  64 + parentSortable.animateAll();
  65 + },
  66 + drop
  67 +};
  68 +
  69 +Object.assign(Remove, {
  70 + pluginName: 'removeOnSpill'
  71 +});
  72 +
  73 +
  74 +export default [Remove, Revert];
  75 +
  76 +export {
  77 + Remove as RemoveOnSpill,
  78 + Revert as RevertOnSpill
  79 +};
  1 +# OnSpill Plugins
  2 +This file contains two seperate plugins, RemoveOnSpill and RevertOnSpill. They can be imported individually, or the default export (an array of both plugins) can be passed to `Sortable.mount` as well.
  3 +
  4 +**These plugins are default plugins, and are included in the default UMD and ESM builds of Sortable**
  5 +
  6 +
  7 +---
  8 +
  9 +
  10 +### Mounting
  11 +```js
  12 +import { Sortable, OnSpill } from 'sortablejs/modular/sortable.core.esm';
  13 +
  14 +Sortable.mount(OnSpill);
  15 +```
  16 +
  17 +
  18 +---
  19 +
  20 +
  21 +## RevertOnSpill Plugin
  22 +This plugin, when enabled, will cause the dragged item to be reverted to it's original position if it is spilled (ie. it is dropped outside of a valid Sortable drop target)
  23 +
  24 +
  25 +
  26 +
  27 +### Options
  28 +
  29 +```js
  30 +new Sortable(el, {
  31 + revertOnSpill: true, // Enable plugin
  32 + // Called when item is spilled
  33 + onSpill: function(/**Event*/evt) {
  34 + evt.item // The spilled item
  35 + }
  36 +});
  37 +```
  38 +
  39 +
  40 +---
  41 +
  42 +
  43 +## RemoveOnSpill Plugin
  44 +This plugin, when enabled, will cause the dragged item to be removed from the DOM if it is spilled (ie. it is dropped outside of a valid Sortable drop target)
  45 +
  46 +
  47 +---
  48 +
  49 +
  50 +### Options
  51 +
  52 +```js
  53 +new Sortable(el, {
  54 + removeOnSpill: true, // Enable plugin
  55 + // Called when item is spilled
  56 + onSpill: function(/**Event*/evt) {
  57 + evt.item // The spilled item
  58 + }
  59 +});
  60 +```
  1 +export { default, RemoveOnSpill, RevertOnSpill } from './OnSpill.js';
  1 +# Creating Sortable Plugins
  2 +Sortable plugins are plugins that can be directly mounted to the Sortable class. They are a powerful way of modifying the default behaviour of Sortable beyond what simply using events alone allows. To mount your plugin to Sortable, it must pass a constructor function to the `Sortable.mount` function. This constructor function will be called (with the `new` keyword in front of it) whenever a Sortable instance with your plugin enabled is initialized. The constructor function will be called with the parameters `sortable` and `el`, which is the HTMLElement that the Sortable is being initialized on. This means that there will be a new instance of your plugin each time it is enabled in a Sortable.
  3 +
  4 +
  5 +## Constructor Parameters
  6 +
  7 +`sortable: Sortable` — The sortable that the plugin is being initialized on
  8 +
  9 +`el: HTMLElement` — The element that the sortable is being initialized on
  10 +
  11 +`options: Object` — The options object that the user has passed into Sortable (not merged with defaults yet)
  12 +
  13 +
  14 +## Static Properties
  15 +The constructor function passed to `Sortable.mount` may contain several static properties and methods. The following static properties may be defined:
  16 +
  17 +`pluginName: String` (Required)
  18 +The name of the option that the user will use in their sortable's options to enable the plugin. Should start with a lower case and be camel-cased. For example: `'multiDrag'`. This is also the property name that the plugin's instance will be under in a sortable instance (ex. `sortableInstance.multiDrag`).
  19 +
  20 +`utils: Object`
  21 +Object containing functions that will be added to the `Sortable.utils` static object on the Sortable class.
  22 +
  23 +`eventOptions(eventName: String): Function`
  24 +A function that is called whenever Sortable fires an event. This function should return an object to be combined with the event object that Sortable will emit. The function will be called in the context of the instance of the plugin on the Sortable that is firing the event (ie. the `this` keyword will be the plugin instance).
  25 +
  26 +`initializeByDefault: Boolean`
  27 +Determines whether or not the plugin will always be initialized on every new Sortable instance. If this option is enabled, it does not mean that by default the plugin will be enabled on the Sortable - this must still be done in the options via the plugin's `pluginName`, or it can be enabled by default if your plugin specifies it's pluginName as a default option that is truthy. Since the plugin will already be initialized on every Sortable instance, it can also be enabled dynamically via `sortableInstance.option('pluginName', true)`.
  28 +It is a good idea to have this option set to `false` if the plugin modifies the behaviour of Sortable in such a way that enabling or disabling the plugin dynamically could cause it to break. Likewise, this option should be disabled if the plugin should only be instantiated on Sortables in which that plugin is enabled.
  29 +This option defaults to `true`.
  30 +
  31 +`optionListeners: Object`
  32 +An object that may contain event listeners that are fired when a specific option is updated.
  33 +These listeners are useful because the user's provided options are not necessarily unchanging once the plugin is initialized, and could be changed dynamically via the `option()` method.
  34 +The listener will be fired in the context of the instance of the plugin that it is being changed in (ie. the `this` keyword will be the instance of your plugin).
  35 +The name of the method should match the name of the option it listens for. The new value of the option will be passed in as an argument, and any returned value will be what the option is stored as. If no value is returned, the option will be stored as the value the user provided.
  36 +
  37 +Example:
  38 +
  39 +```js
  40 +Plugin.name = 'generateTitle';
  41 +Plugin.optionListeners = {
  42 + // Listen for option 'generateTitle'
  43 + generateTitle: function(title) {
  44 + // Store the option in all caps
  45 + return title.toUpperCase();
  46 +
  47 + // OR save it to this instance of your plugin as a private field.
  48 + // This way it can be accessed in events, but will not modify the user's options.
  49 + this.titleAllCaps = title.toUpperCase();
  50 + }
  51 +};
  52 +
  53 +```
  54 +
  55 +## Plugin Options
  56 +Plugins may have custom default options or may override the defaults of other options. In order to do this, there must be a `defaults` object on the initialized plugin. This can be set in the plugin's prototype, or during the initialization of the plugin (when the `el` is available). For example:
  57 +
  58 +```js
  59 +function myPlugin(sortable, el, options) {
  60 + this.defaults = {
  61 + color: el.style.backgroundColor
  62 + };
  63 +}
  64 +
  65 +Sortable.mount(myPlugin);
  66 +```
  67 +
  68 +
  69 +## Plugin Events
  70 +
  71 +### Context
  72 +The events will be fired in the context of their own parent object (ie. context is not changed), however the plugin instance's Sortable instance is available under `this.sortable`. Likewise, the options are available under `this.options`.
  73 +
  74 +### Event List
  75 +The following table contains details on the events that a plugin may handle in the prototype of the plugin's constructor function.
  76 +
  77 +| Event Name | Description | Cancelable? | Cancel Behaviour | Event Type | Custom Event Object Properties |
  78 +|---------------------------|------------------------------------------------------------------------------------------------------------------|-------------|----------------------------------------------------|------------|-------------------------------------------------------------------------|
  79 +| filter | Fired when the element is filtered, and dragging is therefore canceled | No | - | Normal | None |
  80 +| delayStart | Fired when the delay starts, even if there is no delay | Yes | Cancels sorting | Normal | None |
  81 +| delayEnded | Fired when the delay ends, even if there is no delay | Yes | Cancels sorting | Normal | None |
  82 +| setupClone | Fired when Sortable clones the dragged element | Yes | Cancels normal clone setup | Normal | None |
  83 +| dragStart | Fired when the dragging is first started | Yes | Cancels sorting | Normal | None |
  84 +| clone | Fired when the clone is inserted into the DOM (if `removeCloneOnHide: false`). Tick after dragStart. | Yes | Cancels normal clone insertion & hiding | Normal | None |
  85 +| dragStarted | Fired tick after dragStart | No | - | Normal | None |
  86 +| dragOver | Fired when the user drags over a sortable | Yes | Cancels normal dragover behaviour | DragOver | None |
  87 +| dragOverValid | Fired when the user drags over a sortable that the dragged item can be inserted into | Yes | Cancels normal valid dragover behaviour | DragOver | None |
  88 +| revert | Fired when the dragged item is reverted to it's original position when entering it's `sort:false` root | Yes | Cancels normal reverting, but is still completed() | DragOver | None |
  89 +| dragOverCompleted | Fired when dragOver is completed (ie. bubbling is disabled). To check if inserted, use `inserted` even property. | No | - | DragOver | `insertion: Boolean` — Whether or not the dragged element was inserted |
  90 +| dragOverAnimationCapture | Fired right before the animation state is captured in dragOver | No | - | DragOver | None |
  91 +| dragOverAnimationComplete | Fired after the animation is completed after a dragOver insertion | No | - | DragOver | None |
  92 +| drop | Fired on drop | Yes | Cancels normal drop behavior | Normal | None |
  93 +| nulling | Fired when the plugin should preform cleanups, once all drop events have fired | No | - | Normal | None |
  94 +| destroy | Fired when Sortable is destroyed | No | - | Normal | None |
  95 +
  96 +### Global Events
  97 +Normally, an event will only be fired in a plugin if the plugin is enabled on the Sortable from which the event is being fired. However, it sometimes may be desirable for a plugin to listen in on an event from Sortables in which it is not enabled on. This is possible with global events. For an event to be global, simply add the suffix 'Global' to the event's name (casing matters) (eg. `dragStartGlobal`).
  98 +Please note that your plugin must be initialized on any Sortable from which it expects to recieve events, and that includes global events. In other words, you will want to keep the `initializeByDefault` option as it's default `true` value if your plugin needs to recieve events from Sortables it is not enabled on.
  99 +Please also note that if both normal and global event handlers are set, the global event handler will always be fired before the regular one.
  100 +
  101 +### Event Object
  102 +An object with the following properties is passed as an argument to each plugin event when it is fired.
  103 +
  104 +#### Properties:
  105 +
  106 +`dragEl: HTMLElement` — The element being dragged
  107 +
  108 +`parentEl: HTMLElement` — The element that the dragged element is currently in
  109 +
  110 +`ghostEl: HTMLElement|undefined` — If using fallback, the element dragged under the cursor (undefined until after `dragStarted` plugin event)
  111 +
  112 +`rootEl: HTMLElement` — The element that the dragged element originated from
  113 +
  114 +`nextEl: HTMLElement` — The original next sibling of dragEl
  115 +
  116 +`cloneEl: HTMLElement|undefined` — The clone element (undefined until after `setupClone` plugin event)
  117 +
  118 +`cloneHidden: Boolean` — Whether or not the clone is hidden
  119 +
  120 +`dragStarted: Boolean` — Boolean indicating whether or not the dragStart event has fired
  121 +
  122 +`putSortable: Sortable|undefined` — The element that dragEl is dragged into from it's root, otherwise undefined
  123 +
  124 +`activeSortable: Sortable` — The active Sortable instance
  125 +
  126 +`originalEvent: Event` — The original HTML event corresponding to the Sortable event
  127 +
  128 +`oldIndex: Number` — The old index of dragEl
  129 +
  130 +`oldDraggableIndex: Number` — The old index of dragEl, only counting draggable elements
  131 +
  132 +`newIndex: Number` — The new index of dragEl
  133 +
  134 +`newDraggableIndex: Number` — The new index of dragEl, only counting draggable elements
  135 +
  136 +
  137 +#### Methods:
  138 +
  139 +`cloneNowHidden()` — Function to be called if the plugin has hidden the clone
  140 +
  141 +`cloneNowShown()` — Function to be called if the plugin has shown the clone
  142 +
  143 +`hideGhostForTarget()` — Hides the fallback ghost element if CSS pointer-events are not available. Call this before using document.elementFromPoint at the mouse position.
  144 +
  145 +`unhideGhostForTarget()` — Unhides the ghost element. To be called after `hideGhostForTarget()`.
  146 +
  147 +`dispatchSortableEvent(eventName: String)` — Function that can be used to emit an event on the current sortable while sorting, with all usual event properties set (eg. indexes, rootEl, cloneEl, originalEvent, etc.).
  148 +
  149 +
  150 +### DragOverEvent Object
  151 +This event is passed to dragover events, and extends the normal event object.
  152 +
  153 +#### Properties:
  154 +
  155 +`isOwner: Boolean` — Whether or not the dragged over sortable currently contains the dragged element
  156 +
  157 +`axis: String` — Direction of the dragged over sortable, `'vertical'` or `'horizontal'`
  158 +
  159 +`revert: Boolean` — Whether or not the dragged element is being reverted to it's original position from another position
  160 +
  161 +`dragRect: DOMRect` — DOMRect of the dragged element
  162 +
  163 +`targetRect: DOMRect` — DOMRect of the target element
  164 +
  165 +`canSort: Boolean` — Whether or not sorting is enabled in the dragged over sortable
  166 +
  167 +`fromSortable: Sortable` — The sortable that the dragged element is coming from
  168 +
  169 +`target: HTMLElement` — The sortable item that is being dragged over
  170 +
  171 +
  172 +#### Methods:
  173 +
  174 +`onMove(target: HTMLElement, after: Boolean): Boolean|Number` — Calls the `onMove` function the user specified in the options
  175 +
  176 +`changed()` — Fires the `onChange` event with event properties preconfigured
  177 +
  178 +`completed(insertion: Boolean)` — Should be called when dragover has "completed", meaning bubbling should be stopped. If `insertion` is `true`, Sortable will treat it as if the dragged element was inserted into the sortable, and hide/show clone, set ghost class, animate, etc.
  1 +## Swap Plugin
  2 +This plugin modifies the behaviour of Sortable to allow for items to be swapped with eachother rather than sorted. Once dragging starts, the user can drag over other items and there will be no change in the elements. However, the item that the user drops on will be swapped with the originally dragged item.
  3 +
  4 +Demo: https://jsbin.com/yejehog/edit?html,js,output
  5 +
  6 +
  7 +---
  8 +
  9 +
  10 +### Mounting
  11 +```js
  12 +import { Sortable, Swap } from 'sortablejs/modular/sortable.core.esm';
  13 +
  14 +Sortable.mount(new Swap());
  15 +```
  16 +
  17 +
  18 +---
  19 +
  20 +
  21 +### Options
  22 +
  23 +```js
  24 +new Sortable(el, {
  25 + swap: true, // Enable swap mode
  26 + swapClass: "sortable-swap-highlight" // Class name for swap item (if swap mode is enabled)
  27 +});
  28 +```
  29 +
  30 +
  31 +---
  32 +
  33 +
  34 +#### `swapClass` option
  35 +Class name for the item to be swapped with, if swap mode is enabled. Defaults to `sortable-swap-highlight`.
  36 +
  37 +```css
  38 +.highlighted {
  39 + background-color: #9AB6F1;
  40 +}
  41 +```
  42 +
  43 +```js
  44 +Sortable.create(list, {
  45 + swap: true,
  46 + swapClass: "highlighted"
  47 +});
  48 +```
  49 +
  50 +
  51 +---
  52 +
  53 +
  54 +### Event Properties
  55 + - swapItem:`HTMLElement|undefined` — The element that the dragged element was swapped with
  1 +import {
  2 + toggleClass,
  3 + index
  4 +} from '../../src/utils.js';
  5 +
  6 +let lastSwapEl;
  7 +
  8 +
  9 +function SwapPlugin() {
  10 + function Swap() {
  11 + this.defaults = {
  12 + swapClass: 'sortable-swap-highlight'
  13 + };
  14 + }
  15 +
  16 + Swap.prototype = {
  17 + dragStart({ dragEl }) {
  18 + lastSwapEl = dragEl;
  19 + },
  20 + dragOverValid({ completed, target, onMove, activeSortable, changed, cancel }) {
  21 + if (!activeSortable.options.swap) return;
  22 + let el = this.sortable.el,
  23 + options = this.options;
  24 + if (target && target !== el) {
  25 + let prevSwapEl = lastSwapEl;
  26 + if (onMove(target) !== false) {
  27 + toggleClass(target, options.swapClass, true);
  28 + lastSwapEl = target;
  29 + } else {
  30 + lastSwapEl = null;
  31 + }
  32 +
  33 + if (prevSwapEl && prevSwapEl !== lastSwapEl) {
  34 + toggleClass(prevSwapEl, options.swapClass, false);
  35 + }
  36 + }
  37 + changed();
  38 +
  39 + completed(true);
  40 + cancel();
  41 + },
  42 + drop({ activeSortable, putSortable, dragEl }) {
  43 + let toSortable = (putSortable || this.sortable);
  44 + let options = this.options;
  45 + lastSwapEl && toggleClass(lastSwapEl, options.swapClass, false);
  46 + if (lastSwapEl && (options.swap || putSortable && putSortable.options.swap)) {
  47 + if (dragEl !== lastSwapEl) {
  48 + toSortable.captureAnimationState();
  49 + if (toSortable !== activeSortable) activeSortable.captureAnimationState();
  50 + swapNodes(dragEl, lastSwapEl);
  51 +
  52 + toSortable.animateAll();
  53 + if (toSortable !== activeSortable) activeSortable.animateAll();
  54 + }
  55 + }
  56 + },
  57 + nulling() {
  58 + lastSwapEl = null;
  59 + }
  60 + };
  61 +
  62 + return Object.assign(Swap, {
  63 + pluginName: 'swap',
  64 + eventProperties() {
  65 + return {
  66 + swapItem: lastSwapEl
  67 + };
  68 + }
  69 + });
  70 +}
  71 +
  72 +
  73 +function swapNodes(n1, n2) {
  74 + let p1 = n1.parentNode,
  75 + p2 = n2.parentNode,
  76 + i1, i2;
  77 +
  78 + if (!p1 || !p2 || p1.isEqualNode(n2) || p2.isEqualNode(n1)) return;
  79 +
  80 + i1 = index(n1);
  81 + i2 = index(n2);
  82 +
  83 + if (p1.isEqualNode(p2) && i1 < i2) {
  84 + i2++;
  85 + }
  86 + p1.insertBefore(n2, p1.children[i1]);
  87 + p2.insertBefore(n1, p2.children[i2]);
  88 +}
  89 +
  90 +export default SwapPlugin;
  1 +export { default } from './Swap.js';
  1 +import { version } from '../package.json';
  2 +
  3 +export default `/**!
  4 + * Sortable ${ version }
  5 + * @author RubaXa <trash@rubaxa.org>
  6 + * @author owenm <owen23355@gmail.com>
  7 + * @license MIT
  8 + */`;
  1 +import babel from 'rollup-plugin-babel';
  2 +import json from 'rollup-plugin-json';
  3 +import resolve from 'rollup-plugin-node-resolve';
  4 +import banner from './banner.js';
  5 +
  6 +
  7 +export default {
  8 + output: {
  9 + banner,
  10 + name: 'Sortable'
  11 + },
  12 + plugins: [
  13 + json(),
  14 + babel(),
  15 + resolve()
  16 + ]
  17 +};
  1 +import build from './build.js';
  2 +
  3 +export default ([
  4 + {
  5 + input: 'entry/entry-core.js',
  6 + output: Object.assign({}, build.output, {
  7 + file: 'modular/sortable.core.esm.js',
  8 + format: 'esm'
  9 + })
  10 + },
  11 + {
  12 + input: 'entry/entry-defaults.js',
  13 + output: Object.assign({}, build.output, {
  14 + file: 'modular/sortable.esm.js',
  15 + format: 'esm'
  16 + })
  17 + },
  18 + {
  19 + input: 'entry/entry-complete.js',
  20 + output: Object.assign({}, build.output, {
  21 + file: 'modular/sortable.complete.esm.js',
  22 + format: 'esm'
  23 + })
  24 + }
  25 +]).map(config => {
  26 + let buildCopy = { ...build };
  27 + return Object.assign(buildCopy, config);
  28 +});
  1 +const UglifyJS = require('uglify-js'),
  2 + fs = require('fs'),
  3 + package = require('../package.json');
  4 +
  5 +const banner = `/*! Sortable ${ package.version } - ${ package.license } | ${ package.repository.url } */\n`;
  6 +
  7 +fs.writeFileSync(
  8 + `./Sortable.min.js`,
  9 + banner + UglifyJS.minify(fs.readFileSync(`./Sortable.js`, 'utf8')).code,
  10 + 'utf8'
  11 +);
  1 +const createTestCafe = require('testcafe');
  2 +// Testcafe cannot test on IE < 11
  3 +// Testcafe testing on Chrome Android is currently broken (https://github.com/DevExpress/testcafe/issues/3948)
  4 +const browsers = [
  5 + 'saucelabs:Internet Explorer@11.285:Windows 10',
  6 + 'saucelabs:MicrosoftEdge@16.16299:Windows 10',
  7 + 'saucelabs:iPhone XS Simulator@12.2',
  8 + 'saucelabs:Safari@12.0:macOS 10.14',
  9 + 'chrome:headless',
  10 + 'firefox:headless'
  11 +];
  12 +
  13 +let testcafe;
  14 +let runner;
  15 +let failedCount;
  16 +
  17 +createTestCafe(null, 8000, 8001).then((tc) => {
  18 + testcafe = tc;
  19 + runner = tc.createRunner();
  20 + return runner
  21 + .src('./tests/Sortable.compat.test.js')
  22 + .browsers(browsers)
  23 + .run();
  24 +}).then((actualFailedCount) => {
  25 + // https://testcafe-discuss.devexpress.com/t/why-circleci-marked-build-as-green-even-if-this-build-contain-failed-test/726/2
  26 + failedCount = actualFailedCount;
  27 + return testcafe.close();
  28 +}).then(() => process.exit(failedCount));
  29 +
  30 +
  1 +const createTestCafe = require('testcafe');
  2 +
  3 +let testcafe;
  4 +let runner;
  5 +let failedCount;
  6 +
  7 +
  8 +createTestCafe().then((tc) => {
  9 + testcafe = tc;
  10 + runner = tc.createRunner();
  11 + return runner
  12 + .src('./tests/Sortable.test.js')
  13 + .browsers('chrome:headless')
  14 + .concurrency(3)
  15 + .run();
  16 +}).then((actualFailedCount) => {
  17 + failedCount = actualFailedCount;
  18 + console.log('FAILED COUNT', actualFailedCount)
  19 + return testcafe.close();
  20 +}).then(() => process.exit(failedCount));
  21 +
  1 +import build from './build.js';
  2 +
  3 +
  4 +export default ([
  5 + {
  6 + input: 'entry/entry-complete.js',
  7 + output: Object.assign({}, build.output, {
  8 + file: './Sortable.js',
  9 + format: 'umd'
  10 + })
  11 + }
  12 +]).map(config => {
  13 + let buildCopy = { ...build };
  14 + return Object.assign(buildCopy, config);
  15 +});
  1 +import { getRect, css, matrix, isRectEqual, indexOfObject } from './utils.js';
  2 +import Sortable from './Sortable.js';
  3 +
  4 +export default function AnimationStateManager() {
  5 + let animationStates = [],
  6 + animationCallbackId;
  7 +
  8 + return {
  9 + captureAnimationState() {
  10 + animationStates = [];
  11 + if (!this.options.animation) return;
  12 + let children = [].slice.call(this.el.children);
  13 +
  14 + children.forEach(child => {
  15 + if (css(child, 'display') === 'none' || child === Sortable.ghost) return;
  16 + animationStates.push({
  17 + target: child,
  18 + rect: getRect(child)
  19 + });
  20 + let fromRect = { ...animationStates[animationStates.length - 1].rect };
  21 +
  22 + // If animating: compensate for current animation
  23 + if (child.thisAnimationDuration) {
  24 + let childMatrix = matrix(child, true);
  25 + if (childMatrix) {
  26 + fromRect.top -= childMatrix.f;
  27 + fromRect.left -= childMatrix.e;
  28 + }
  29 + }
  30 +
  31 + child.fromRect = fromRect;
  32 + });
  33 + },
  34 +
  35 + addAnimationState(state) {
  36 + animationStates.push(state);
  37 + },
  38 +
  39 + removeAnimationState(target) {
  40 + animationStates.splice(indexOfObject(animationStates, { target }), 1);
  41 + },
  42 +
  43 + animateAll(callback) {
  44 + if (!this.options.animation) {
  45 + clearTimeout(animationCallbackId);
  46 + if (typeof(callback) === 'function') callback();
  47 + return;
  48 + }
  49 +
  50 + let animating = false,
  51 + animationTime = 0;
  52 +
  53 + animationStates.forEach((state) => {
  54 + let time = 0,
  55 + animatingThis = false,
  56 + target = state.target,
  57 + fromRect = target.fromRect,
  58 + toRect = getRect(target),
  59 + prevFromRect = target.prevFromRect,
  60 + prevToRect = target.prevToRect,
  61 + animatingRect = state.rect,
  62 + targetMatrix = matrix(target, true);
  63 +
  64 +
  65 + if (targetMatrix) {
  66 + // Compensate for current animation
  67 + toRect.top -= targetMatrix.f;
  68 + toRect.left -= targetMatrix.e;
  69 + }
  70 +
  71 + target.toRect = toRect;
  72 +
  73 + if (target.thisAnimationDuration) {
  74 + // Could also check if animatingRect is between fromRect and toRect
  75 + if (
  76 + isRectEqual(prevFromRect, toRect) &&
  77 + !isRectEqual(fromRect, toRect) &&
  78 + // Make sure animatingRect is on line between toRect & fromRect
  79 + (animatingRect.top - toRect.top) /
  80 + (animatingRect.left - toRect.left) ===
  81 + (fromRect.top - toRect.top) /
  82 + (fromRect.left - toRect.left)
  83 + ) {
  84 + // If returning to same place as started from animation and on same axis
  85 + time = calculateRealTime(animatingRect, prevFromRect, prevToRect, this.options);
  86 + }
  87 + }
  88 +
  89 + // if fromRect != toRect: animate
  90 + if (!isRectEqual(toRect, fromRect)) {
  91 + target.prevFromRect = fromRect;
  92 + target.prevToRect = toRect;
  93 +
  94 + if (!time) {
  95 + time = this.options.animation;
  96 + }
  97 + this.animate(
  98 + target,
  99 + animatingRect,
  100 + toRect,
  101 + time
  102 + );
  103 + }
  104 +
  105 + if (time) {
  106 + animating = true;
  107 + animationTime = Math.max(animationTime, time);
  108 + clearTimeout(target.animationResetTimer);
  109 + target.animationResetTimer = setTimeout(function() {
  110 + target.animationTime = 0;
  111 + target.prevFromRect = null;
  112 + target.fromRect = null;
  113 + target.prevToRect = null;
  114 + target.thisAnimationDuration = null;
  115 + }, time);
  116 + target.thisAnimationDuration = time;
  117 + }
  118 + });
  119 +
  120 +
  121 + clearTimeout(animationCallbackId);
  122 + if (!animating) {
  123 + if (typeof(callback) === 'function') callback();
  124 + } else {
  125 + animationCallbackId = setTimeout(function() {
  126 + if (typeof(callback) === 'function') callback();
  127 + }, animationTime);
  128 + }
  129 + animationStates = [];
  130 + },
  131 +
  132 + animate(target, currentRect, toRect, duration) {
  133 + if (duration) {
  134 + css(target, 'transition', '');
  135 + css(target, 'transform', '');
  136 + let elMatrix = matrix(this.el),
  137 + scaleX = elMatrix && elMatrix.a,
  138 + scaleY = elMatrix && elMatrix.d,
  139 + translateX = (currentRect.left - toRect.left) / (scaleX || 1),
  140 + translateY = (currentRect.top - toRect.top) / (scaleY || 1);
  141 +
  142 + target.animatingX = !!translateX;
  143 + target.animatingY = !!translateY;
  144 +
  145 + css(target, 'transform', 'translate3d(' + translateX + 'px,' + translateY + 'px,0)');
  146 +
  147 + repaint(target); // repaint
  148 +
  149 + css(target, 'transition', 'transform ' + duration + 'ms' + (this.options.easing ? ' ' + this.options.easing : ''));
  150 + css(target, 'transform', 'translate3d(0,0,0)');
  151 + (typeof target.animated === 'number') && clearTimeout(target.animated);
  152 + target.animated = setTimeout(function () {
  153 + css(target, 'transition', '');
  154 + css(target, 'transform', '');
  155 + target.animated = false;
  156 +
  157 + target.animatingX = false;
  158 + target.animatingY = false;
  159 + }, duration);
  160 + }
  161 + }
  162 + };
  163 +}
  164 +
  165 +function repaint(target) {
  166 + return target.offsetWidth;
  167 +}
  168 +
  169 +
  170 +function calculateRealTime(animatingRect, fromRect, toRect, options) {
  171 + return (
  172 + Math.sqrt(Math.pow(fromRect.top - animatingRect.top, 2) + Math.pow(fromRect.left - animatingRect.left, 2)) /
  173 + Math.sqrt(Math.pow(fromRect.top - toRect.top, 2) + Math.pow(fromRect.left - toRect.left, 2))
  174 + ) * options.animation;
  175 +}
  1 +function userAgent(pattern) {
  2 + if (typeof window !== 'undefined' && window.navigator) {
  3 + return !!/*@__PURE__*/navigator.userAgent.match(pattern);
  4 + }
  5 +}
  6 +
  7 +export const IE11OrLess = userAgent(/(?:Trident.*rv[ :]?11\.|msie|iemobile|Windows Phone)/i);
  8 +export const Edge = userAgent(/Edge/i);
  9 +export const FireFox = userAgent(/firefox/i);
  10 +export const Safari = userAgent(/safari/i) && !userAgent(/chrome/i) && !userAgent(/android/i);
  11 +export const IOS = userAgent(/iP(ad|od|hone)/i);
  12 +export const ChromeForAndroid = userAgent(/chrome/i) && userAgent(/android/i);
  1 +import { IE11OrLess, Edge } from './BrowserInfo.js';
  2 +import { expando } from './utils.js';
  3 +import PluginManager from './PluginManager.js';
  4 +
  5 +export default function dispatchEvent(
  6 + {
  7 + sortable, rootEl, name,
  8 + targetEl, cloneEl, toEl, fromEl,
  9 + oldIndex, newIndex,
  10 + oldDraggableIndex, newDraggableIndex,
  11 + originalEvent, putSortable, extraEventProperties
  12 + }
  13 +) {
  14 + sortable = (sortable || (rootEl && rootEl[expando]));
  15 + if (!sortable) return;
  16 +
  17 + let evt,
  18 + options = sortable.options,
  19 + onName = 'on' + name.charAt(0).toUpperCase() + name.substr(1);
  20 + // Support for new CustomEvent feature
  21 + if (window.CustomEvent && !IE11OrLess && !Edge) {
  22 + evt = new CustomEvent(name, {
  23 + bubbles: true,
  24 + cancelable: true
  25 + });
  26 + } else {
  27 + evt = document.createEvent('Event');
  28 + evt.initEvent(name, true, true);
  29 + }
  30 +
  31 + evt.to = toEl || rootEl;
  32 + evt.from = fromEl || rootEl;
  33 + evt.item = targetEl || rootEl;
  34 + evt.clone = cloneEl;
  35 +
  36 + evt.oldIndex = oldIndex;
  37 + evt.newIndex = newIndex;
  38 +
  39 + evt.oldDraggableIndex = oldDraggableIndex;
  40 + evt.newDraggableIndex = newDraggableIndex;
  41 +
  42 + evt.originalEvent = originalEvent;
  43 + evt.pullMode = putSortable ? putSortable.lastPutMode : undefined;
  44 +
  45 + let allEventProperties = { ...extraEventProperties, ...PluginManager.getEventProperties(name, sortable) };
  46 + for (let option in allEventProperties) {
  47 + evt[option] = allEventProperties[option];
  48 + }
  49 +
  50 + if (rootEl) {
  51 + rootEl.dispatchEvent(evt);
  52 + }
  53 +
  54 + if (options[onName]) {
  55 + options[onName].call(sortable, evt);
  56 + }
  57 +}
  1 +let plugins = [];
  2 +
  3 +const defaults = {
  4 + initializeByDefault: true
  5 +};
  6 +
  7 +export default {
  8 + mount(plugin) {
  9 + // Set default static properties
  10 + for (let option in defaults) {
  11 + if (defaults.hasOwnProperty(option) && !(option in plugin)) {
  12 + plugin[option] = defaults[option];
  13 + }
  14 + }
  15 + plugins.push(plugin);
  16 + },
  17 + pluginEvent(eventName, sortable, evt) {
  18 + this.eventCanceled = false;
  19 + evt.cancel = () => {
  20 + this.eventCanceled = true;
  21 + };
  22 + const eventNameGlobal = eventName + 'Global';
  23 + plugins.forEach(plugin => {
  24 + if (!sortable[plugin.pluginName]) return;
  25 + // Fire global events if it exists in this sortable
  26 + if (
  27 + sortable[plugin.pluginName][eventNameGlobal]
  28 + ) {
  29 + sortable[plugin.pluginName][eventNameGlobal]({ sortable, ...evt });
  30 + }
  31 +
  32 + // Only fire plugin event if plugin is enabled in this sortable,
  33 + // and plugin has event defined
  34 + if (
  35 + sortable.options[plugin.pluginName] &&
  36 + sortable[plugin.pluginName][eventName]
  37 + ) {
  38 + sortable[plugin.pluginName][eventName]({ sortable, ...evt });
  39 + }
  40 + });
  41 + },
  42 + initializePlugins(sortable, el, defaults, options) {
  43 + plugins.forEach(plugin => {
  44 + const pluginName = plugin.pluginName;
  45 + if (!sortable.options[pluginName] && !plugin.initializeByDefault) return;
  46 +
  47 + let initialized = new plugin(sortable, el, sortable.options);
  48 + initialized.sortable = sortable;
  49 + initialized.options = sortable.options;
  50 + sortable[pluginName] = initialized;
  51 +
  52 + // Add default options from plugin
  53 + Object.assign(defaults, initialized.defaults);
  54 + });
  55 +
  56 + for (let option in sortable.options) {
  57 + if (!sortable.options.hasOwnProperty(option)) continue;
  58 + let modified = this.modifyOption(sortable, option, sortable.options[option]);
  59 + if (typeof(modified) !== 'undefined') {
  60 + sortable.options[option] = modified;
  61 + }
  62 + }
  63 + },
  64 + getEventProperties(name, sortable) {
  65 + let eventProperties = {};
  66 + plugins.forEach(plugin => {
  67 + if (typeof(plugin.eventProperties) !== 'function') return;
  68 + Object.assign(eventProperties, plugin.eventProperties.call(sortable[plugin.pluginName], name));
  69 + });
  70 +
  71 + return eventProperties;
  72 + },
  73 + modifyOption(sortable, name, value) {
  74 + let modifiedValue;
  75 + plugins.forEach(plugin => {
  76 + // Plugin must exist on the Sortable
  77 + if (!sortable[plugin.pluginName]) return;
  78 +
  79 + // If static option listener exists for this option, call in the context of the Sortable's instance of this plugin
  80 + if (plugin.optionListeners && typeof(plugin.optionListeners[name]) === 'function') {
  81 + modifiedValue = plugin.optionListeners[name].call(sortable[plugin.pluginName], value);
  82 + }
  83 + });
  84 +
  85 + return modifiedValue;
  86 + }
  87 +};