Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
malwaredllc
GitHub Repository: malwaredllc/byob
Path: blob/master/web-gui/buildyourownbotnet/assets/js/jquery.nestable.js
1292 views
1
/*!
2
* Nestable jQuery Plugin - Copyright (c) 2012 David Bushell - http://dbushell.com/
3
* Dual-licensed under the BSD or MIT licenses
4
*/
5
;(function($, window, document, undefined)
6
{
7
var hasTouch = 'ontouchstart' in window;
8
9
/**
10
* Detect CSS pointer-events property
11
* events are normally disabled on the dragging element to avoid conflicts
12
* https://github.com/ausi/Feature-detection-technique-for-pointer-events/blob/master/modernizr-pointerevents.js
13
*/
14
var hasPointerEvents = (function()
15
{
16
var el = document.createElement('div'),
17
docEl = document.documentElement;
18
if (!('pointerEvents' in el.style)) {
19
return false;
20
}
21
el.style.pointerEvents = 'auto';
22
el.style.pointerEvents = 'x';
23
docEl.appendChild(el);
24
var supports = window.getComputedStyle && window.getComputedStyle(el, '').pointerEvents === 'auto';
25
docEl.removeChild(el);
26
return !!supports;
27
})();
28
29
var eStart = hasTouch ? 'touchstart' : 'mousedown',
30
eMove = hasTouch ? 'touchmove' : 'mousemove',
31
eEnd = hasTouch ? 'touchend' : 'mouseup';
32
eCancel = hasTouch ? 'touchcancel' : 'mouseup';
33
34
var defaults = {
35
listNodeName : 'ul',
36
itemNodeName : 'li',
37
rootClass : 'dd',
38
listClass : 'dd-list',
39
itemClass : 'dd-item',
40
dragClass : 'dd-dragel',
41
handleClass : 'dd-handle',
42
collapsedClass : 'dd-collapsed',
43
placeClass : 'dd-placeholder',
44
noDragClass : 'dd-nodrag',
45
emptyClass : 'dd-empty',
46
expandBtnHTML : '<button data-action="expand" type="button">Expand</button>',
47
collapseBtnHTML : '<button data-action="collapse" type="button">Collapse</button>',
48
group : 0,
49
maxDepth : 5,
50
threshold : 20
51
};
52
53
function Plugin(element, options)
54
{
55
this.w = $(window);
56
this.el = $(element);
57
this.options = $.extend({}, defaults, options);
58
this.init();
59
}
60
61
Plugin.prototype = {
62
63
init: function()
64
{
65
var list = this;
66
67
list.reset();
68
69
list.el.data('nestable-group', this.options.group);
70
71
list.placeEl = $('<div class="' + list.options.placeClass + '"/>');
72
73
$.each(this.el.find(list.options.itemNodeName), function(k, el) {
74
list.setParent($(el));
75
});
76
77
list.el.on('click', 'button', function(e) {
78
if (list.dragEl || (!hasTouch && e.button !== 0)) {
79
return;
80
}
81
var target = $(e.currentTarget),
82
action = target.data('action'),
83
item = target.parent(list.options.itemNodeName);
84
if (action === 'collapse') {
85
list.collapseItem(item);
86
}
87
if (action === 'expand') {
88
list.expandItem(item);
89
}
90
});
91
92
var onStartEvent = function(e)
93
{
94
var handle = $(e.target);
95
if (!handle.hasClass(list.options.handleClass)) {
96
if (handle.closest('.' + list.options.noDragClass).length) {
97
return;
98
}
99
handle = handle.closest('.' + list.options.handleClass);
100
}
101
if (!handle.length || list.dragEl || (!hasTouch && e.button !== 0) || (hasTouch && e.touches.length !== 1)) {
102
return;
103
}
104
e.preventDefault();
105
list.dragStart(hasTouch ? e.touches[0] : e);
106
};
107
108
var onMoveEvent = function(e)
109
{
110
if (list.dragEl) {
111
e.preventDefault();
112
list.dragMove(hasTouch ? e.touches[0] : e);
113
}
114
};
115
116
var onEndEvent = function(e)
117
{
118
if (list.dragEl) {
119
e.preventDefault();
120
list.dragStop(hasTouch ? e.touches[0] : e);
121
}
122
};
123
124
if (hasTouch) {
125
list.el[0].addEventListener(eStart, onStartEvent, false);
126
window.addEventListener(eMove, onMoveEvent, false);
127
window.addEventListener(eEnd, onEndEvent, false);
128
window.addEventListener(eCancel, onEndEvent, false);
129
} else {
130
list.el.on(eStart, onStartEvent);
131
list.w.on(eMove, onMoveEvent);
132
list.w.on(eEnd, onEndEvent);
133
}
134
135
},
136
137
serialize: function()
138
{
139
var data,
140
depth = 0,
141
list = this;
142
step = function(level, depth)
143
{
144
var array = [ ],
145
items = level.children(list.options.itemNodeName);
146
items.each(function()
147
{
148
var li = $(this),
149
item = $.extend({}, li.data()),
150
sub = li.children(list.options.listNodeName);
151
if (sub.length) {
152
item.children = step(sub, depth + 1);
153
}
154
array.push(item);
155
});
156
return array;
157
};
158
data = step(list.el.find(list.options.listNodeName).first(), depth);
159
return data;
160
},
161
162
serialise: function()
163
{
164
return this.serialize();
165
},
166
167
reset: function()
168
{
169
this.mouse = {
170
offsetX : 0,
171
offsetY : 0,
172
startX : 0,
173
startY : 0,
174
lastX : 0,
175
lastY : 0,
176
nowX : 0,
177
nowY : 0,
178
distX : 0,
179
distY : 0,
180
dirAx : 0,
181
dirX : 0,
182
dirY : 0,
183
lastDirX : 0,
184
lastDirY : 0,
185
distAxX : 0,
186
distAxY : 0
187
};
188
this.moving = false;
189
this.dragEl = null;
190
this.dragRootEl = null;
191
this.dragDepth = 0;
192
this.hasNewRoot = false;
193
this.pointEl = null;
194
},
195
196
expandItem: function(li)
197
{
198
li.removeClass(this.options.collapsedClass);
199
li.children('[data-action="expand"]').hide();
200
li.children('[data-action="collapse"]').show();
201
li.children(this.options.listNodeName).show();
202
},
203
204
collapseItem: function(li)
205
{
206
var lists = li.children(this.options.listNodeName);
207
if (lists.length) {
208
li.addClass(this.options.collapsedClass);
209
li.children('[data-action="collapse"]').hide();
210
li.children('[data-action="expand"]').show();
211
li.children(this.options.listNodeName).hide();
212
}
213
},
214
215
expandAll: function()
216
{
217
var list = this;
218
list.el.find(list.options.itemNodeName).each(function() {
219
list.expandItem($(this));
220
});
221
},
222
223
collapseAll: function()
224
{
225
var list = this;
226
list.el.find(list.options.itemNodeName).each(function() {
227
list.collapseItem($(this));
228
});
229
},
230
231
setParent: function(li)
232
{
233
if (li.children(this.options.listNodeName).length) {
234
li.prepend($(this.options.expandBtnHTML));
235
li.prepend($(this.options.collapseBtnHTML));
236
}
237
li.children('[data-action="expand"]').hide();
238
},
239
240
unsetParent: function(li)
241
{
242
li.removeClass(this.options.collapsedClass);
243
li.children('[data-action]').remove();
244
li.children(this.options.listNodeName).remove();
245
},
246
247
dragStart: function(e)
248
{
249
var mouse = this.mouse,
250
target = $(e.target),
251
dragItem = target.closest(this.options.itemNodeName);
252
253
this.placeEl.css('height', dragItem.height());
254
255
mouse.offsetX = e.offsetX !== undefined ? e.offsetX : e.pageX - target.offset().left;
256
mouse.offsetY = e.offsetY !== undefined ? e.offsetY : e.pageY - target.offset().top;
257
mouse.startX = mouse.lastX = e.pageX;
258
mouse.startY = mouse.lastY = e.pageY;
259
260
this.dragRootEl = this.el;
261
262
this.dragEl = $(document.createElement(this.options.listNodeName)).addClass(this.options.listClass + ' ' + this.options.dragClass + ($(this.el).hasClass('custom-drag-button') ? ' custom-handler' : ''));
263
this.dragEl.css('width', dragItem.width());
264
265
// fix for zepto.js
266
//dragItem.after(this.placeEl).detach().appendTo(this.dragEl);
267
dragItem.after(this.placeEl);
268
dragItem[0].parentNode.removeChild(dragItem[0]);
269
dragItem.appendTo(this.dragEl);
270
271
$(document.body).append(this.dragEl);
272
this.dragEl.css({
273
'left' : e.pageX - mouse.offsetX,
274
'top' : e.pageY - mouse.offsetY
275
});
276
// total depth of dragging item
277
var i, depth,
278
items = this.dragEl.find(this.options.itemNodeName);
279
for (i = 0; i < items.length; i++) {
280
depth = $(items[i]).parents(this.options.listNodeName).length;
281
if (depth > this.dragDepth) {
282
this.dragDepth = depth;
283
}
284
}
285
286
},
287
288
dragStop: function(e)
289
{
290
// fix for zepto.js
291
//this.placeEl.replaceWith(this.dragEl.children(this.options.itemNodeName + ':first').detach());
292
var el = this.dragEl.children(this.options.itemNodeName).first();
293
el[0].parentNode.removeChild(el[0]);
294
this.placeEl.replaceWith(el);
295
296
this.dragEl.remove();
297
this.el.trigger('change');
298
if (this.hasNewRoot) {
299
this.dragRootEl.trigger('change');
300
}
301
this.reset();
302
},
303
304
dragMove: function(e)
305
{
306
var list, parent, prev, next, depth,
307
opt = this.options,
308
mouse = this.mouse;
309
310
this.dragEl.css({
311
'left' : e.pageX - mouse.offsetX,
312
'top' : e.pageY - mouse.offsetY
313
});
314
315
// mouse position last events
316
mouse.lastX = mouse.nowX;
317
mouse.lastY = mouse.nowY;
318
// mouse position this events
319
mouse.nowX = e.pageX;
320
mouse.nowY = e.pageY;
321
// distance mouse moved between events
322
mouse.distX = mouse.nowX - mouse.lastX;
323
mouse.distY = mouse.nowY - mouse.lastY;
324
// direction mouse was moving
325
mouse.lastDirX = mouse.dirX;
326
mouse.lastDirY = mouse.dirY;
327
// direction mouse is now moving (on both axis)
328
mouse.dirX = mouse.distX === 0 ? 0 : mouse.distX > 0 ? 1 : -1;
329
mouse.dirY = mouse.distY === 0 ? 0 : mouse.distY > 0 ? 1 : -1;
330
// axis mouse is now moving on
331
var newAx = Math.abs(mouse.distX) > Math.abs(mouse.distY) ? 1 : 0;
332
333
// do nothing on first move
334
if (!mouse.moving) {
335
mouse.dirAx = newAx;
336
mouse.moving = true;
337
return;
338
}
339
340
// calc distance moved on this axis (and direction)
341
if (mouse.dirAx !== newAx) {
342
mouse.distAxX = 0;
343
mouse.distAxY = 0;
344
} else {
345
mouse.distAxX += Math.abs(mouse.distX);
346
if (mouse.dirX !== 0 && mouse.dirX !== mouse.lastDirX) {
347
mouse.distAxX = 0;
348
}
349
mouse.distAxY += Math.abs(mouse.distY);
350
if (mouse.dirY !== 0 && mouse.dirY !== mouse.lastDirY) {
351
mouse.distAxY = 0;
352
}
353
}
354
mouse.dirAx = newAx;
355
356
/**
357
* move horizontal
358
*/
359
if (mouse.dirAx && mouse.distAxX >= opt.threshold) {
360
// reset move distance on x-axis for new phase
361
mouse.distAxX = 0;
362
prev = this.placeEl.prev(opt.itemNodeName);
363
// increase horizontal level if previous sibling exists and is not collapsed
364
if (mouse.distX > 0 && prev.length && !prev.hasClass(opt.collapsedClass)) {
365
// cannot increase level when item above is collapsed
366
list = prev.find(opt.listNodeName).last();
367
// check if depth limit has reached
368
depth = this.placeEl.parents(opt.listNodeName).length;
369
if (depth + this.dragDepth <= opt.maxDepth) {
370
// create new sub-level if one doesn't exist
371
if (!list.length) {
372
list = $('<' + opt.listNodeName + '/>').addClass(opt.listClass);
373
list.append(this.placeEl);
374
prev.append(list);
375
this.setParent(prev);
376
} else {
377
// else append to next level up
378
list = prev.children(opt.listNodeName).last();
379
list.append(this.placeEl);
380
}
381
}
382
}
383
// decrease horizontal level
384
if (mouse.distX < 0) {
385
// we can't decrease a level if an item preceeds the current one
386
next = this.placeEl.next(opt.itemNodeName);
387
if (!next.length) {
388
parent = this.placeEl.parent();
389
this.placeEl.closest(opt.itemNodeName).after(this.placeEl);
390
if (!parent.children().length) {
391
this.unsetParent(parent.parent());
392
}
393
}
394
}
395
}
396
397
var isEmpty = false;
398
399
// find list item under cursor
400
if (!hasPointerEvents) {
401
this.dragEl[0].style.visibility = 'hidden';
402
}
403
this.pointEl = $(document.elementFromPoint(e.pageX - document.body.scrollLeft, e.pageY - (window.pageYOffset || document.documentElement.scrollTop)));
404
if (!hasPointerEvents) {
405
this.dragEl[0].style.visibility = 'visible';
406
}
407
if (this.pointEl.hasClass(opt.handleClass)) {
408
this.pointEl = this.pointEl.parent(opt.itemNodeName);
409
}
410
if (this.pointEl.hasClass(opt.emptyClass)) {
411
isEmpty = true;
412
}
413
else if (!this.pointEl.length || !this.pointEl.hasClass(opt.itemClass)) {
414
return;
415
}
416
417
// find parent list of item under cursor
418
var pointElRoot = this.pointEl.closest('.' + opt.rootClass),
419
isNewRoot = this.dragRootEl.data('nestable-id') !== pointElRoot.data('nestable-id');
420
421
/**
422
* move vertical
423
*/
424
if (!mouse.dirAx || isNewRoot || isEmpty) {
425
// check if groups match if dragging over new root
426
if (isNewRoot && opt.group !== pointElRoot.data('nestable-group')) {
427
return;
428
}
429
// check depth limit
430
depth = this.dragDepth - 1 + this.pointEl.parents(opt.listNodeName).length;
431
if (depth > opt.maxDepth) {
432
return;
433
}
434
var before = e.pageY < (this.pointEl.offset().top + this.pointEl.height() / 2);
435
parent = this.placeEl.parent();
436
// if empty create new list to replace empty placeholder
437
if (isEmpty) {
438
list = $(document.createElement(opt.listNodeName)).addClass(opt.listClass);
439
list.append(this.placeEl);
440
this.pointEl.replaceWith(list);
441
}
442
else if (before) {
443
this.pointEl.before(this.placeEl);
444
}
445
else {
446
this.pointEl.after(this.placeEl);
447
}
448
if (!parent.children().length) {
449
this.unsetParent(parent.parent());
450
}
451
if (!this.dragRootEl.find(opt.itemNodeName).length) {
452
this.dragRootEl.append('<div class="' + opt.emptyClass + '"/>');
453
}
454
// parent root list has changed
455
if (isNewRoot) {
456
this.dragRootEl = pointElRoot;
457
this.hasNewRoot = this.el[0] !== this.dragRootEl[0];
458
}
459
}
460
}
461
462
};
463
464
$.fn.nestable = function(params)
465
{
466
var lists = this,
467
retval = this;
468
469
lists.each(function()
470
{
471
var plugin = $(this).data("nestable");
472
473
if (!plugin) {
474
$(this).data("nestable", new Plugin(this, params));
475
$(this).data("nestable-id", new Date().getTime());
476
} else {
477
if (typeof params === 'string' && typeof plugin[params] === 'function') {
478
retval = plugin[params]();
479
}
480
}
481
});
482
483
return retval || lists;
484
};
485
486
})(window.jQuery || window.Zepto, window, document);
487
488