vitrage 0.0.3 → 0.0.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,1051 @@
1
+ /**!
2
+ * Sortable
3
+ * @author RubaXa <trash@rubaxa.org>
4
+ * @license MIT
5
+ */
6
+
7
+
8
+ (function (factory) {
9
+ "use strict";
10
+
11
+ if (typeof define === "function" && define.amd) {
12
+ define(factory);
13
+ }
14
+ else if (typeof module != "undefined" && typeof module.exports != "undefined") {
15
+ module.exports = factory();
16
+ }
17
+ else if (typeof Package !== "undefined") {
18
+ Sortable = factory(); // export for Meteor.js
19
+ }
20
+ else {
21
+ /* jshint sub:true */
22
+ window["Sortable"] = factory();
23
+ }
24
+ })(function () {
25
+ "use strict";
26
+
27
+ var dragEl,
28
+ ghostEl,
29
+ cloneEl,
30
+ rootEl,
31
+ nextEl,
32
+
33
+ scrollEl,
34
+ scrollParentEl,
35
+
36
+ lastEl,
37
+ lastCSS,
38
+
39
+ oldIndex,
40
+ newIndex,
41
+
42
+ activeGroup,
43
+ autoScroll = {},
44
+
45
+ tapEvt,
46
+ touchEvt,
47
+
48
+ expando = 'Sortable' + (new Date).getTime(),
49
+
50
+ win = window,
51
+ document = win.document,
52
+ parseInt = win.parseInt,
53
+
54
+ supportDraggable = !!('draggable' in document.createElement('div')),
55
+
56
+
57
+ _silent = false,
58
+
59
+ _dispatchEvent = function (rootEl, name, targetEl, fromEl, startIndex, newIndex) {
60
+ var evt = document.createEvent('Event');
61
+
62
+ evt.initEvent(name, true, true);
63
+
64
+ evt.item = targetEl || rootEl;
65
+ evt.from = fromEl || rootEl;
66
+ evt.clone = cloneEl;
67
+
68
+ evt.oldIndex = startIndex;
69
+ evt.newIndex = newIndex;
70
+
71
+ rootEl.dispatchEvent(evt);
72
+ },
73
+
74
+ _customEvents = 'onAdd onUpdate onRemove onStart onEnd onFilter onSort'.split(' '),
75
+
76
+ noop = function () {},
77
+
78
+ abs = Math.abs,
79
+ slice = [].slice,
80
+
81
+ touchDragOverListeners = [],
82
+
83
+ _autoScroll = _throttle(function (/**Event*/evt, /**Object*/options, /**HTMLElement*/rootEl) {
84
+ // Bug: https://bugzilla.mozilla.org/show_bug.cgi?id=505521
85
+ if (rootEl && options.scroll) {
86
+ var el,
87
+ rect,
88
+ sens = options.scrollSensitivity,
89
+ speed = options.scrollSpeed,
90
+
91
+ x = evt.clientX,
92
+ y = evt.clientY,
93
+
94
+ winWidth = window.innerWidth,
95
+ winHeight = window.innerHeight,
96
+
97
+ vx,
98
+ vy
99
+ ;
100
+
101
+ // Delect scrollEl
102
+ if (scrollParentEl !== rootEl) {
103
+ scrollEl = options.scroll;
104
+ scrollParentEl = rootEl;
105
+
106
+ if (scrollEl === true) {
107
+ scrollEl = rootEl;
108
+
109
+ do {
110
+ if ((scrollEl.offsetWidth < scrollEl.scrollWidth) ||
111
+ (scrollEl.offsetHeight < scrollEl.scrollHeight)
112
+ ) {
113
+ break;
114
+ }
115
+ /* jshint boss:true */
116
+ } while (scrollEl = scrollEl.parentNode);
117
+ }
118
+ }
119
+
120
+ if (scrollEl) {
121
+ el = scrollEl;
122
+ rect = scrollEl.getBoundingClientRect();
123
+ vx = (abs(rect.right - x) <= sens) - (abs(rect.left - x) <= sens);
124
+ vy = (abs(rect.bottom - y) <= sens) - (abs(rect.top - y) <= sens);
125
+ }
126
+
127
+
128
+ if (!(vx || vy)) {
129
+ vx = (winWidth - x <= sens) - (x <= sens);
130
+ vy = (winHeight - y <= sens) - (y <= sens);
131
+
132
+ /* jshint expr:true */
133
+ (vx || vy) && (el = win);
134
+ }
135
+
136
+
137
+ if (autoScroll.vx !== vx || autoScroll.vy !== vy || autoScroll.el !== el) {
138
+ autoScroll.el = el;
139
+ autoScroll.vx = vx;
140
+ autoScroll.vy = vy;
141
+
142
+ clearInterval(autoScroll.pid);
143
+
144
+ if (el) {
145
+ autoScroll.pid = setInterval(function () {
146
+ if (el === win) {
147
+ win.scrollTo(win.scrollX + vx * speed, win.scrollY + vy * speed);
148
+ } else {
149
+ vy && (el.scrollTop += vy * speed);
150
+ vx && (el.scrollLeft += vx * speed);
151
+ }
152
+ }, 24);
153
+ }
154
+ }
155
+ }
156
+ }, 30)
157
+ ;
158
+
159
+
160
+
161
+ /**
162
+ * @class Sortable
163
+ * @param {HTMLElement} el
164
+ * @param {Object} [options]
165
+ */
166
+ function Sortable(el, options) {
167
+ this.el = el; // root element
168
+ this.options = options = (options || {});
169
+
170
+
171
+ // Default options
172
+ var defaults = {
173
+ group: Math.random(),
174
+ sort: true,
175
+ disabled: false,
176
+ store: null,
177
+ handle: null,
178
+ scroll: true,
179
+ scrollSensitivity: 30,
180
+ scrollSpeed: 10,
181
+ draggable: /[uo]l/i.test(el.nodeName) ? 'li' : '>*',
182
+ ghostClass: 'sortable-ghost',
183
+ ignore: 'a, img',
184
+ filter: null,
185
+ animation: 0,
186
+ setData: function (dataTransfer, dragEl) {
187
+ dataTransfer.setData('Text', dragEl.textContent);
188
+ },
189
+ dropBubble: false,
190
+ dragoverBubble: false
191
+ };
192
+
193
+
194
+ // Set default options
195
+ for (var name in defaults) {
196
+ !(name in options) && (options[name] = defaults[name]);
197
+ }
198
+
199
+
200
+ var group = options.group;
201
+
202
+ if (!group || typeof group != 'object') {
203
+ group = options.group = { name: group };
204
+ }
205
+
206
+
207
+ ['pull', 'put'].forEach(function (key) {
208
+ if (!(key in group)) {
209
+ group[key] = true;
210
+ }
211
+ });
212
+
213
+
214
+ // Define events
215
+ _customEvents.forEach(function (name) {
216
+ options[name] = _bind(this, options[name] || noop);
217
+ _on(el, name.substr(2).toLowerCase(), options[name]);
218
+ }, this);
219
+
220
+
221
+ // Export options
222
+ options.groups = ' ' + group.name + (group.put.join ? ' ' + group.put.join(' ') : '') + ' ';
223
+ el[expando] = options;
224
+
225
+
226
+ // Bind all private methods
227
+ for (var fn in this) {
228
+ if (fn.charAt(0) === '_') {
229
+ this[fn] = _bind(this, this[fn]);
230
+ }
231
+ }
232
+
233
+
234
+ // Bind events
235
+ _on(el, 'mousedown', this._onTapStart);
236
+ _on(el, 'touchstart', this._onTapStart);
237
+
238
+ _on(el, 'dragover', this);
239
+ _on(el, 'dragenter', this);
240
+
241
+ touchDragOverListeners.push(this._onDragOver);
242
+
243
+ // Restore sorting
244
+ options.store && this.sort(options.store.get(this));
245
+ }
246
+
247
+
248
+ Sortable.prototype = /** @lends Sortable.prototype */ {
249
+ constructor: Sortable,
250
+
251
+
252
+ _dragStarted: function () {
253
+ if (rootEl && dragEl) {
254
+ // Apply effect
255
+ _toggleClass(dragEl, this.options.ghostClass, true);
256
+
257
+ Sortable.active = this;
258
+
259
+ // Drag start event
260
+ _dispatchEvent(rootEl, 'start', dragEl, rootEl, oldIndex);
261
+ }
262
+ },
263
+
264
+
265
+ _onTapStart: function (/**Event|TouchEvent*/evt) {
266
+ var type = evt.type,
267
+ touch = evt.touches && evt.touches[0],
268
+ target = (touch || evt).target,
269
+ originalTarget = target,
270
+ options = this.options,
271
+ el = this.el,
272
+ filter = options.filter;
273
+
274
+ if (type === 'mousedown' && evt.button !== 0 || options.disabled) {
275
+ return; // only left button or enabled
276
+ }
277
+
278
+ target = _closest(target, options.draggable, el);
279
+
280
+ if (!target) {
281
+ return;
282
+ }
283
+
284
+ // get the index of the dragged element within its parent
285
+ oldIndex = _index(target);
286
+
287
+ // Check filter
288
+ if (typeof filter === 'function') {
289
+ if (filter.call(this, evt, target, this)) {
290
+ _dispatchEvent(originalTarget, 'filter', target, el, oldIndex);
291
+ evt.preventDefault();
292
+ return; // cancel dnd
293
+ }
294
+ }
295
+ else if (filter) {
296
+ filter = filter.split(',').some(function (criteria) {
297
+ criteria = _closest(originalTarget, criteria.trim(), el);
298
+
299
+ if (criteria) {
300
+ _dispatchEvent(criteria, 'filter', target, el, oldIndex);
301
+ return true;
302
+ }
303
+ });
304
+
305
+ if (filter) {
306
+ evt.preventDefault();
307
+ return; // cancel dnd
308
+ }
309
+ }
310
+
311
+
312
+ if (options.handle && !_closest(originalTarget, options.handle, el)) {
313
+ return;
314
+ }
315
+
316
+
317
+ // Prepare `dragstart`
318
+ if (target && !dragEl && (target.parentNode === el)) {
319
+ tapEvt = evt;
320
+
321
+ rootEl = this.el;
322
+ dragEl = target;
323
+ nextEl = dragEl.nextSibling;
324
+ activeGroup = this.options.group;
325
+
326
+ dragEl.draggable = true;
327
+
328
+ // Disable "draggable"
329
+ options.ignore.split(',').forEach(function (criteria) {
330
+ _find(target, criteria.trim(), _disableDraggable);
331
+ });
332
+
333
+ if (touch) {
334
+ // Touch device support
335
+ tapEvt = {
336
+ target: target,
337
+ clientX: touch.clientX,
338
+ clientY: touch.clientY
339
+ };
340
+
341
+ this._onDragStart(tapEvt, 'touch');
342
+ evt.preventDefault();
343
+ }
344
+
345
+ _on(document, 'mouseup', this._onDrop);
346
+ _on(document, 'touchend', this._onDrop);
347
+ _on(document, 'touchcancel', this._onDrop);
348
+
349
+ _on(dragEl, 'dragend', this);
350
+ _on(rootEl, 'dragstart', this._onDragStart);
351
+
352
+ if (!supportDraggable) {
353
+ this._onDragStart(tapEvt, true);
354
+ }
355
+
356
+ try {
357
+ if (document.selection) {
358
+ document.selection.empty();
359
+ } else {
360
+ window.getSelection().removeAllRanges();
361
+ }
362
+ } catch (err) {
363
+ }
364
+ }
365
+ },
366
+
367
+ _emulateDragOver: function () {
368
+ if (touchEvt) {
369
+ _css(ghostEl, 'display', 'none');
370
+
371
+ var target = document.elementFromPoint(touchEvt.clientX, touchEvt.clientY),
372
+ parent = target,
373
+ groupName = ' ' + this.options.group.name + '',
374
+ i = touchDragOverListeners.length;
375
+
376
+ if (parent) {
377
+ do {
378
+ if (parent[expando] && parent[expando].groups.indexOf(groupName) > -1) {
379
+ while (i--) {
380
+ touchDragOverListeners[i]({
381
+ clientX: touchEvt.clientX,
382
+ clientY: touchEvt.clientY,
383
+ target: target,
384
+ rootEl: parent
385
+ });
386
+ }
387
+
388
+ break;
389
+ }
390
+
391
+ target = parent; // store last element
392
+ }
393
+ /* jshint boss:true */
394
+ while (parent = parent.parentNode);
395
+ }
396
+
397
+ _css(ghostEl, 'display', '');
398
+ }
399
+ },
400
+
401
+
402
+ _onTouchMove: function (/**TouchEvent*/evt) {
403
+ if (tapEvt) {
404
+ var touch = evt.touches ? evt.touches[0] : evt,
405
+ dx = touch.clientX - tapEvt.clientX,
406
+ dy = touch.clientY - tapEvt.clientY,
407
+ translate3d = evt.touches ? 'translate3d(' + dx + 'px,' + dy + 'px,0)' : 'translate(' + dx + 'px,' + dy + 'px)';
408
+
409
+ touchEvt = touch;
410
+
411
+ _css(ghostEl, 'webkitTransform', translate3d);
412
+ _css(ghostEl, 'mozTransform', translate3d);
413
+ _css(ghostEl, 'msTransform', translate3d);
414
+ _css(ghostEl, 'transform', translate3d);
415
+
416
+ evt.preventDefault();
417
+ }
418
+ },
419
+
420
+
421
+ _onDragStart: function (/**Event*/evt, /**boolean*/useFallback) {
422
+ var dataTransfer = evt.dataTransfer,
423
+ options = this.options;
424
+
425
+ this._offUpEvents();
426
+
427
+ if (activeGroup.pull == 'clone') {
428
+ cloneEl = dragEl.cloneNode(true);
429
+ _css(cloneEl, 'display', 'none');
430
+ rootEl.insertBefore(cloneEl, dragEl);
431
+ }
432
+
433
+ if (useFallback) {
434
+ var rect = dragEl.getBoundingClientRect(),
435
+ css = _css(dragEl),
436
+ ghostRect;
437
+
438
+ ghostEl = dragEl.cloneNode(true);
439
+
440
+ _css(ghostEl, 'top', rect.top - parseInt(css.marginTop, 10));
441
+ _css(ghostEl, 'left', rect.left - parseInt(css.marginLeft, 10));
442
+ _css(ghostEl, 'width', rect.width);
443
+ _css(ghostEl, 'height', rect.height);
444
+ _css(ghostEl, 'opacity', '0.8');
445
+ _css(ghostEl, 'position', 'fixed');
446
+ _css(ghostEl, 'zIndex', '100000');
447
+
448
+ rootEl.appendChild(ghostEl);
449
+
450
+ // Fixing dimensions.
451
+ ghostRect = ghostEl.getBoundingClientRect();
452
+ _css(ghostEl, 'width', rect.width * 2 - ghostRect.width);
453
+ _css(ghostEl, 'height', rect.height * 2 - ghostRect.height);
454
+
455
+ if (useFallback === 'touch') {
456
+ // Bind touch events
457
+ _on(document, 'touchmove', this._onTouchMove);
458
+ _on(document, 'touchend', this._onDrop);
459
+ _on(document, 'touchcancel', this._onDrop);
460
+ } else {
461
+ // Old brwoser
462
+ _on(document, 'mousemove', this._onTouchMove);
463
+ _on(document, 'mouseup', this._onDrop);
464
+ }
465
+
466
+ this._loopId = setInterval(this._emulateDragOver, 150);
467
+ }
468
+ else {
469
+ if (dataTransfer) {
470
+ dataTransfer.effectAllowed = 'move';
471
+ options.setData && options.setData.call(this, dataTransfer, dragEl);
472
+ }
473
+
474
+ _on(document, 'drop', this);
475
+ }
476
+
477
+ setTimeout(this._dragStarted, 0);
478
+ },
479
+
480
+ _onDragOver: function (/**Event*/evt) {
481
+ var el = this.el,
482
+ target,
483
+ dragRect,
484
+ revert,
485
+ options = this.options,
486
+ group = options.group,
487
+ groupPut = group.put,
488
+ isOwner = (activeGroup === group),
489
+ canSort = options.sort;
490
+
491
+ if (!dragEl) {
492
+ return;
493
+ }
494
+
495
+ if (evt.preventDefault !== void 0) {
496
+ evt.preventDefault();
497
+ !options.dragoverBubble && evt.stopPropagation();
498
+ }
499
+
500
+ if (activeGroup && !options.disabled &&
501
+ (isOwner
502
+ ? canSort || (revert = !rootEl.contains(dragEl))
503
+ : activeGroup.pull && groupPut && (
504
+ (activeGroup.name === group.name) || // by Name
505
+ (groupPut.indexOf && ~groupPut.indexOf(activeGroup.name)) // by Array
506
+ )
507
+ ) &&
508
+ (evt.rootEl === void 0 || evt.rootEl === this.el)
509
+ ) {
510
+ // Smart auto-scrolling
511
+ _autoScroll(evt, options, this.el);
512
+
513
+ if (_silent) {
514
+ return;
515
+ }
516
+
517
+ target = _closest(evt.target, options.draggable, el);
518
+ dragRect = dragEl.getBoundingClientRect();
519
+
520
+
521
+ if (revert) {
522
+ _cloneHide(true);
523
+
524
+ if (cloneEl || nextEl) {
525
+ rootEl.insertBefore(dragEl, cloneEl || nextEl);
526
+ }
527
+ else if (!canSort) {
528
+ rootEl.appendChild(dragEl);
529
+ }
530
+
531
+ return;
532
+ }
533
+
534
+
535
+ if ((el.children.length === 0) || (el.children[0] === ghostEl) ||
536
+ (el === evt.target) && (target = _ghostInBottom(el, evt))
537
+ ) {
538
+ if (target) {
539
+ if (target.animated) {
540
+ return;
541
+ }
542
+ targetRect = target.getBoundingClientRect();
543
+ }
544
+
545
+ _cloneHide(isOwner);
546
+
547
+ el.appendChild(dragEl);
548
+ this._animate(dragRect, dragEl);
549
+ target && this._animate(targetRect, target);
550
+ }
551
+ else if (target && !target.animated && target !== dragEl && (target.parentNode[expando] !== void 0)) {
552
+ if (lastEl !== target) {
553
+ lastEl = target;
554
+ lastCSS = _css(target);
555
+ }
556
+
557
+
558
+ var targetRect = target.getBoundingClientRect(),
559
+ width = targetRect.right - targetRect.left,
560
+ height = targetRect.bottom - targetRect.top,
561
+ floating = /left|right|inline/.test(lastCSS.cssFloat + lastCSS.display),
562
+ isWide = (target.offsetWidth > dragEl.offsetWidth),
563
+ isLong = (target.offsetHeight > dragEl.offsetHeight),
564
+ halfway = (floating ? (evt.clientX - targetRect.left) / width : (evt.clientY - targetRect.top) / height) > 0.5,
565
+ nextSibling = target.nextElementSibling,
566
+ after
567
+ ;
568
+
569
+ _silent = true;
570
+ setTimeout(_unsilent, 30);
571
+
572
+ _cloneHide(isOwner);
573
+
574
+ if (floating) {
575
+ after = (target.previousElementSibling === dragEl) && !isWide || halfway && isWide;
576
+ } else {
577
+ after = (nextSibling !== dragEl) && !isLong || halfway && isLong;
578
+ }
579
+
580
+ if (after && !nextSibling) {
581
+ el.appendChild(dragEl);
582
+ } else {
583
+ target.parentNode.insertBefore(dragEl, after ? nextSibling : target);
584
+ }
585
+
586
+ this._animate(dragRect, dragEl);
587
+ this._animate(targetRect, target);
588
+ }
589
+ }
590
+ },
591
+
592
+ _animate: function (prevRect, target) {
593
+ var ms = this.options.animation;
594
+
595
+ if (ms) {
596
+ var currentRect = target.getBoundingClientRect();
597
+
598
+ _css(target, 'transition', 'none');
599
+ _css(target, 'transform', 'translate3d('
600
+ + (prevRect.left - currentRect.left) + 'px,'
601
+ + (prevRect.top - currentRect.top) + 'px,0)'
602
+ );
603
+
604
+ target.offsetWidth; // repaint
605
+
606
+ _css(target, 'transition', 'all ' + ms + 'ms');
607
+ _css(target, 'transform', 'translate3d(0,0,0)');
608
+
609
+ clearTimeout(target.animated);
610
+ target.animated = setTimeout(function () {
611
+ _css(target, 'transition', '');
612
+ _css(target, 'transform', '');
613
+ target.animated = false;
614
+ }, ms);
615
+ }
616
+ },
617
+
618
+ _offUpEvents: function () {
619
+ _off(document, 'mouseup', this._onDrop);
620
+ _off(document, 'touchmove', this._onTouchMove);
621
+ _off(document, 'touchend', this._onDrop);
622
+ _off(document, 'touchcancel', this._onDrop);
623
+ },
624
+
625
+ _onDrop: function (/**Event*/evt) {
626
+ var el = this.el,
627
+ options = this.options;
628
+
629
+ clearInterval(this._loopId);
630
+ clearInterval(autoScroll.pid);
631
+
632
+ // Unbind events
633
+ _off(document, 'drop', this);
634
+ _off(document, 'mousemove', this._onTouchMove);
635
+ _off(el, 'dragstart', this._onDragStart);
636
+
637
+ this._offUpEvents();
638
+
639
+ if (evt) {
640
+ evt.preventDefault();
641
+ !options.dropBubble && evt.stopPropagation();
642
+
643
+ ghostEl && ghostEl.parentNode.removeChild(ghostEl);
644
+
645
+ if (dragEl) {
646
+ _off(dragEl, 'dragend', this);
647
+
648
+ _disableDraggable(dragEl);
649
+ _toggleClass(dragEl, this.options.ghostClass, false);
650
+
651
+ if (rootEl !== dragEl.parentNode) {
652
+ newIndex = _index(dragEl);
653
+
654
+ // drag from one list and drop into another
655
+ _dispatchEvent(dragEl.parentNode, 'sort', dragEl, rootEl, oldIndex, newIndex);
656
+ _dispatchEvent(rootEl, 'sort', dragEl, rootEl, oldIndex, newIndex);
657
+
658
+ // Add event
659
+ _dispatchEvent(dragEl, 'add', dragEl, rootEl, oldIndex, newIndex);
660
+
661
+ // Remove event
662
+ _dispatchEvent(rootEl, 'remove', dragEl, rootEl, oldIndex, newIndex);
663
+ }
664
+ else {
665
+ // Remove clone
666
+ cloneEl && cloneEl.parentNode.removeChild(cloneEl);
667
+
668
+ if (dragEl.nextSibling !== nextEl) {
669
+ // Get the index of the dragged element within its parent
670
+ newIndex = _index(dragEl);
671
+
672
+ // drag & drop within the same list
673
+ _dispatchEvent(rootEl, 'update', dragEl, rootEl, oldIndex, newIndex);
674
+ _dispatchEvent(rootEl, 'sort', dragEl, rootEl, oldIndex, newIndex);
675
+ }
676
+ }
677
+
678
+ // Drag end event
679
+ Sortable.active && _dispatchEvent(rootEl, 'end', dragEl, rootEl, oldIndex, newIndex);
680
+ }
681
+
682
+ // Nulling
683
+ rootEl =
684
+ dragEl =
685
+ ghostEl =
686
+ nextEl =
687
+ cloneEl =
688
+
689
+ scrollEl =
690
+ scrollParentEl =
691
+
692
+ tapEvt =
693
+ touchEvt =
694
+
695
+ lastEl =
696
+ lastCSS =
697
+
698
+ activeGroup =
699
+ Sortable.active = null;
700
+
701
+ // Save sorting
702
+ this.save();
703
+ }
704
+ },
705
+
706
+
707
+ handleEvent: function (/**Event*/evt) {
708
+ var type = evt.type;
709
+
710
+ if (type === 'dragover' || type === 'dragenter') {
711
+ this._onDragOver(evt);
712
+ _globalDragOver(evt);
713
+ }
714
+ else if (type === 'drop' || type === 'dragend') {
715
+ this._onDrop(evt);
716
+ }
717
+ },
718
+
719
+
720
+ /**
721
+ * Serializes the item into an array of string.
722
+ * @returns {String[]}
723
+ */
724
+ toArray: function () {
725
+ var order = [],
726
+ el,
727
+ children = this.el.children,
728
+ i = 0,
729
+ n = children.length;
730
+
731
+ for (; i < n; i++) {
732
+ el = children[i];
733
+ if (_closest(el, this.options.draggable, this.el)) {
734
+ order.push(el.getAttribute('data-id') || _generateId(el));
735
+ }
736
+ }
737
+
738
+ return order;
739
+ },
740
+
741
+
742
+ /**
743
+ * Sorts the elements according to the array.
744
+ * @param {String[]} order order of the items
745
+ */
746
+ sort: function (order) {
747
+ var items = {}, rootEl = this.el;
748
+
749
+ this.toArray().forEach(function (id, i) {
750
+ var el = rootEl.children[i];
751
+
752
+ if (_closest(el, this.options.draggable, rootEl)) {
753
+ items[id] = el;
754
+ }
755
+ }, this);
756
+
757
+ order.forEach(function (id) {
758
+ if (items[id]) {
759
+ rootEl.removeChild(items[id]);
760
+ rootEl.appendChild(items[id]);
761
+ }
762
+ });
763
+ },
764
+
765
+
766
+ /**
767
+ * Save the current sorting
768
+ */
769
+ save: function () {
770
+ var store = this.options.store;
771
+ store && store.set(this);
772
+ },
773
+
774
+
775
+ /**
776
+ * 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.
777
+ * @param {HTMLElement} el
778
+ * @param {String} [selector] default: `options.draggable`
779
+ * @returns {HTMLElement|null}
780
+ */
781
+ closest: function (el, selector) {
782
+ return _closest(el, selector || this.options.draggable, this.el);
783
+ },
784
+
785
+
786
+ /**
787
+ * Set/get option
788
+ * @param {string} name
789
+ * @param {*} [value]
790
+ * @returns {*}
791
+ */
792
+ option: function (name, value) {
793
+ var options = this.options;
794
+
795
+ if (value === void 0) {
796
+ return options[name];
797
+ } else {
798
+ options[name] = value;
799
+ }
800
+ },
801
+
802
+
803
+ /**
804
+ * Destroy
805
+ */
806
+ destroy: function () {
807
+ var el = this.el, options = this.options;
808
+
809
+ _customEvents.forEach(function (name) {
810
+ _off(el, name.substr(2).toLowerCase(), options[name]);
811
+ });
812
+
813
+ _off(el, 'mousedown', this._onTapStart);
814
+ _off(el, 'touchstart', this._onTapStart);
815
+
816
+ _off(el, 'dragover', this);
817
+ _off(el, 'dragenter', this);
818
+
819
+ //remove draggable attributes
820
+ Array.prototype.forEach.call(el.querySelectorAll('[draggable]'), function (el) {
821
+ el.removeAttribute('draggable');
822
+ });
823
+
824
+ touchDragOverListeners.splice(touchDragOverListeners.indexOf(this._onDragOver), 1);
825
+
826
+ this._onDrop();
827
+
828
+ this.el = null;
829
+ }
830
+ };
831
+
832
+
833
+ function _cloneHide(state) {
834
+ if (cloneEl && (cloneEl.state !== state)) {
835
+ _css(cloneEl, 'display', state ? 'none' : '');
836
+ !state && cloneEl.state && rootEl.insertBefore(cloneEl, dragEl);
837
+ cloneEl.state = state;
838
+ }
839
+ }
840
+
841
+
842
+ function _bind(ctx, fn) {
843
+ var args = slice.call(arguments, 2);
844
+ return fn.bind ? fn.bind.apply(fn, [ctx].concat(args)) : function () {
845
+ return fn.apply(ctx, args.concat(slice.call(arguments)));
846
+ };
847
+ }
848
+
849
+
850
+ function _closest(/**HTMLElement*/el, /**String*/selector, /**HTMLElement*/ctx) {
851
+ if (el) {
852
+ ctx = ctx || document;
853
+ selector = selector.split('.');
854
+
855
+ var tag = selector.shift().toUpperCase(),
856
+ re = new RegExp('\\s(' + selector.join('|') + ')\\s', 'g');
857
+
858
+ do {
859
+ if (
860
+ (tag === '>*' && el.parentNode === ctx) || (
861
+ (tag === '' || el.nodeName.toUpperCase() == tag) &&
862
+ (!selector.length || ((' ' + el.className + ' ').match(re) || []).length == selector.length)
863
+ )
864
+ ) {
865
+ return el;
866
+ }
867
+ }
868
+ while (el !== ctx && (el = el.parentNode));
869
+ }
870
+
871
+ return null;
872
+ }
873
+
874
+
875
+ function _globalDragOver(/**Event*/evt) {
876
+ evt.dataTransfer.dropEffect = 'move';
877
+ evt.preventDefault();
878
+ }
879
+
880
+
881
+ function _on(el, event, fn) {
882
+ el.addEventListener(event, fn, false);
883
+ }
884
+
885
+
886
+ function _off(el, event, fn) {
887
+ el.removeEventListener(event, fn, false);
888
+ }
889
+
890
+
891
+ function _toggleClass(el, name, state) {
892
+ if (el) {
893
+ if (el.classList) {
894
+ el.classList[state ? 'add' : 'remove'](name);
895
+ }
896
+ else {
897
+ var className = (' ' + el.className + ' ').replace(/\s+/g, ' ').replace(' ' + name + ' ', '');
898
+ el.className = className + (state ? ' ' + name : '');
899
+ }
900
+ }
901
+ }
902
+
903
+
904
+ function _css(el, prop, val) {
905
+ var style = el && el.style;
906
+
907
+ if (style) {
908
+ if (val === void 0) {
909
+ if (document.defaultView && document.defaultView.getComputedStyle) {
910
+ val = document.defaultView.getComputedStyle(el, '');
911
+ }
912
+ else if (el.currentStyle) {
913
+ val = el.currentStyle;
914
+ }
915
+
916
+ return prop === void 0 ? val : val[prop];
917
+ }
918
+ else {
919
+ if (!(prop in style)) {
920
+ prop = '-webkit-' + prop;
921
+ }
922
+
923
+ style[prop] = val + (typeof val === 'string' ? '' : 'px');
924
+ }
925
+ }
926
+ }
927
+
928
+
929
+ function _find(ctx, tagName, iterator) {
930
+ if (ctx) {
931
+ var list = ctx.getElementsByTagName(tagName), i = 0, n = list.length;
932
+
933
+ if (iterator) {
934
+ for (; i < n; i++) {
935
+ iterator(list[i], i);
936
+ }
937
+ }
938
+
939
+ return list;
940
+ }
941
+
942
+ return [];
943
+ }
944
+
945
+
946
+ function _disableDraggable(el) {
947
+ el.draggable = false;
948
+ }
949
+
950
+
951
+ function _unsilent() {
952
+ _silent = false;
953
+ }
954
+
955
+
956
+ /** @returns {HTMLElement|false} */
957
+ function _ghostInBottom(el, evt) {
958
+ var lastEl = el.lastElementChild, rect = lastEl.getBoundingClientRect();
959
+ return (evt.clientY - (rect.top + rect.height) > 5) && lastEl; // min delta
960
+ }
961
+
962
+
963
+ /**
964
+ * Generate id
965
+ * @param {HTMLElement} el
966
+ * @returns {String}
967
+ * @private
968
+ */
969
+ function _generateId(el) {
970
+ var str = el.tagName + el.className + el.src + el.href + el.textContent,
971
+ i = str.length,
972
+ sum = 0;
973
+
974
+ while (i--) {
975
+ sum += str.charCodeAt(i);
976
+ }
977
+
978
+ return sum.toString(36);
979
+ }
980
+
981
+ /**
982
+ * Returns the index of an element within its parent
983
+ * @param el
984
+ * @returns {number}
985
+ * @private
986
+ */
987
+ function _index(/**HTMLElement*/el) {
988
+ var index = 0;
989
+ while (el && (el = el.previousElementSibling)) {
990
+ if (el.nodeName.toUpperCase() !== 'TEMPLATE') {
991
+ index++;
992
+ }
993
+ }
994
+ return index;
995
+ }
996
+
997
+ function _throttle(callback, ms) {
998
+ var args, _this;
999
+
1000
+ return function () {
1001
+ if (args === void 0) {
1002
+ args = arguments;
1003
+ _this = this;
1004
+
1005
+ setTimeout(function () {
1006
+ if (args.length === 1) {
1007
+ callback.call(_this, args[0]);
1008
+ } else {
1009
+ callback.apply(_this, args);
1010
+ }
1011
+
1012
+ args = void 0;
1013
+ }, ms);
1014
+ }
1015
+ };
1016
+ }
1017
+
1018
+
1019
+ // Export utils
1020
+ Sortable.utils = {
1021
+ on: _on,
1022
+ off: _off,
1023
+ css: _css,
1024
+ find: _find,
1025
+ bind: _bind,
1026
+ is: function (el, selector) {
1027
+ return !!_closest(el, selector, el);
1028
+ },
1029
+ throttle: _throttle,
1030
+ closest: _closest,
1031
+ toggleClass: _toggleClass,
1032
+ dispatchEvent: _dispatchEvent,
1033
+ index: _index
1034
+ };
1035
+
1036
+
1037
+ Sortable.version = '1.1.1';
1038
+
1039
+
1040
+ /**
1041
+ * Create sortable instance
1042
+ * @param {HTMLElement} el
1043
+ * @param {Object} [options]
1044
+ */
1045
+ Sortable.create = function (el, options) {
1046
+ return new Sortable(el, options);
1047
+ };
1048
+
1049
+ // Export
1050
+ return Sortable;
1051
+ });