comparison default/node_modules/nestable2/a.js @ 0:1d038bc9b3d2 default tip

Up:default
author Liny <dev@neowd.com>
date Sat, 31 May 2025 09:21:51 +0800
parents
children
comparison
equal deleted inserted replaced
-1:000000000000 0:1d038bc9b3d2
1 /*!
2 * Nestable jQuery Plugin - Copyright (c) 2014 Ramon Smit - https://github.com/RamonSmit/Nestable
3 */
4
5 (function($, window, document, undefined) {
6 var hasTouch = 'ontouchstart' in document;
7
8 /**
9 * Detect CSS pointer-events property
10 * events are normally disabled on the dragging element to avoid conflicts
11 * https://github.com/ausi/Feature-detection-technique-for-pointer-events/blob/master/modernizr-pointerevents.js
12 */
13 var hasPointerEvents = (function() {
14 var el = document.createElement('div'),
15 docEl = document.documentElement;
16 if (!('pointerEvents' in el.style)) {
17 return false;
18 }
19 el.style.pointerEvents = 'auto';
20 el.style.pointerEvents = 'x';
21 docEl.appendChild(el);
22 var supports = window.getComputedStyle && window.getComputedStyle(el, '').pointerEvents === 'auto';
23 docEl.removeChild(el);
24 return !!supports;
25 })();
26
27 var defaults = {
28 contentCallback: function(item) {return item.content || '' ? item.content : item.id;},
29 listNodeName: 'ol',
30 itemNodeName: 'li',
31 handleNodeName: 'div',
32 contentNodeName: 'span',
33 rootClass: 'dd',
34 listClass: 'dd-list',
35 itemClass: 'dd-item',
36 dragClass: 'dd-dragel',
37 handleClass: 'dd-handle',
38 contentClass: 'dd-content',
39 collapsedClass: 'dd-collapsed',
40 placeClass: 'dd-placeholder',
41 noDragClass: 'dd-nodrag',
42 noChildrenClass: 'dd-nochildren',
43 emptyClass: 'dd-empty',
44 expandBtnHTML: '<button class="dd-expand" data-action="expand" type="button">Expand</button>',
45 collapseBtnHTML: '<button class="dd-collapse" data-action="collapse" type="button">Collapse</button>',
46 group: 0,
47 maxDepth: 5,
48 threshold: 20,
49 fixedDepth: false, //fixed item's depth
50 fixed: false,
51 includeContent: false,
52 scroll: false,
53 scrollSensitivity: 1,
54 scrollSpeed: 5,
55 scrollTriggers: {
56 top: 40,
57 left: 40,
58 right: -40,
59 bottom: -40
60 },
61 callback: function(l, e, p) {},
62 onDragStart: function(l, e, p) {},
63 beforeDragStop: function(l, e, p) {},
64 listRenderer: function(children, options) {
65 var html = '<' + options.listNodeName + ' class="' + options.listClass + '">';
66 html += children;
67 html += '</' + options.listNodeName + '>';
68
69 return html;
70 },
71 itemRenderer: function(item_attrs, content, children, options, item) {
72 var item_attrs_string = $.map(item_attrs, function(value, key) {
73 return ' ' + key + '="' + value + '"';
74 }).join(' ');
75
76 var html = '<' + options.itemNodeName + item_attrs_string + '>';
77 html += '<' + options.handleNodeName + ' class="' + options.handleClass + '">';
78 html += '<' + options.contentNodeName + ' class="' + options.contentClass + '">';
79 html += content;
80 html += '</' + options.contentNodeName + '>';
81 html += '</' + options.handleNodeName + '>';
82 html += children;
83 html += '</' + options.itemNodeName + '>';
84
85 return html;
86 }
87 };
88
89 function Plugin(element, options) {
90 this.w = $(document);
91 this.el = $(element);
92 options = options || defaults;
93
94 if (options.rootClass !== undefined && options.rootClass !== 'dd') {
95 options.listClass = options.listClass ? options.listClass : options.rootClass + '-list';
96 options.itemClass = options.itemClass ? options.itemClass : options.rootClass + '-item';
97 options.dragClass = options.dragClass ? options.dragClass : options.rootClass + '-dragel';
98 options.handleClass = options.handleClass ? options.handleClass : options.rootClass + '-handle';
99 options.collapsedClass = options.collapsedClass ? options.collapsedClass : options.rootClass + '-collapsed';
100 options.placeClass = options.placeClass ? options.placeClass : options.rootClass + '-placeholder';
101 options.noDragClass = options.noDragClass ? options.noDragClass : options.rootClass + '-nodrag';
102 options.noChildrenClass = options.noChildrenClass ? options.noChildrenClass : options.rootClass + '-nochildren';
103 options.emptyClass = options.emptyClass ? options.emptyClass : options.rootClass + '-empty';
104 }
105
106 this.options = $.extend({}, defaults, options);
107
108 // build HTML from serialized JSON if passed
109 if (this.options.json !== undefined) {
110 this._build();
111 }
112
113 this.init();
114 }
115
116 Plugin.prototype = {
117
118 init: function() {
119 var list = this;
120
121 list.reset();
122
123 list.el.data('nestable-group', this.options.group);
124
125 list.placeEl = $('<div class="' + list.options.placeClass + '"/>');
126
127 var items = this.el.find(list.options.itemNodeName);
128 $.each(items, function(k, el) {
129 var item = $(el),
130 parent = item.parent();
131 list.setParent(item);
132 if (parent.hasClass(list.options.collapsedClass)) {
133 list.collapseItem(parent.parent());
134 }
135 });
136
137 // Append the .dd-empty div if the list don't have any items on init
138 if (!items.length) {
139 this.appendEmptyElement(this.el);
140 }
141
142 list.el.on('click', 'button', function(e) {
143 if (list.dragEl) {
144 return;
145 }
146 var target = $(e.currentTarget),
147 action = target.data('action'),
148 item = target.parents(list.options.itemNodeName).eq(0);
149 if (action === 'collapse') {
150 list.collapseItem(item);
151 }
152 if (action === 'expand') {
153 list.expandItem(item);
154 }
155 });
156
157 var onStartEvent = function(e) {
158 var handle = $(e.target);
159 if (!handle.hasClass(list.options.handleClass)) {
160 if (handle.closest('.' + list.options.noDragClass).length) {
161 return;
162 }
163 handle = handle.closest('.' + list.options.handleClass);
164 }
165 if (!handle.length || list.dragEl) {
166 return;
167 }
168
169 list.isTouch = /^touch/.test(e.type);
170 if (list.isTouch && e.touches.length !== 1) {
171 return;
172 }
173
174 e.preventDefault();
175 list.dragStart(e.touches ? e.touches[0] : e);
176 };
177
178 var onMoveEvent = function(e) {
179 if (list.dragEl) {
180 e.preventDefault();
181 list.dragMove(e.touches ? e.touches[0] : e);
182 }
183 };
184
185 var onEndEvent = function(e) {
186 if (list.dragEl) {
187 e.preventDefault();
188 list.dragStop(e.touches ? e.changedTouches[0] : e);
189 }
190 };
191
192 if (hasTouch) {
193 list.el[0].addEventListener('touchstart', onStartEvent, false);
194 window.addEventListener('touchmove', onMoveEvent, false);
195 window.addEventListener('touchend', onEndEvent, false);
196 window.addEventListener('touchcancel', onEndEvent, false);
197 }
198
199 list.el.on('mousedown', onStartEvent);
200 list.w.on('mousemove', onMoveEvent);
201 list.w.on('mouseup', onEndEvent);
202
203 var destroyNestable = function()
204 {
205 if (hasTouch) {
206 list.el[0].removeEventListener('touchstart', onStartEvent, false);
207 window.removeEventListener('touchmove', onMoveEvent, false);
208 window.removeEventListener('touchend', onEndEvent, false);
209 window.removeEventListener('touchcancel', onEndEvent, false);
210 }
211
212 list.el.off('mousedown', onStartEvent);
213 list.w.off('mousemove', onMoveEvent);
214 list.w.off('mouseup', onEndEvent);
215
216 list.el.off('click');
217 list.el.unbind('destroy-nestable');
218
219 list.el.data("nestable", null);
220 };
221
222 list.el.bind('destroy-nestable', destroyNestable);
223
224 },
225
226 destroy: function ()
227 {
228 this.el.trigger('destroy-nestable');
229 },
230
231 add: function (item)
232 {
233 var listClassSelector = '.' + this.options.listClass;
234 var tree = $(this.el).children(listClassSelector);
235
236 if (item.parent_id !== undefined) {
237 tree = tree.find('[data-id="' + item.parent_id + '"]');
238 delete item.parent_id;
239
240 if (tree.children(listClassSelector).length === 0) {
241 tree = tree.append(this.options.listRenderer('', this.options));
242 }
243
244 tree = tree.find(listClassSelector);
245 this.setParent(tree.parent());
246 }
247
248 tree.append(this._buildItem(item, this.options));
249 },
250
251 replace: function (item)
252 {
253 var html = this._buildItem(item, this.options);
254
255 this._getItemById(item.id)
256 .replaceWith(html);
257 },
258
259 //use fade = 'fade' to fadeout item before removing.
260 //by using time(string/msecs), you can control animation speed, default is jq 'slow'
261 remove: function (itemId, fade, time)
262 {
263 var opts = this.options,
264 el = this.el,
265 item = this._getItemById(itemId);
266
267 //animation time
268 time = time || 'slow';
269
270 //removes item and additional elements from list
271 function removeItem(item) {
272
273 // remove item
274 item = item || this;
275 item.remove();
276
277 // remove empty children lists
278 var emptyListsSelector = '.' + opts.listClass
279 + ' .' + opts.listClass + ':not(:has(*))';
280 $(el).find(emptyListsSelector).remove();
281
282 // remove buttons if parents do not have children
283 var buttonsSelector = '[data-action="expand"], [data-action="collapse"]';
284 $(el).find(buttonsSelector).each(function() {
285 var siblings = $(this).siblings('.' + opts.listClass);
286 if (siblings.length === 0) {
287 $(this).remove();
288 }
289 });
290 }
291
292 //Setting fade to true, adds fadeOut effect to removing.
293 if (fade === 'fade') {
294 item.fadeOut(time, removeItem);
295 }
296 else {
297 removeItem(item);
298 }
299 },
300
301 _getItemById: function(itemId) {
302 return $(this.el).children('.' + this.options.listClass)
303 .find('[data-id="' + itemId + '"]');
304 },
305
306 _build: function() {
307 var json = this.options.json;
308
309 if (typeof json === 'string') {
310 json = JSON.parse(json);
311 }
312
313 $(this.el).html(this._buildList(json, this.options));
314 },
315
316 _buildList: function(items, options) {
317 if (!items) {
318 return '';
319 }
320
321 var children = '';
322 var that = this;
323
324 $.each(items, function(index, sub) {
325 children += that._buildItem(sub, options);
326 });
327
328 return options.listRenderer(children, options);
329 },
330
331 _buildItem: function(item, options) {
332 function escapeHtml(text) {
333 var map = {
334 '&': '&amp;',
335 '<': '&lt;',
336 '>': '&gt;',
337 '"': '&quot;',
338 "'": '&#039;'
339 };
340
341 return text + "".replace(/[&<>"']/g, function(m) { return map[m]; });
342 }
343
344 function filterClasses(classes) {
345 var new_classes = {};
346
347 for (var k in classes) {
348 // Remove duplicates
349 new_classes[classes[k]] = classes[k];
350 }
351
352 return new_classes;
353 }
354
355 function createClassesString(item, options) {
356 var classes = item.classes || {};
357
358 if (typeof classes === 'string') {
359 classes = [classes];
360 }
361
362 var item_classes = filterClasses(classes);
363 item_classes[options.itemClass] = options.itemClass;
364
365 // create class string
366 return $.map(item_classes, function(val) {
367 return val;
368 }).join(' ');
369 }
370
371 function createDataAttrs(attr) {
372 attr = $.extend({}, attr);
373
374 delete attr.children;
375 delete attr.classes;
376 delete attr.content;
377
378 var data_attrs = {};
379
380 $.each(attr, function(key, value) {
381 if (typeof value === 'object') {
382 value = JSON.stringify(value);
383 }
384
385 data_attrs["data-" + key] = escapeHtml(value);
386 });
387
388 return data_attrs;
389 }
390
391 var item_attrs = createDataAttrs(item);
392 item_attrs["class"] = createClassesString(item, options);
393
394 var content = options.contentCallback(item);
395 var children = this._buildList(item.children, options);
396 var html = $(options.itemRenderer(item_attrs, content, children, options, item));
397
398 this.setParent(html);
399
400 return html[0].outerHTML;
401 },
402
403 serialize: function() {
404 var data, list = this, step = function(level) {
405 var array = [],
406 items = level.children(list.options.itemNodeName);
407 items.each(function() {
408 var li = $(this),
409 item = $.extend({}, li.data()),
410 sub = li.children(list.options.listNodeName);
411
412 if (list.options.includeContent) {
413 var content = li.find('.' + list.options.contentClass).html();
414
415 if (content) {
416 item.content = content;
417 }
418 }
419
420 if (sub.length) {
421 item.children = step(sub);
422 }
423 array.push(item);
424 });
425 return array;
426 };
427 data = step(list.el.find(list.options.listNodeName).first());
428 return data;
429 },
430
431 asNestedSet: function() {
432 var list = this, o = list.options, depth = -1, ret = [], lft = 1;
433 var items = list.el.find(o.listNodeName).first().children(o.itemNodeName);
434
435 items.each(function () {
436 lft = traverse(this, depth + 1, lft);
437 });
438
439 ret = ret.sort(function(a,b){ return (a.lft - b.lft); });
440 return ret;
441
442 function traverse(item, depth, lft) {
443 var rgt = lft + 1, id, pid;
444
445 if ($(item).children(o.listNodeName).children(o.itemNodeName).length > 0 ) {
446 depth++;
447 $(item).children(o.listNodeName).children(o.itemNodeName).each(function () {
448 rgt = traverse($(this), depth, rgt);
449 });
450 depth--;
451 }
452
453 id = $(item).attr('data-id');
454 pid = $(item).parent(o.listNodeName).parent(o.itemNodeName).attr('data-id') || '';
455
456 if ($.isNumeric(id)) {
457 id = parseInt(id);
458 }
459
460 if ($.isNumeric(pid)) {
461 pid = parseInt(pid);
462 }
463
464 if (id) {
465 ret.push({"id": id, "parent_id": pid, "depth": depth, "lft": lft, "rgt": rgt});
466 }
467
468 lft = rgt + 1;
469 return lft;
470 }
471 },
472
473 returnOptions: function() {
474 return this.options;
475 },
476
477 serialise: function() {
478 return this.serialize();
479 },
480
481 toHierarchy: function(options) {
482
483 var o = $.extend({}, this.options, options),
484 ret = [];
485
486 $(this.element).children(o.items).each(function() {
487 var level = _recursiveItems(this);
488 ret.push(level);
489 });
490
491 return ret;
492
493 function _recursiveItems(item) {
494 var id = ($(item).attr(o.attribute || 'id') || '').match(o.expression || (/(.+)[-=_](.+)/));
495 if (id) {
496 var currentItem = {
497 "id": id[2]
498 };
499 if ($(item).children(o.listType).children(o.items).length > 0) {
500 currentItem.children = [];
501 $(item).children(o.listType).children(o.items).each(function() {
502 var level = _recursiveItems(this);
503 currentItem.children.push(level);
504 });
505 }
506 return currentItem;
507 }
508 }
509 },
510
511 toArray: function() {
512
513 var o = $.extend({}, this.options, this),
514 sDepth = o.startDepthCount || 0,
515 ret = [],
516 left = 2,
517 list = this,
518 element = list.el.find(list.options.listNodeName).first();
519
520 var items = element.children(list.options.itemNodeName);
521 items.each(function() {
522 left = _recursiveArray($(this), sDepth + 1, left);
523 });
524
525 ret = ret.sort(function(a, b) {
526 return (a.left - b.left);
527 });
528
529 return ret;
530
531 function _recursiveArray(item, depth, left) {
532
533 var right = left + 1,
534 id,
535 pid;
536
537 if (item.children(o.options.listNodeName).children(o.options.itemNodeName).length > 0) {
538 depth++;
539 item.children(o.options.listNodeName).children(o.options.itemNodeName).each(function() {
540 right = _recursiveArray($(this), depth, right);
541 });
542 depth--;
543 }
544
545 id = item.data().id;
546
547
548 if (depth === sDepth + 1) {
549 pid = o.rootID;
550 } else {
551
552 var parentItem = (item.parent(o.options.listNodeName)
553 .parent(o.options.itemNodeName)
554 .data());
555 pid = parentItem.id;
556
557 }
558
559 if (id) {
560 ret.push({
561 "id": id,
562 "parent_id": pid,
563 "depth": depth,
564 "left": left,
565 "right": right
566 });
567 }
568
569 left = right + 1;
570 return left;
571 }
572
573 },
574
575 reset: function() {
576 this.mouse = {
577 offsetX: 0,
578 offsetY: 0,
579 startX: 0,
580 startY: 0,
581 lastX: 0,
582 lastY: 0,
583 nowX: 0,
584 nowY: 0,
585 distX: 0,
586 distY: 0,
587 dirAx: 0,
588 dirX: 0,
589 dirY: 0,
590 lastDirX: 0,
591 lastDirY: 0,
592 distAxX: 0,
593 distAxY: 0
594 };
595 this.isTouch = false;
596 this.moving = false;
597 this.dragEl = null;
598 this.dragRootEl = null;
599 this.dragDepth = 0;
600 this.hasNewRoot = false;
601 this.pointEl = null;
602 },
603
604 expandItem: function(li) {
605 li.removeClass(this.options.collapsedClass);
606 },
607
608 collapseItem: function(li) {
609 var lists = li.children(this.options.listNodeName);
610 if (lists.length) {
611 li.addClass(this.options.collapsedClass);
612 }
613 },
614
615 expandAll: function() {
616 var list = this;
617 list.el.find(list.options.itemNodeName).each(function() {
618 list.expandItem($(this));
619 });
620 },
621
622 collapseAll: function() {
623 var list = this;
624 list.el.find(list.options.itemNodeName).each(function() {
625 list.collapseItem($(this));
626 });
627 },
628
629 setParent: function(li) {
630 //Check if li is an element of itemNodeName type and has children
631 if (li.is(this.options.itemNodeName) && li.children(this.options.listNodeName).length) {
632 // make sure NOT showing two or more sets data-action buttons
633 li.children('[data-action]').remove();
634 li.prepend($(this.options.expandBtnHTML));
635 li.prepend($(this.options.collapseBtnHTML));
636 }
637 },
638
639 unsetParent: function(li) {
640 li.removeClass(this.options.collapsedClass);
641 li.children('[data-action]').remove();
642 li.children(this.options.listNodeName).remove();
643 },
644
645 dragStart: function(e) {
646 var mouse = this.mouse,
647 target = $(e.target),
648 dragItem = target.closest(this.options.itemNodeName);
649
650 var position = {};
651 position.top = e.pageY;
652 position.left = e.pageX;
653
654 var continueExecution = this.options.onDragStart.call(this, this.el, dragItem, position);
655
656 if (typeof continueExecution !== 'undefined' && continueExecution === false) {
657 return;
658 }
659
660 this.placeEl.css('height', dragItem.height());
661
662 mouse.offsetX = e.pageX - dragItem.offset().left;
663 mouse.offsetY = e.pageY - dragItem.offset().top;
664 mouse.startX = mouse.lastX = e.pageX;
665 mouse.startY = mouse.lastY = e.pageY;
666
667 this.dragRootEl = this.el;
668 this.dragEl = $(document.createElement(this.options.listNodeName)).addClass(this.options.listClass + ' ' + this.options.dragClass);
669 this.dragEl.css('width', dragItem.outerWidth());
670
671 this.setIndexOfItem(dragItem);
672
673 // fix for zepto.js
674 //dragItem.after(this.placeEl).detach().appendTo(this.dragEl);
675 dragItem.after(this.placeEl);
676 dragItem[0].parentNode.removeChild(dragItem[0]);
677 dragItem.appendTo(this.dragEl);
678
679 $(document.body).append(this.dragEl);
680 this.dragEl.css({
681 'left': e.pageX - mouse.offsetX,
682 'top': e.pageY - mouse.offsetY
683 });
684 // total depth of dragging item
685 var i, depth,
686 items = this.dragEl.find(this.options.itemNodeName);
687 for (i = 0; i < items.length; i++) {
688 depth = $(items[i]).parents(this.options.listNodeName).length;
689 if (depth > this.dragDepth) {
690 this.dragDepth = depth;
691 }
692 }
693 },
694
695 //Create sublevel.
696 // element : element which become parent
697 // item : something to place into new sublevel
698 createSubLevel: function(element, item) {
699 var list = $('<' + this.options.listNodeName + '/>').addClass(this.options.listClass);
700 if (item) list.append(item);
701 element.append(list);
702 this.setParent(element);
703 return list;
704 },
705
706 setIndexOfItem: function(item, index) {
707 index = index || [];
708
709 index.unshift(item.index());
710
711 if ($(item[0].parentNode)[0] !== this.dragRootEl[0]) {
712 this.setIndexOfItem($(item[0].parentNode), index);
713 }
714 else {
715 this.dragEl.data('indexOfItem', index);
716 }
717 },
718
719 restoreItemAtIndex: function(dragElement, indexArray) {
720 var currentEl = this.el,
721 lastIndex = indexArray.length - 1;
722
723 //Put drag element at current element position.
724 function placeElement(currentEl, dragElement) {
725 if (indexArray[lastIndex] === 0) {
726 $(currentEl).prepend(dragElement.clone());
727 } else {
728 $(currentEl.children[indexArray[lastIndex] - 1]).after(dragElement.clone());
729 }
730 }
731 //Diggin through indexArray to get home for dragElement.
732 for (var i = 0; i < indexArray.length; i++) {
733 if (lastIndex === parseInt(i)) {
734 placeElement(currentEl, dragElement);
735 return;
736 }
737 //element can have no indexes, so we have to use conditional here to avoid errors.
738 //if element doesn't exist we defenetly need to add new list.
739 var element = (currentEl[0]) ? currentEl[0] : currentEl;
740 var nextEl = element.children[indexArray[i]];
741 currentEl = (!nextEl) ? this.createSubLevel($(element)) : nextEl;
742 }
743 },
744
745 dragStop: function(e) {
746 // fix for zepto.js
747 //this.placeEl.replaceWith(this.dragEl.children(this.options.itemNodeName + ':first').detach());
748 var position = {
749 top : e.pageY,
750 left : e.pageX
751 };
752 //Get indexArray of item at drag start.
753 var srcIndex = this.dragEl.data('indexOfItem');
754
755 var el = this.dragEl.children(this.options.itemNodeName).first();
756
757 el[0].parentNode.removeChild(el[0]);
758
759 this.dragEl.remove(); //Remove dragEl, cause it can affect on indexing in html collection.
760
761 //Before drag stop callback
762 var continueExecution = this.options.beforeDragStop.call(this, this.el, el, this.placeEl.parent());
763 if (typeof continueExecution !== 'undefined' && continueExecution === false) {
764 var parent = this.placeEl.parent();
765 this.placeEl.remove();
766 if (!parent.children().length) {
767 this.unsetParent(parent.parent());
768 }
769 this.restoreItemAtIndex(el, srcIndex);
770 this.reset();
771 return;
772 }
773
774 this.placeEl.replaceWith(el);
775
776 if (this.hasNewRoot) {
777 if (this.options.fixed === true) {
778 this.restoreItemAtIndex(el, srcIndex);
779 }
780 else {
781 this.el.trigger('lostItem');
782 }
783 this.dragRootEl.trigger('gainedItem');
784 }
785 else {
786 this.dragRootEl.trigger('change');
787 }
788
789 this.options.callback.call(this, this.dragRootEl, el, position);
790
791 this.reset();
792 },
793
794 dragMove: function(e) {
795 var list, parent, prev, next, depth,
796 opt = this.options,
797 mouse = this.mouse;
798
799 this.dragEl.css({
800 'left': e.pageX - mouse.offsetX,
801 'top': e.pageY - mouse.offsetY
802 });
803
804 // mouse position last events
805 mouse.lastX = mouse.nowX;
806 mouse.lastY = mouse.nowY;
807 // mouse position this events
808 mouse.nowX = e.pageX;
809 mouse.nowY = e.pageY;
810 // distance mouse moved between events
811 mouse.distX = mouse.nowX - mouse.lastX;
812 mouse.distY = mouse.nowY - mouse.lastY;
813 // direction mouse was moving
814 mouse.lastDirX = mouse.dirX;
815 mouse.lastDirY = mouse.dirY;
816 // direction mouse is now moving (on both axis)
817 mouse.dirX = mouse.distX === 0 ? 0 : mouse.distX > 0 ? 1 : -1;
818 mouse.dirY = mouse.distY === 0 ? 0 : mouse.distY > 0 ? 1 : -1;
819 // axis mouse is now moving on
820 var newAx = Math.abs(mouse.distX) > Math.abs(mouse.distY) ? 1 : 0;
821
822 // do nothing on first move
823 if (!mouse.moving) {
824 mouse.dirAx = newAx;
825 mouse.moving = true;
826 return;
827 }
828
829 // do scrolling if enable
830 if (opt.scroll) {
831 if (typeof window.jQuery.fn.scrollParent !== 'undefined') {
832 var scrolled = false;
833 var scrollParent = this.el.scrollParent()[0];
834 if (scrollParent !== document && scrollParent.tagName !== 'HTML') {
835 if ((opt.scrollTriggers.bottom + scrollParent.offsetHeight) - e.pageY < opt.scrollSensitivity)
836 scrollParent.scrollTop = scrolled = scrollParent.scrollTop + opt.scrollSpeed;
837 else if (e.pageY - opt.scrollTriggers.top < opt.scrollSensitivity)
838 scrollParent.scrollTop = scrolled = scrollParent.scrollTop - opt.scrollSpeed;
839
840 if ((opt.scrollTriggers.right + scrollParent.offsetWidth) - e.pageX < opt.scrollSensitivity)
841 scrollParent.scrollLeft = scrolled = scrollParent.scrollLeft + opt.scrollSpeed;
842 else if (e.pageX - opt.scrollTriggers.left < opt.scrollSensitivity)
843 scrollParent.scrollLeft = scrolled = scrollParent.scrollLeft - opt.scrollSpeed;
844 } else {
845 if (e.pageY - $(document).scrollTop() < opt.scrollSensitivity)
846 scrolled = $(document).scrollTop($(document).scrollTop() - opt.scrollSpeed);
847 else if ($(window).height() - (e.pageY - $(document).scrollTop()) < opt.scrollSensitivity)
848 scrolled = $(document).scrollTop($(document).scrollTop() + opt.scrollSpeed);
849
850 if (e.pageX - $(document).scrollLeft() < opt.scrollSensitivity)
851 scrolled = $(document).scrollLeft($(document).scrollLeft() - opt.scrollSpeed);
852 else if ($(window).width() - (e.pageX - $(document).scrollLeft()) < opt.scrollSensitivity)
853 scrolled = $(document).scrollLeft($(document).scrollLeft() + opt.scrollSpeed);
854 }
855 } else {
856 console.warn('To use scrolling you need to have scrollParent() function, check documentation for more information');
857 }
858 }
859
860 if (this.scrollTimer) {
861 clearTimeout(this.scrollTimer);
862 }
863
864 if (opt.scroll && scrolled) {
865 this.scrollTimer = setTimeout(function() {
866 $(window).trigger(e);
867 }, 10);
868 }
869
870 // calc distance moved on this axis (and direction)
871 if (mouse.dirAx !== newAx) {
872 mouse.distAxX = 0;
873 mouse.distAxY = 0;
874 }
875 else {
876 mouse.distAxX += Math.abs(mouse.distX);
877 if (mouse.dirX !== 0 && mouse.dirX !== mouse.lastDirX) {
878 mouse.distAxX = 0;
879 }
880 mouse.distAxY += Math.abs(mouse.distY);
881 if (mouse.dirY !== 0 && mouse.dirY !== mouse.lastDirY) {
882 mouse.distAxY = 0;
883 }
884 }
885 mouse.dirAx = newAx;
886
887 /**
888 * move horizontal
889 */
890 if (mouse.dirAx && mouse.distAxX >= opt.threshold) {
891 // reset move distance on x-axis for new phase
892 mouse.distAxX = 0;
893 prev = this.placeEl.prev(opt.itemNodeName);
894 // increase horizontal level if previous sibling exists, is not collapsed, and can have children
895 if (mouse.distX > 0 && prev.length && !prev.hasClass(opt.collapsedClass) && !prev.hasClass(opt.noChildrenClass)) {
896 // cannot increase level when item above is collapsed
897 list = prev.find(opt.listNodeName).last();
898 // check if depth limit has reached
899 depth = this.placeEl.parents(opt.listNodeName).length;
900 if (depth + this.dragDepth <= opt.maxDepth) {
901 // create new sub-level if one doesn't exist
902 if (!list.length) {
903 this.createSubLevel(prev, this.placeEl);
904 }
905 else {
906 // else append to next level up
907 list = prev.children(opt.listNodeName).last();
908 list.append(this.placeEl);
909 }
910 }
911 }
912 // decrease horizontal level
913 if (mouse.distX < 0) {
914 // we can't decrease a level if an item preceeds the current one
915 next = this.placeEl.next(opt.itemNodeName);
916 if (!next.length) {
917 parent = this.placeEl.parent();
918 this.placeEl.closest(opt.itemNodeName).after(this.placeEl);
919 if (!parent.children().length) {
920 this.unsetParent(parent.parent());
921 }
922 }
923 }
924 }
925
926 var isEmpty = false;
927
928 // find list item under cursor
929 if (!hasPointerEvents) {
930 this.dragEl[0].style.visibility = 'hidden';
931 }
932 this.pointEl = $(document.elementFromPoint(e.pageX - document.body.scrollLeft, e.pageY - (window.pageYOffset || document.documentElement.scrollTop)));
933 if (!hasPointerEvents) {
934 this.dragEl[0].style.visibility = 'visible';
935 }
936 if (this.pointEl.hasClass(opt.handleClass)) {
937 this.pointEl = this.pointEl.closest(opt.itemNodeName);
938 }
939 if (this.pointEl.hasClass(opt.emptyClass)) {
940 isEmpty = true;
941 }
942 else if (!this.pointEl.length || !this.pointEl.hasClass(opt.itemClass)) {
943 return;
944 }
945
946 // find parent list of item under cursor
947 var pointElRoot = this.pointEl.closest('.' + opt.rootClass),
948 isNewRoot = this.dragRootEl.data('nestable-id') !== pointElRoot.data('nestable-id');
949
950 /**
951 * move vertical
952 */
953 if (!mouse.dirAx || isNewRoot || isEmpty) {
954 // check if groups match if dragging over new root
955 if (isNewRoot && opt.group !== pointElRoot.data('nestable-group')) {
956 return;
957 }
958
959 // fixed item's depth, use for some list has specific type, eg:'Volume, Section, Chapter ...'
960 if (this.options.fixedDepth && this.dragDepth + 1 !== this.pointEl.parents(opt.listNodeName).length) {
961 return;
962 }
963
964 // check depth limit
965 depth = this.dragDepth - 1 + this.pointEl.parents(opt.listNodeName).length;
966 if (depth > opt.maxDepth) {
967 return;
968 }
969 var before = e.pageY < (this.pointEl.offset().top + this.pointEl.height() / 2);
970 parent = this.placeEl.parent();
971 // if empty create new list to replace empty placeholder
972 if (isEmpty) {
973 list = $(document.createElement(opt.listNodeName)).addClass(opt.listClass);
974 list.append(this.placeEl);
975 this.pointEl.replaceWith(list);
976 }
977 else if (before) {
978 this.pointEl.before(this.placeEl);
979 }
980 else {
981 this.pointEl.after(this.placeEl);
982 }
983 if (!parent.children().length) {
984 this.unsetParent(parent.parent());
985 }
986 if (!this.dragRootEl.find(opt.itemNodeName).length) {
987 this.appendEmptyElement(this.dragRootEl);
988 }
989 // parent root list has changed
990 this.dragRootEl = pointElRoot;
991 if (isNewRoot) {
992 this.hasNewRoot = this.el[0] !== this.dragRootEl[0];
993 }
994 }
995 },
996 // Append the .dd-empty div to the list so it can be populated and styled
997 appendEmptyElement: function(element) {
998 element.append('<div class="' + this.options.emptyClass + '"/>');
999 }
1000 };
1001
1002 $.fn.nestable = function(params) {
1003 var lists = this,
1004 retval = this,
1005 args = arguments;
1006
1007 if (!('Nestable' in window)) {
1008 window.Nestable = {};
1009 Nestable.counter = 0;
1010 }
1011
1012 lists.each(function() {
1013 var plugin = $(this).data("nestable");
1014
1015 if (!plugin) {
1016 Nestable.counter++;
1017 $(this).data("nestable", new Plugin(this, params));
1018 $(this).data("nestable-id", Nestable.counter);
1019 }
1020 else {
1021 if (typeof params === 'string' && typeof plugin[params] === 'function') {
1022 if (args.length > 1){
1023 var pluginArgs = [];
1024 for (var i = 1; i < args.length; i++) {
1025 pluginArgs.push(args[i]);
1026 }
1027 retval = plugin[params].apply(plugin, pluginArgs);
1028 }
1029 else {
1030 retval = plugin[params]();
1031 }
1032 }
1033 }
1034 });
1035
1036 return retval || lists;
1037 };
1038
1039 })(window.jQuery || window.Zepto, window, document);