summaryrefslogtreecommitdiff
path: root/js/components/nestable.js
diff options
context:
space:
mode:
Diffstat (limited to 'js/components/nestable.js')
-rwxr-xr-xjs/components/nestable.js653
1 files changed, 0 insertions, 653 deletions
diff --git a/js/components/nestable.js b/js/components/nestable.js
deleted file mode 100755
index 573345d..0000000
--- a/js/components/nestable.js
+++ /dev/null
@@ -1,653 +0,0 @@
1/*! UIkit 2.26.4 | http://www.getuikit.com | (c) 2014 YOOtheme | MIT License */
2/*
3 * Based on Nestable jQuery Plugin - Copyright (c) 2012 David Bushell - http://dbushell.com/
4 */
5(function(addon) {
6
7 var component;
8
9 if (window.UIkit) {
10 component = addon(UIkit);
11 }
12
13 if (typeof define == "function" && define.amd) {
14 define("uikit-nestable", ["uikit"], function(){
15 return component || addon(UIkit);
16 });
17 }
18
19})(function(UI) {
20
21 "use strict";
22
23 var hasTouch = 'ontouchstart' in window,
24 html = UI.$html,
25 touchedlists = [],
26 $win = UI.$win,
27 draggingElement;
28
29 var eStart = hasTouch ? 'touchstart' : 'mousedown',
30 eMove = hasTouch ? 'touchmove' : 'mousemove',
31 eEnd = hasTouch ? 'touchend' : 'mouseup',
32 eCancel = hasTouch ? 'touchcancel' : 'mouseup';
33
34
35 UI.component('nestable', {
36
37 defaults: {
38 listBaseClass : 'uk-nestable',
39 listClass : 'uk-nestable-list',
40 listItemClass : 'uk-nestable-item',
41 dragClass : 'uk-nestable-dragged',
42 movingClass : 'uk-nestable-moving',
43 noChildrenClass : 'uk-nestable-nochildren',
44 emptyClass : 'uk-nestable-empty',
45 handleClass : '',
46 collapsedClass : 'uk-collapsed',
47 placeholderClass: 'uk-nestable-placeholder',
48 noDragClass : 'uk-nestable-nodrag',
49 group : false,
50 maxDepth : 10,
51 threshold : 20,
52 idlethreshold : 10,
53 },
54
55 boot: function() {
56
57 // adjust document scrolling
58 UI.$html.on('mousemove touchmove', function(e) {
59
60 if (draggingElement) {
61
62 var top = draggingElement.offset().top;
63
64 if (top < UI.$win.scrollTop()) {
65 UI.$win.scrollTop(UI.$win.scrollTop() - Math.ceil(draggingElement.height()/2));
66 } else if ( (top + draggingElement.height()) > (window.innerHeight + UI.$win.scrollTop()) ) {
67 UI.$win.scrollTop(UI.$win.scrollTop() + Math.ceil(draggingElement.height()/2));
68 }
69 }
70 });
71
72 // init code
73 UI.ready(function(context) {
74
75 UI.$("[data-uk-nestable]", context).each(function(){
76
77 var ele = UI.$(this);
78
79 if (!ele.data("nestable")) {
80 UI.nestable(ele, UI.Utils.options(ele.attr("data-uk-nestable")));
81 }
82 });
83 });
84 },
85
86 init: function() {
87
88 var $this = this;
89
90 Object.keys(this.options).forEach(function(key){
91
92 if(String(key).indexOf('Class')!=-1) {
93 $this.options['_'+key] = '.' + $this.options[key];
94 }
95 });
96
97 this.find(this.options._listItemClass).find(">ul").addClass(this.options.listClass);
98
99 this.checkEmptyList();
100
101 this.reset();
102 this.element.data('nestable-group', this.options.group || UI.Utils.uid('nestable-group'));
103
104 this.find(this.options._listItemClass).each(function() {
105 $this.setParent(UI.$(this));
106 });
107
108 this.on('click', '[data-nestable-action]', function(e) {
109
110 if ($this.dragEl || (!hasTouch && e.button !== 0)) {
111 return;
112 }
113
114 e.preventDefault();
115
116 var target = UI.$(e.currentTarget),
117 action = target.data('nestableAction'),
118 item = target.closest($this.options._listItemClass);
119
120 if (action === 'collapse') {
121 $this.collapseItem(item);
122 }
123 if (action === 'expand') {
124 $this.expandItem(item);
125 }
126 if (action === 'toggle') {
127 $this.toggleItem(item);
128 }
129 });
130
131 var onStartEvent = function(e) {
132
133 var handle = UI.$(e.target),
134 link = handle.is('a[href]') ? handle:handle.parents('a[href]');
135
136 if (e.target === $this.element[0]) {
137 return;
138 }
139
140 if (handle.is($this.options._noDragClass) || handle.closest($this.options._noDragClass).length) {
141 return;
142 }
143
144 if (handle.is('[data-nestable-action]') || handle.closest('[data-nestable-action]').length) {
145 return;
146 }
147
148 if ($this.options.handleClass && !handle.hasClass($this.options.handleClass)) {
149
150 if ($this.options.handleClass) {
151 handle = handle.closest($this.options._handleClass);
152 }
153 }
154
155 if (!handle.length || $this.dragEl || (!hasTouch && e.button !== 0) || (hasTouch && e.touches.length !== 1)) {
156 return;
157 }
158
159 if (e.originalEvent && e.originalEvent.touches) {
160 e = evt.originalEvent.touches[0];
161 }
162
163 $this.delayMove = function(evt) {
164
165 link = false;
166
167 evt.preventDefault();
168 $this.dragStart(e);
169 $this.trigger('start.uk.nestable', [$this]);
170
171 $this.delayMove = false;
172 };
173
174 $this.delayMove.x = parseInt(e.pageX, 10);
175 $this.delayMove.y = parseInt(e.pageY, 10);
176 $this.delayMove.threshold = $this.options.idlethreshold;
177
178 if (link.length && eEnd == 'touchend') {
179
180 $this.one(eEnd, function(){
181 if (link && link.attr('href').trim()) {
182 location.href = link.attr('href');
183 }
184 });
185 }
186
187 e.preventDefault();
188 };
189
190 var onMoveEvent = function(e) {
191
192 if (e.originalEvent && e.originalEvent.touches) {
193 e = e.originalEvent.touches[0];
194 }
195
196 if ($this.delayMove && (Math.abs(e.pageX - $this.delayMove.x) > $this.delayMove.threshold || Math.abs(e.pageY - $this.delayMove.y) > $this.delayMove.threshold)) {
197
198 if (!window.getSelection().toString()) {
199 $this.delayMove(e);
200 } else {
201 $this.delayMove = false;
202 }
203 }
204
205 if ($this.dragEl) {
206 e.preventDefault();
207 $this.dragMove(e);
208 $this.trigger('move.uk.nestable', [$this]);
209 }
210 };
211
212 var onEndEvent = function(e) {
213
214 if ($this.dragEl) {
215 e.preventDefault();
216 $this.dragStop(hasTouch ? e.touches[0] : e);
217 }
218
219 draggingElement = false;
220 $this.delayMove = false;
221 };
222
223 if (hasTouch) {
224 this.element[0].addEventListener(eStart, onStartEvent, false);
225 window.addEventListener(eMove, onMoveEvent, false);
226 window.addEventListener(eEnd, onEndEvent, false);
227 window.addEventListener(eCancel, onEndEvent, false);
228 } else {
229 this.on(eStart, onStartEvent);
230 $win.on(eMove, onMoveEvent);
231 $win.on(eEnd, onEndEvent);
232 }
233
234 },
235
236 serialize: function() {
237
238 var data,
239 depth = 0,
240 list = this,
241 step = function(level, depth) {
242
243 var array = [ ], items = level.children(list.options._listItemClass);
244
245 items.each(function() {
246
247 var li = UI.$(this),
248 item = {}, attribute,
249 sub = li.children(list.options._listClass);
250
251 for (var i = 0, attr, val; i < li[0].attributes.length; i++) {
252 attribute = li[0].attributes[i];
253 if (attribute.name.indexOf('data-') === 0) {
254 attr = attribute.name.substr(5);
255 val = UI.Utils.str2json(attribute.value);
256 item[attr] = (val || attribute.value=='false' || attribute.value=='0') ? val:attribute.value;
257 }
258 }
259
260 if (sub.length) {
261 item.children = step(sub, depth + 1);
262 }
263
264 array.push(item);
265
266 });
267 return array;
268 };
269
270 data = step(list.element, depth);
271
272 return data;
273 },
274
275 list: function(options) {
276
277 var data = [],
278 list = this,
279 depth = 0,
280 step = function(level, depth, parent) {
281
282 var items = level.children(options._listItemClass);
283
284 items.each(function(index) {
285 var li = UI.$(this),
286 item = UI.$.extend({parent_id: (parent ? parent : null), depth: depth, order: index}, li.data()),
287 sub = li.children(options._listClass);
288
289 data.push(item);
290
291 if (sub.length) {
292 step(sub, depth + 1, li.data(options.idProperty || 'id'));
293 }
294 });
295 };
296
297 options = UI.$.extend({}, list.options, options);
298
299 step(list.element, depth);
300
301 return data;
302 },
303
304 reset: function() {
305
306 this.mouse = {
307 offsetX : 0,
308 offsetY : 0,
309 startX : 0,
310 startY : 0,
311 lastX : 0,
312 lastY : 0,
313 nowX : 0,
314 nowY : 0,
315 distX : 0,
316 distY : 0,
317 dirAx : 0,
318 dirX : 0,
319 dirY : 0,
320 lastDirX : 0,
321 lastDirY : 0,
322 distAxX : 0,
323 distAxY : 0
324 };
325 this.moving = false;
326 this.dragEl = null;
327 this.dragRootEl = null;
328 this.dragDepth = 0;
329 this.hasNewRoot = false;
330 this.pointEl = null;
331
332 for (var i=0; i<touchedlists.length; i++) {
333 this.checkEmptyList(touchedlists[i]);
334 }
335
336 touchedlists = [];
337 },
338
339 toggleItem: function(li) {
340 this[li.hasClass(this.options.collapsedClass) ? "expandItem":"collapseItem"](li);
341 },
342
343 expandItem: function(li) {
344 li.removeClass(this.options.collapsedClass);
345 },
346
347 collapseItem: function(li) {
348 var lists = li.children(this.options._listClass);
349 if (lists.length) {
350 li.addClass(this.options.collapsedClass);
351 }
352 },
353
354 expandAll: function() {
355 var list = this;
356 this.find(list.options._listItemClass).each(function() {
357 list.expandItem(UI.$(this));
358 });
359 },
360
361 collapseAll: function() {
362 var list = this;
363 this.find(list.options._listItemClass).each(function() {
364 list.collapseItem(UI.$(this));
365 });
366 },
367
368 setParent: function(li) {
369
370 if (li.children(this.options._listClass).length) {
371 li.addClass("uk-parent");
372 }
373 },
374
375 unsetParent: function(li) {
376 li.removeClass('uk-parent '+this.options.collapsedClass);
377 li.children(this.options._listClass).remove();
378 },
379
380 dragStart: function(e) {
381
382 var mouse = this.mouse,
383 target = UI.$(e.target),
384 dragItem = target.closest(this.options._listItemClass),
385 offset = dragItem.offset();
386
387 this.placeEl = dragItem;
388
389 mouse.offsetX = e.pageX - offset.left;
390 mouse.offsetY = e.pageY - offset.top;
391
392 mouse.startX = mouse.lastX = offset.left;
393 mouse.startY = mouse.lastY = offset.top;
394
395 this.dragRootEl = this.element;
396
397 this.dragEl = UI.$('<ul></ul>').addClass(this.options.listClass + ' ' + this.options.dragClass).append(dragItem.clone());
398 this.dragEl.css('width', dragItem.width());
399 this.placeEl.addClass(this.options.placeholderClass);
400
401 draggingElement = this.dragEl;
402
403 this.tmpDragOnSiblings = [dragItem[0].previousSibling, dragItem[0].nextSibling];
404
405 UI.$body.append(this.dragEl);
406
407 this.dragEl.css({
408 left : offset.left,
409 top : offset.top
410 });
411
412 // total depth of dragging item
413 var i, depth, items = this.dragEl.find(this.options._listItemClass);
414
415 for (i = 0; i < items.length; i++) {
416 depth = UI.$(items[i]).parents(this.options._listClass+','+this.options._listBaseClass).length;
417 if (depth > this.dragDepth) {
418 this.dragDepth = depth;
419 }
420 }
421
422 html.addClass(this.options.movingClass);
423 },
424
425 dragStop: function(e) {
426
427 var el = UI.$(this.placeEl),
428 root = this.placeEl.parents(this.options._listBaseClass+':first');
429
430 this.placeEl.removeClass(this.options.placeholderClass);
431 this.dragEl.remove();
432
433 if (this.element[0] !== root[0]) {
434
435 root.trigger('change.uk.nestable',[root.data('nestable'), el, 'added']);
436 this.element.trigger('change.uk.nestable', [this, el, 'removed']);
437
438 } else {
439 this.element.trigger('change.uk.nestable',[this, el, "moved"]);
440 }
441
442 this.trigger('stop.uk.nestable', [this, el]);
443
444 this.reset();
445
446 html.removeClass(this.options.movingClass);
447 },
448
449 dragMove: function(e) {
450 var list, parent, prev, next, depth,
451 opt = this.options,
452 mouse = this.mouse,
453 maxDepth = this.dragRootEl ? this.dragRootEl.data('nestable').options.maxDepth : opt.maxDepth;
454
455 this.dragEl.css({
456 left : e.pageX - mouse.offsetX,
457 top : e.pageY - mouse.offsetY
458 });
459
460 // mouse position last events
461 mouse.lastX = mouse.nowX;
462 mouse.lastY = mouse.nowY;
463 // mouse position this events
464 mouse.nowX = e.pageX;
465 mouse.nowY = e.pageY;
466 // distance mouse moved between events
467 mouse.distX = mouse.nowX - mouse.lastX;
468 mouse.distY = mouse.nowY - mouse.lastY;
469 // direction mouse was moving
470 mouse.lastDirX = mouse.dirX;
471 mouse.lastDirY = mouse.dirY;
472 // direction mouse is now moving (on both axis)
473 mouse.dirX = mouse.distX === 0 ? 0 : mouse.distX > 0 ? 1 : -1;
474 mouse.dirY = mouse.distY === 0 ? 0 : mouse.distY > 0 ? 1 : -1;
475 // axis mouse is now moving on
476 var newAx = Math.abs(mouse.distX) > Math.abs(mouse.distY) ? 1 : 0;
477
478 // do nothing on first move
479 if (!mouse.moving) {
480 mouse.dirAx = newAx;
481 mouse.moving = true;
482 return;
483 }
484
485 // calc distance moved on this axis (and direction)
486 if (mouse.dirAx !== newAx) {
487 mouse.distAxX = 0;
488 mouse.distAxY = 0;
489 } else {
490 mouse.distAxX += Math.abs(mouse.distX);
491 if (mouse.dirX !== 0 && mouse.dirX !== mouse.lastDirX) {
492 mouse.distAxX = 0;
493 }
494 mouse.distAxY += Math.abs(mouse.distY);
495 if (mouse.dirY !== 0 && mouse.dirY !== mouse.lastDirY) {
496 mouse.distAxY = 0;
497 }
498 }
499 mouse.dirAx = newAx;
500
501 /**
502 * move horizontal
503 */
504 if (mouse.dirAx && mouse.distAxX >= opt.threshold) {
505 // reset move distance on x-axis for new phase
506 mouse.distAxX = 0;
507 prev = this.placeEl.prev('li');
508
509 // increase horizontal level if previous sibling exists, is not collapsed, and does not have a 'no children' class
510 if (mouse.distX > 0 && prev.length && !prev.hasClass(opt.collapsedClass) && !prev.hasClass(opt.noChildrenClass)) {
511
512 // cannot increase level when item above is collapsed
513 list = prev.find(opt._listClass).last();
514
515 // check if depth limit has reached
516 depth = this.placeEl.parents(opt._listClass+','+opt._listBaseClass).length;
517
518 if (depth + this.dragDepth <= maxDepth) {
519
520 // create new sub-level if one doesn't exist
521 if (!list.length) {
522 list = UI.$('<ul/>').addClass(opt.listClass);
523 list.append(this.placeEl);
524 prev.append(list);
525 this.setParent(prev);
526 } else {
527 // else append to next level up
528 list = prev.children(opt._listClass).last();
529 list.append(this.placeEl);
530 }
531 }
532 }
533
534 // decrease horizontal level
535 if (mouse.distX < 0) {
536
537 // we cannot decrease the level if an item precedes the current one
538 next = this.placeEl.next(opt._listItemClass);
539 if (!next.length) {
540
541 // get parent ul of the list item
542 var parentUl = this.placeEl.closest([opt._listBaseClass, opt._listClass].join(','));
543 // try to get the li surrounding the ul
544 var surroundingLi = parentUl.closest(opt._listItemClass);
545
546 // if the ul is inside of a li (meaning it is nested)
547 if (surroundingLi.length) {
548 // we can decrease the horizontal level
549 surroundingLi.after(this.placeEl);
550 // if the previous parent ul is now empty
551 if (!parentUl.children().length) {
552 this.unsetParent(surroundingLi);
553 }
554 }
555 }
556 }
557 }
558
559 var isEmpty = false;
560
561 // find list item under cursor
562 var pointX = e.pageX - (window.pageXOffset || document.scrollLeft || 0),
563 pointY = e.pageY - (window.pageYOffset || document.documentElement.scrollTop);
564 this.pointEl = UI.$(document.elementFromPoint(pointX, pointY));
565
566 if (opt.handleClass && this.pointEl.hasClass(opt.handleClass)) {
567
568 this.pointEl = this.pointEl.closest(opt._listItemClass);
569
570 } else {
571
572 var nestableitem = this.pointEl.closest(opt._listItemClass);
573
574 if (nestableitem.length) {
575 this.pointEl = nestableitem;
576 }
577 }
578
579 if (this.placeEl.find(this.pointEl).length) {
580 return;
581 }
582
583 if (this.pointEl.data('nestable') && !this.pointEl.children().length) {
584 isEmpty = true;
585 this.checkEmptyList(this.pointEl);
586 } else if (!this.pointEl.length || !this.pointEl.hasClass(opt.listItemClass)) {
587 return;
588 }
589
590 // find parent list of item under cursor
591 var pointElRoot = this.element,
592 tmpRoot = this.pointEl.closest(this.options._listBaseClass),
593 isNewRoot = pointElRoot[0] != tmpRoot[0];
594
595 /**
596 * move vertical
597 */
598 if (!mouse.dirAx || isNewRoot || isEmpty) {
599
600 // check if groups match if dragging over new root
601 if (isNewRoot && opt.group !== tmpRoot.data('nestable-group')) {
602 return;
603 } else {
604 touchedlists.push(pointElRoot);
605 }
606
607 // check depth limit
608 depth = this.dragDepth - 1 + this.pointEl.parents(opt._listClass+','+opt._listBaseClass).length;
609
610 if (depth > maxDepth) {
611 return;
612 }
613
614 var before = e.pageY < (this.pointEl.offset().top + this.pointEl.height() / 2);
615
616 parent = this.placeEl.parent();
617
618 if (isEmpty) {
619 this.pointEl.append(this.placeEl);
620 } else if (before) {
621 this.pointEl.before(this.placeEl);
622 } else {
623 this.pointEl.after(this.placeEl);
624 }
625
626 if (!parent.children().length) {
627 if (!parent.data("nestable")) this.unsetParent(parent.parent());
628 }
629
630 this.checkEmptyList(this.dragRootEl);
631 this.checkEmptyList(pointElRoot);
632
633 // parent root list has changed
634 if (isNewRoot) {
635 this.dragRootEl = tmpRoot;
636 this.hasNewRoot = this.element[0] !== this.dragRootEl[0];
637 }
638 }
639 },
640
641 checkEmptyList: function(list) {
642
643 list = list ? UI.$(list) : this.element;
644
645 if (this.options.emptyClass) {
646 list[!list.children().length ? 'addClass':'removeClass'](this.options.emptyClass);
647 }
648 }
649
650 });
651
652 return UI.nestable;
653});