lavkach3

Форк
0
484 строки · 17.2 Кб
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 document;
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 defaults = {
30
            listNodeName    : 'ol',
31
            itemNodeName    : 'li',
32
            rootClass       : 'dd',
33
            listClass       : 'dd-list',
34
            itemClass       : 'dd-item',
35
            dragClass       : 'dd-dragel',
36
            handleClass     : 'dd-handle',
37
            collapsedClass  : 'dd-collapsed',
38
            placeClass      : 'dd-placeholder',
39
            noDragClass     : 'dd-nodrag',
40
            emptyClass      : 'dd-empty',
41
            expandBtnHTML   : '<button data-action="expand" type="button">Expand</button>',
42
            collapseBtnHTML : '<button data-action="collapse" type="button">Collapse</button>',
43
            group           : 0,
44
            maxDepth        : 5,
45
            threshold       : 20
46
        };
47

48
    function Plugin(element, options)
49
    {
50
        this.w  = $(document);
51
        this.el = $(element);
52
        this.options = $.extend({}, defaults, options);
53
        this.init();
54
    }
55

56
    Plugin.prototype = {
57

58
        init: function()
59
        {
60
            var list = this;
61

62
            list.reset();
63

64
            list.el.data('nestable-group', this.options.group);
65

66
            list.placeEl = $('<div class="' + list.options.placeClass + '"/>');
67

68
            $.each(this.el.find(list.options.itemNodeName), function(k, el) {
69
                list.setParent($(el));
70
            });
71

72
            list.el.on('click', 'button', function(e) {
73
                if (list.dragEl) {
74
                    return;
75
                }
76
                var target = $(e.currentTarget),
77
                    action = target.data('action'),
78
                    item   = target.parent(list.options.itemNodeName);
79
                if (action === 'collapse') {
80
                    list.collapseItem(item);
81
                }
82
                if (action === 'expand') {
83
                    list.expandItem(item);
84
                }
85
            });
86

87
            var onStartEvent = function(e)
88
            {
89
                var handle = $(e.target);
90
                if (!handle.hasClass(list.options.handleClass)) {
91
                    if (handle.closest('.' + list.options.noDragClass).length) {
92
                        return;
93
                    }
94
                    handle = handle.closest('.' + list.options.handleClass);
95
                }
96

97
                if (!handle.length || list.dragEl) {
98
                    return;
99
                }
100

101
                list.isTouch = /^touch/.test(e.type);
102
                if (list.isTouch && e.touches.length !== 1) {
103
                    return;
104
                }
105

106
                e.preventDefault();
107
                list.dragStart(e.touches ? e.touches[0] : e);
108
            };
109

110
            var onMoveEvent = function(e)
111
            {
112
                if (list.dragEl) {
113
                    e.preventDefault();
114
                    list.dragMove(e.touches ? e.touches[0] : e);
115
                }
116
            };
117

118
            var onEndEvent = function(e)
119
            {
120
                if (list.dragEl) {
121
                    e.preventDefault();
122
                    list.dragStop(e.touches ? e.touches[0] : e);
123
                }
124
            };
125

126
            if (hasTouch) {
127
                list.el[0].addEventListener('touchstart', onStartEvent, false);
128
                window.addEventListener('touchmove', onMoveEvent, false);
129
                window.addEventListener('touchend', onEndEvent, false);
130
                window.addEventListener('touchcancel', onEndEvent, false);
131
            }
132

133
            list.el.on('mousedown', onStartEvent);
134
            list.w.on('mousemove', onMoveEvent);
135
            list.w.on('mouseup', onEndEvent);
136

137
        },
138

139
        serialize: function()
140
        {
141
            var data,
142
                depth = 0,
143
                list  = this;
144
                step  = function(level, depth)
145
                {
146
                    var array = [ ],
147
                        items = level.children(list.options.itemNodeName);
148
                    items.each(function()
149
                    {
150
                        var li   = $(this),
151
                            item = $.extend({}, li.data()),
152
                            sub  = li.children(list.options.listNodeName);
153
                        if (sub.length) {
154
                            item.children = step(sub, depth + 1);
155
                        }
156
                        array.push(item);
157
                    });
158
                    return array;
159
                };
160
            data = step(list.el.find(list.options.listNodeName).first(), depth);
161
            return data;
162
        },
163

164
        serialise: function()
165
        {
166
            return this.serialize();
167
        },
168

169
        reset: function()
170
        {
171
            this.mouse = {
172
                offsetX   : 0,
173
                offsetY   : 0,
174
                startX    : 0,
175
                startY    : 0,
176
                lastX     : 0,
177
                lastY     : 0,
178
                nowX      : 0,
179
                nowY      : 0,
180
                distX     : 0,
181
                distY     : 0,
182
                dirAx     : 0,
183
                dirX      : 0,
184
                dirY      : 0,
185
                lastDirX  : 0,
186
                lastDirY  : 0,
187
                distAxX   : 0,
188
                distAxY   : 0
189
            };
190
            this.isTouch    = false;
191
            this.moving     = false;
192
            this.dragEl     = null;
193
            this.dragRootEl = null;
194
            this.dragDepth  = 0;
195
            this.hasNewRoot = false;
196
            this.pointEl    = null;
197
        },
198

199
        expandItem: function(li)
200
        {
201
            li.removeClass(this.options.collapsedClass);
202
            li.children('[data-action="expand"]').hide();
203
            li.children('[data-action="collapse"]').show();
204
            li.children(this.options.listNodeName).show();
205
        },
206

207
        collapseItem: function(li)
208
        {
209
            var lists = li.children(this.options.listNodeName);
210
            if (lists.length) {
211
                li.addClass(this.options.collapsedClass);
212
                li.children('[data-action="collapse"]').hide();
213
                li.children('[data-action="expand"]').show();
214
                li.children(this.options.listNodeName).hide();
215
            }
216
        },
217

218
        expandAll: function()
219
        {
220
            var list = this;
221
            list.el.find(list.options.itemNodeName).each(function() {
222
                list.expandItem($(this));
223
            });
224
        },
225

226
        collapseAll: function()
227
        {
228
            var list = this;
229
            list.el.find(list.options.itemNodeName).each(function() {
230
                list.collapseItem($(this));
231
            });
232
        },
233

234
        setParent: function(li)
235
        {
236
            if (li.children(this.options.listNodeName).length) {
237
                li.prepend($(this.options.expandBtnHTML));
238
                li.prepend($(this.options.collapseBtnHTML));
239
            }
240
            li.children('[data-action="expand"]').hide();
241
        },
242

243
        unsetParent: function(li)
244
        {
245
            li.removeClass(this.options.collapsedClass);
246
            li.children('[data-action]').remove();
247
            li.children(this.options.listNodeName).remove();
248
        },
249

250
        dragStart: function(e)
251
        {
252
            var mouse    = this.mouse,
253
                target   = $(e.target),
254
                dragItem = target.closest(this.options.itemNodeName);
255

256
            this.placeEl.css('height', dragItem.height());
257

258
            mouse.offsetX = e.offsetX !== undefined ? e.offsetX : e.pageX - target.offset().left;
259
            mouse.offsetY = e.offsetY !== undefined ? e.offsetY : e.pageY - target.offset().top;
260
            mouse.startX = mouse.lastX = e.pageX;
261
            mouse.startY = mouse.lastY = e.pageY;
262

263
            this.dragRootEl = this.el;
264

265
            this.dragEl = $(document.createElement(this.options.listNodeName)).addClass(this.options.listClass + ' ' + this.options.dragClass);
266
            this.dragEl.css('width', dragItem.width());
267

268
            dragItem.after(this.placeEl);
269
            dragItem[0].parentNode.removeChild(dragItem[0]);
270
            dragItem.appendTo(this.dragEl);
271

272
            $(document.body).append(this.dragEl);
273
            this.dragEl.css({
274
                'left' : e.pageX - mouse.offsetX,
275
                'top'  : e.pageY - mouse.offsetY
276
            });
277
            // total depth of dragging item
278
            var i, depth,
279
                items = this.dragEl.find(this.options.itemNodeName);
280
            for (i = 0; i < items.length; i++) {
281
                depth = $(items[i]).parents(this.options.listNodeName).length;
282
                if (depth > this.dragDepth) {
283
                    this.dragDepth = depth;
284
                }
285
            }
286
        },
287

288
        dragStop: function(e)
289
        {
290
            var el = this.dragEl.children(this.options.itemNodeName).first();
291
            el[0].parentNode.removeChild(el[0]);
292
            this.placeEl.replaceWith(el);
293

294
            this.dragEl.remove();
295
            this.el.trigger('change');
296
            if (this.hasNewRoot) {
297
                this.dragRootEl.trigger('change');
298
            }
299
            this.reset();
300
        },
301

302
        dragMove: function(e)
303
        {
304
            var list, parent, prev, next, depth,
305
                opt   = this.options,
306
                mouse = this.mouse;
307

308
            this.dragEl.css({
309
                'left' : e.pageX - mouse.offsetX,
310
                'top'  : e.pageY - mouse.offsetY
311
            });
312

313
            // mouse position last events
314
            mouse.lastX = mouse.nowX;
315
            mouse.lastY = mouse.nowY;
316
            // mouse position this events
317
            mouse.nowX  = e.pageX;
318
            mouse.nowY  = e.pageY;
319
            // distance mouse moved between events
320
            mouse.distX = mouse.nowX - mouse.lastX;
321
            mouse.distY = mouse.nowY - mouse.lastY;
322
            // direction mouse was moving
323
            mouse.lastDirX = mouse.dirX;
324
            mouse.lastDirY = mouse.dirY;
325
            // direction mouse is now moving (on both axis)
326
            mouse.dirX = mouse.distX === 0 ? 0 : mouse.distX > 0 ? 1 : -1;
327
            mouse.dirY = mouse.distY === 0 ? 0 : mouse.distY > 0 ? 1 : -1;
328
            // axis mouse is now moving on
329
            var newAx   = Math.abs(mouse.distX) > Math.abs(mouse.distY) ? 1 : 0;
330

331
            // do nothing on first move
332
            if (!mouse.moving) {
333
                mouse.dirAx  = newAx;
334
                mouse.moving = true;
335
                return;
336
            }
337

338
            // calc distance moved on this axis (and direction)
339
            if (mouse.dirAx !== newAx) {
340
                mouse.distAxX = 0;
341
                mouse.distAxY = 0;
342
            } else {
343
                mouse.distAxX += Math.abs(mouse.distX);
344
                if (mouse.dirX !== 0 && mouse.dirX !== mouse.lastDirX) {
345
                    mouse.distAxX = 0;
346
                }
347
                mouse.distAxY += Math.abs(mouse.distY);
348
                if (mouse.dirY !== 0 && mouse.dirY !== mouse.lastDirY) {
349
                    mouse.distAxY = 0;
350
                }
351
            }
352
            mouse.dirAx = newAx;
353

354
            /**
355
             * move horizontal
356
             */
357
            if (mouse.dirAx && mouse.distAxX >= opt.threshold) {
358
                // reset move distance on x-axis for new phase
359
                mouse.distAxX = 0;
360
                prev = this.placeEl.prev(opt.itemNodeName);
361
                // increase horizontal level if previous sibling exists and is not collapsed
362
                if (mouse.distX > 0 && prev.length && !prev.hasClass(opt.collapsedClass)) {
363
                    // cannot increase level when item above is collapsed
364
                    list = prev.find(opt.listNodeName).last();
365
                    // check if depth limit has reached
366
                    depth = this.placeEl.parents(opt.listNodeName).length;
367
                    if (depth + this.dragDepth <= opt.maxDepth) {
368
                        // create new sub-level if one doesn't exist
369
                        if (!list.length) {
370
                            list = $('<' + opt.listNodeName + '/>').addClass(opt.listClass);
371
                            list.append(this.placeEl);
372
                            prev.append(list);
373
                            this.setParent(prev);
374
                        } else {
375
                            // else append to next level up
376
                            list = prev.children(opt.listNodeName).last();
377
                            list.append(this.placeEl);
378
                        }
379
                    }
380
                }
381
                // decrease horizontal level
382
                if (mouse.distX < 0) {
383
                    // we can't decrease a level if an item preceeds the current one
384
                    next = this.placeEl.next(opt.itemNodeName);
385
                    if (!next.length) {
386
                        parent = this.placeEl.parent();
387
                        this.placeEl.closest(opt.itemNodeName).after(this.placeEl);
388
                        if (!parent.children().length) {
389
                            this.unsetParent(parent.parent());
390
                        }
391
                    }
392
                }
393
            }
394

395
            var isEmpty = false;
396

397
            // find list item under cursor
398
            if (!hasPointerEvents) {
399
                this.dragEl[0].style.visibility = 'hidden';
400
            }
401
            this.pointEl = $(document.elementFromPoint(e.pageX - document.body.scrollLeft, e.pageY - (window.pageYOffset || document.documentElement.scrollTop)));
402
            if (!hasPointerEvents) {
403
                this.dragEl[0].style.visibility = 'visible';
404
            }
405
            if (this.pointEl.hasClass(opt.handleClass)) {
406
                this.pointEl = this.pointEl.parent(opt.itemNodeName);
407
            }
408
            if (this.pointEl.hasClass(opt.emptyClass)) {
409
                isEmpty = true;
410
            }
411
            else if (!this.pointEl.length || !this.pointEl.hasClass(opt.itemClass)) {
412
                return;
413
            }
414

415
            // find parent list of item under cursor
416
            var pointElRoot = this.pointEl.closest('.' + opt.rootClass),
417
                isNewRoot   = this.dragRootEl.data('nestable-id') !== pointElRoot.data('nestable-id');
418

419
            /**
420
             * move vertical
421
             */
422
            if (!mouse.dirAx || isNewRoot || isEmpty) {
423
                // check if groups match if dragging over new root
424
                if (isNewRoot && opt.group !== pointElRoot.data('nestable-group')) {
425
                    return;
426
                }
427
                // check depth limit
428
                depth = this.dragDepth - 1 + this.pointEl.parents(opt.listNodeName).length;
429
                if (depth > opt.maxDepth) {
430
                    return;
431
                }
432
                var before = e.pageY < (this.pointEl.offset().top + this.pointEl.height() / 2);
433
                    parent = this.placeEl.parent();
434
                // if empty create new list to replace empty placeholder
435
                if (isEmpty) {
436
                    list = $(document.createElement(opt.listNodeName)).addClass(opt.listClass);
437
                    list.append(this.placeEl);
438
                    this.pointEl.replaceWith(list);
439
                }
440
                else if (before) {
441
                    this.pointEl.before(this.placeEl);
442
                }
443
                else {
444
                    this.pointEl.after(this.placeEl);
445
                }
446
                if (!parent.children().length) {
447
                    this.unsetParent(parent.parent());
448
                }
449
                if (!this.dragRootEl.find(opt.itemNodeName).length) {
450
                    this.dragRootEl.append('<div class="' + opt.emptyClass + '"/>');
451
                }
452
                // parent root list has changed
453
                if (isNewRoot) {
454
                    this.dragRootEl = pointElRoot;
455
                    this.hasNewRoot = this.el[0] !== this.dragRootEl[0];
456
                }
457
            }
458
        }
459

460
    };
461

462
    $.fn.nestable = function(params)
463
    {
464
        var lists  = this,
465
            retval = this;
466

467
        lists.each(function()
468
        {
469
            var plugin = $(this).data("nestable");
470

471
            if (!plugin) {
472
                $(this).data("nestable", new Plugin(this, params));
473
                $(this).data("nestable-id", new Date().getTime());
474
            } else {
475
                if (typeof params === 'string' && typeof plugin[params] === 'function') {
476
                    retval = plugin[params]();
477
                }
478
            }
479
        });
480

481
        return retval || lists;
482
    };
483

484
})(window.jQuery || window.Zepto, window, document);
485

Использование cookies

Мы используем файлы cookie в соответствии с Политикой конфиденциальности и Политикой использования cookies.

Нажимая кнопку «Принимаю», Вы даете АО «СберТех» согласие на обработку Ваших персональных данных в целях совершенствования нашего веб-сайта и Сервиса GitVerse, а также повышения удобства их использования.

Запретить использование cookies Вы можете самостоятельно в настройках Вашего браузера.