Mercurial > nebulaweb3
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 '&': '&', | |
335 '<': '<', | |
336 '>': '>', | |
337 '"': '"', | |
338 "'": ''' | |
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); |