rails_admin_nested_set 0.4.5 → 0.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: e6301d16ceec2c3175cfdc8c3d0e1871a22e881c
4
- data.tar.gz: be43d5974508cfdf63fba8c683ee40cb2b16f1c7
3
+ metadata.gz: 8bb8fc551013d6a130bfda3482db3bbd00f266eb
4
+ data.tar.gz: cbca74c2218af4e609f9e9d7914f4b50aa11ca94
5
5
  SHA512:
6
- metadata.gz: 3e27491f71b3423f6befad008aeea489b896a3bb9c24bb2fb26402a53925f7b50ed3b6716846c69e126bd8b0ffe0cca0b7d349bd45a8fab5a826e6eade83055f
7
- data.tar.gz: 74514fccd38cd9f3f10d01f5e885cbb823894488d21bf99d998fbc701de938a26df95017e4b6d8fcdea443dda4eb512b3374b9340cccf38e4a0a6b99d3e5816a
6
+ metadata.gz: b8d5280af6ac8f30246800cdf5064bec0331abce8a563ebb0422e1bd9b24e81b1252e6fc11f94a8358017527446f19f57ddd8c8698b447414216fd9d0a46b918
7
+ data.tar.gz: 94144f4dcad97230c4c490eb17487fa956c5b333d7530fcc1b8fe1bbdfb9c6658d012ccbaa5b8491c54bd066173d76a522cf8f153ece0ac7ccd5f458c96cf6f5
@@ -1,639 +1,910 @@
1
1
  /*
2
2
  * jQuery UI Nested Sortable
3
- * v 2.0 / 29 oct 2012
4
- * http://mjsarfatti.com/sandbox/nestedSortable
3
+ * v 2.0b1 / 2016-02-04
4
+ * https://github.com/ilikenwf/nestedSortable
5
5
  *
6
6
  * Depends on:
7
- * jquery.ui.sortable.js 1.10+
7
+ * jquery.ui.sortable.js 1.10+
8
8
  *
9
- * Copyright (c) 2010-2013 Manuele J Sarfatti
9
+ * Copyright (c) 2010-2016 Manuele J Sarfatti and contributors
10
10
  * Licensed under the MIT License
11
11
  * http://www.opensource.org/licenses/mit-license.php
12
12
  */
13
-
14
- (function($) {
15
-
16
- function isOverAxis( x, reference, size ) {
17
- return ( x > reference ) && ( x < ( reference + size ) );
18
- }
19
-
20
- $.widget("mjs.nestedSortable", $.extend({}, $.ui.sortable.prototype, {
21
-
22
- options: {
23
- doNotClear: false,
24
- expandOnHover: 700,
25
- isAllowed: function(placeholder, placeholderParent, originalItem) { return true; },
26
- isTree: false,
27
- listType: 'ol',
28
- maxLevels: 0,
29
- protectRoot: false,
30
- rootID: null,
31
- rtl: false,
32
- startCollapsed: false,
33
- tabSize: 20,
34
- scrollX: true, //if true, the page scrolls when a list item is dragged past the width of the container
35
- scrollY: true, //if true, the page scrolls when a list item is dragged past the height of the container
36
- branchClass: 'mjs-nestedSortable-branch',
37
- collapsedClass: 'mjs-nestedSortable-collapsed',
38
- disableNestingClass: 'mjs-nestedSortable-no-nesting',
39
- errorClass: 'mjs-nestedSortable-error',
40
- expandedClass: 'mjs-nestedSortable-expanded',
41
- hoveringClass: 'mjs-nestedSortable-hovering',
42
- leafClass: 'mjs-nestedSortable-leaf',
43
- disabledClass: 'mjs-nestedSortable-disabled'
44
- },
45
-
46
- _create: function() {
47
- this.element.data('ui-sortable', this.element.data('mjs-nestedSortable'));
48
-
49
- // mjs - prevent browser from freezing if the HTML is not correct
50
- if (!this.element.is(this.options.listType))
51
- throw new Error('nestedSortable: Please check that the listType option is set to your actual list type');
52
-
53
- // mjs - force 'intersect' tolerance method if we have a tree with expanding/collapsing functionality
54
- if (this.options.isTree && this.options.expandOnHover) {
55
- this.options.tolerance = 'intersect';
56
- }
57
-
58
- $.ui.sortable.prototype._create.apply(this, arguments);
59
-
60
- // mjs - prepare the tree by applying the right classes (the CSS is responsible for actual hide/show functionality)
61
- if (this.options.isTree) {
62
- var self = this;
63
- $(this.items).each(function() {
64
- var $li = this.item;
65
- if ($li.children(self.options.listType).length) {
66
- $li.addClass(self.options.branchClass);
67
- // expand/collapse class only if they have children
68
- if (self.options.startCollapsed) $li.addClass(self.options.collapsedClass);
69
- else $li.addClass(self.options.expandedClass);
70
- } else {
71
- $li.addClass(self.options.leafClass);
72
- }
73
- })
74
- }
75
- },
76
-
77
- _destroy: function() {
78
- this.element
79
- .removeData("mjs-nestedSortable")
80
- .removeData("ui-sortable");
81
- return $.ui.sortable.prototype._destroy.apply(this, arguments);
82
- },
83
-
84
- _mouseDrag: function(event) {
85
- var i, item, itemElement, intersection,
86
- o = this.options,
87
- scrolled = false;
88
-
89
- //Compute the helpers position
90
- this.position = this._generatePosition(event);
91
- this.positionAbs = this._convertPositionTo("absolute");
92
-
93
- if (!this.lastPositionAbs) {
94
- this.lastPositionAbs = this.positionAbs;
95
- }
96
-
97
- //Do scrolling
98
- if(this.options.scroll) {
99
- if(this.scrollParent[0] != document && this.scrollParent[0].tagName != 'HTML') {
100
-
101
- if( o.scrollY ) {
102
- if((this.overflowOffset.top + this.scrollParent[0].offsetHeight) - event.pageY < o.scrollSensitivity) {
103
- this.scrollParent[0].scrollTop = scrolled = this.scrollParent[0].scrollTop + o.scrollSpeed;
104
- } else if(event.pageY - this.overflowOffset.top < o.scrollSensitivity) {
105
- this.scrollParent[0].scrollTop = scrolled = this.scrollParent[0].scrollTop - o.scrollSpeed;
106
- }
107
- }
108
- if( o.scrollX ) {
109
- if((this.overflowOffset.left + this.scrollParent[0].offsetWidth) - event.pageX < o.scrollSensitivity) {
110
- this.scrollParent[0].scrollLeft = scrolled = this.scrollParent[0].scrollLeft + o.scrollSpeed;
111
- } else if(event.pageX - this.overflowOffset.left < o.scrollSensitivity) {
112
- this.scrollParent[0].scrollLeft = scrolled = this.scrollParent[0].scrollLeft - o.scrollSpeed;
113
- }
114
- }
115
- } else {
116
- if( o.scrollY ) {
117
- if(event.pageY - $(document).scrollTop() < o.scrollSensitivity) {
118
- scrolled = $(document).scrollTop($(document).scrollTop() - o.scrollSpeed);
119
- } else if($(window).height() - (event.pageY - $(document).scrollTop()) < o.scrollSensitivity) {
120
- scrolled = $(document).scrollTop($(document).scrollTop() + o.scrollSpeed);
121
- }
122
- }
123
- if( o.scrollX ) {
124
- if(event.pageX - $(document).scrollLeft() < o.scrollSensitivity) {
125
- scrolled = $(document).scrollLeft($(document).scrollLeft() - o.scrollSpeed);
126
- } else if($(window).width() - (event.pageX - $(document).scrollLeft()) < o.scrollSensitivity) {
127
- scrolled = $(document).scrollLeft($(document).scrollLeft() + o.scrollSpeed);
128
- }
129
- }
130
- }
131
-
132
- if(scrolled !== false && $.ui.ddmanager && !o.dropBehaviour)
133
- $.ui.ddmanager.prepareOffsets(this, event);
134
- }
135
-
136
- //Regenerate the absolute position used for position checks
137
- this.positionAbs = this._convertPositionTo("absolute");
138
-
139
- // mjs - find the top offset before rearrangement,
140
- var previousTopOffset = this.placeholder.offset().top;
141
-
142
- //Set the helper position
143
- if(!this.options.axis || this.options.axis !== "y") {
144
- this.helper[0].style.left = this.position.left+"px";
145
- }
146
- if(!this.options.axis || this.options.axis !== "x") {
147
- this.helper[0].style.top = this.position.top+"px";
148
- }
149
-
150
- // mjs - check and reset hovering state at each cycle
151
- this.hovering = this.hovering ? this.hovering : null;
152
- this.mouseentered = this.mouseentered ? this.mouseentered : false;
153
-
154
- // mjs - let's start caching some variables
155
- var parentItem = (this.placeholder[0].parentNode.parentNode &&
156
- $(this.placeholder[0].parentNode.parentNode).closest('.ui-sortable').length)
157
- ? $(this.placeholder[0].parentNode.parentNode)
158
- : null,
159
- level = this._getLevel(this.placeholder),
160
- childLevels = this._getChildLevels(this.helper);
161
-
162
- var newList = document.createElement(o.listType);
163
-
164
- //Rearrange
165
- for (i = this.items.length - 1; i >= 0; i--) {
166
-
167
- //Cache variables and intersection, continue if no intersection
168
- item = this.items[i];
169
- itemElement = item.item[0];
170
- intersection = this._intersectsWithPointer(item);
171
- if (!intersection) {
172
- continue;
173
- }
174
-
175
- // Only put the placeholder inside the current Container, skip all
176
- // items form other containers. This works because when moving
177
- // an item from one container to another the
178
- // currentContainer is switched before the placeholder is moved.
179
- //
180
- // Without this moving items in "sub-sortables" can cause the placeholder to jitter
181
- // beetween the outer and inner container.
182
- if (item.instance !== this.currentContainer) {
183
- continue;
184
- }
185
-
186
- // No action if intersected item is disabled
187
- // and the element above or below in the direction we're going is also disabled
188
- if (itemElement.className.indexOf(o.disabledClass) !== -1) {
189
- // Note: intersection hardcoded direction values from jquery.ui.sortable.js:_intersectsWithPointer
190
- if (intersection === 2) {
191
- // Going down
192
- var itemAfter = this.items[i + 1];
193
- if (itemAfter && itemAfter.item[0].className.indexOf(o.disabledClass) !== -1){
194
- continue;
195
- }
196
-
197
- }
198
- else if (intersection === 1) {
199
- // Going up
200
- var itemBefore = this.items[i - 1];
201
- if (itemBefore && itemBefore.item[0].className.indexOf(o.disabledClass) !== -1){
202
- continue;
203
- }
204
- }
205
- }
206
-
207
- // cannot intersect with itself
208
- // no useless actions that have been done before
209
- // no action if the item moved is the parent of the item checked
210
- if (itemElement !== this.currentItem[0] &&
211
- this.placeholder[intersection === 1 ? "next" : "prev"]()[0] !== itemElement &&
212
- !$.contains(this.placeholder[0], itemElement) &&
213
- (this.options.type === "semi-dynamic" ? !$.contains(this.element[0], itemElement) : true)
214
- ) {
215
-
216
- // mjs - we are intersecting an element: trigger the mouseenter event and store this state
217
- if (!this.mouseentered) {
218
- $(itemElement).mouseenter();
219
- this.mouseentered = true;
220
- }
221
-
222
- // mjs - if the element has children and they are hidden, show them after a delay (CSS responsible)
223
- if (o.isTree && $(itemElement).hasClass(o.collapsedClass) && o.expandOnHover) {
224
- if (!this.hovering) {
225
- $(itemElement).addClass(o.hoveringClass);
226
- var self = this;
227
- this.hovering = window.setTimeout(function() {
228
- $(itemElement).removeClass(o.collapsedClass).addClass(o.expandedClass);
229
- self.refreshPositions();
230
- self._trigger("expand", event, self._uiHash());
231
- }, o.expandOnHover);
232
- }
233
- }
234
-
235
- this.direction = intersection == 1 ? "down" : "up";
236
-
237
- // mjs - rearrange the elements and reset timeouts and hovering state
238
- if (this.options.tolerance == "pointer" || this._intersectsWithSides(item)) {
239
- $(itemElement).mouseleave();
240
- this.mouseentered = false;
241
- $(itemElement).removeClass(o.hoveringClass);
242
- this.hovering && window.clearTimeout(this.hovering);
243
- this.hovering = null;
244
-
245
- // mjs - do not switch container if it's a root item and 'protectRoot' is true
246
- // or if it's not a root item but we are trying to make it root
247
- if (o.protectRoot
248
- && ! (this.currentItem[0].parentNode == this.element[0] // it's a root item
249
- && itemElement.parentNode != this.element[0]) // it's intersecting a non-root item
250
- ) {
251
- if (this.currentItem[0].parentNode != this.element[0]
252
- && itemElement.parentNode == this.element[0]
253
- ) {
254
-
255
- if ( ! $(itemElement).children(o.listType).length) {
256
- itemElement.appendChild(newList);
257
- o.isTree && $(itemElement).removeClass(o.leafClass).addClass(o.branchClass + ' ' + o.expandedClass);
258
- }
259
-
260
- var a = this.direction === "down" ? $(itemElement).prev().children(o.listType) : $(itemElement).children(o.listType);
261
- if (a[0] !== undefined) {
262
- this._rearrange(event, null, a);
263
- }
264
-
265
- } else {
266
- this._rearrange(event, item);
267
- }
268
- } else if ( ! o.protectRoot) {
269
- this._rearrange(event, item);
270
- }
271
- } else {
272
- break;
273
- }
274
-
275
- // Clear emtpy ul's/ol's
276
- this._clearEmpty(itemElement);
277
-
278
- this._trigger("change", event, this._uiHash());
279
- break;
280
- }
281
- }
282
-
283
- // mjs - to find the previous sibling in the list, keep backtracking until we hit a valid list item.
284
- var previousItem = this.placeholder[0].previousSibling ? $(this.placeholder[0].previousSibling) : null;
285
- if (previousItem != null) {
286
- while (previousItem[0].nodeName.toLowerCase() != 'li' || previousItem[0].className.indexOf(o.disabledClass) !== -1 || previousItem[0] == this.currentItem[0] || previousItem[0] == this.helper[0]) {
287
- if (previousItem[0].previousSibling) {
288
- previousItem = $(previousItem[0].previousSibling);
289
- } else {
290
- previousItem = null;
291
- break;
292
- }
293
- }
294
- }
295
-
296
- // mjs - to find the next sibling in the list, keep stepping forward until we hit a valid list item.
297
- var nextItem = this.placeholder[0].nextSibling ? $(this.placeholder[0].nextSibling) : null;
298
- if (nextItem != null) {
299
- while (nextItem[0].nodeName.toLowerCase() != 'li' || nextItem[0].className.indexOf(o.disabledClass) !== -1 || nextItem[0] == this.currentItem[0] || nextItem[0] == this.helper[0]) {
300
- if (nextItem[0].nextSibling) {
301
- nextItem = $(nextItem[0].nextSibling);
302
- } else {
303
- nextItem = null;
304
- break;
305
- }
306
- }
307
- }
308
-
309
- this.beyondMaxLevels = 0;
310
-
311
- // mjs - if the item is moved to the left, send it one level up but only if it's at the bottom of the list
312
- if (parentItem != null
313
- && nextItem == null
314
- && ! (o.protectRoot && parentItem[0].parentNode == this.element[0])
315
- &&
316
- (o.rtl && (this.positionAbs.left + this.helper.outerWidth() > parentItem.offset().left + parentItem.outerWidth())
317
- || ! o.rtl && (this.positionAbs.left < parentItem.offset().left))
318
- ) {
319
-
320
- parentItem.after(this.placeholder[0]);
321
- if (o.isTree && parentItem.children(o.listItem).children('li:visible:not(.ui-sortable-helper)').length < 1) {
322
- parentItem.removeClass(this.options.branchClass + ' ' + this.options.expandedClass)
323
- .addClass(this.options.leafClass);
324
- }
325
- this._clearEmpty(parentItem[0]);
326
- this._trigger("change", event, this._uiHash());
327
- }
328
- // mjs - if the item is below a sibling and is moved to the right, make it a child of that sibling
329
- else if (previousItem != null
330
- && ! previousItem.hasClass(o.disableNestingClass)
331
- &&
332
- (previousItem.children(o.listType).length && previousItem.children(o.listType).is(':visible')
333
- || ! previousItem.children(o.listType).length)
334
- && ! (o.protectRoot && this.currentItem[0].parentNode == this.element[0])
335
- &&
336
- (o.rtl && (this.positionAbs.left + this.helper.outerWidth() < previousItem.offset().left + previousItem.outerWidth() - o.tabSize)
337
- || ! o.rtl && (this.positionAbs.left > previousItem.offset().left + o.tabSize))
338
- ) {
339
-
340
- this._isAllowed(previousItem, level, level+childLevels+1);
341
-
342
- if (!previousItem.children(o.listType).length) {
343
- previousItem[0].appendChild(newList);
344
- o.isTree && previousItem.removeClass(o.leafClass).addClass(o.branchClass + ' ' + o.expandedClass);
345
- }
346
-
347
- // mjs - if this item is being moved from the top, add it to the top of the list.
348
- if (previousTopOffset && (previousTopOffset <= previousItem.offset().top)) {
349
- previousItem.children(o.listType).prepend(this.placeholder);
350
- }
351
- // mjs - otherwise, add it to the bottom of the list.
352
- else {
353
- previousItem.children(o.listType)[0].appendChild(this.placeholder[0]);
354
- }
355
-
356
- this._trigger("change", event, this._uiHash());
357
- }
358
- else {
359
- this._isAllowed(parentItem, level, level+childLevels);
360
- }
361
-
362
- //Post events to containers
363
- this._contactContainers(event);
364
-
365
- //Interconnect with droppables
366
- if($.ui.ddmanager) {
367
- $.ui.ddmanager.drag(this, event);
368
- }
369
-
370
- //Call callbacks
371
- this._trigger('sort', event, this._uiHash());
372
-
373
- this.lastPositionAbs = this.positionAbs;
374
- return false;
375
-
376
- },
377
-
378
- _mouseStop: function(event, noPropagation) {
379
-
380
- // mjs - if the item is in a position not allowed, send it back
381
- if (this.beyondMaxLevels) {
382
-
383
- this.placeholder.removeClass(this.options.errorClass);
384
-
385
- if (this.domPosition.prev) {
386
- $(this.domPosition.prev).after(this.placeholder);
387
- } else {
388
- $(this.domPosition.parent).prepend(this.placeholder);
389
- }
390
-
391
- this._trigger("revert", event, this._uiHash());
392
-
393
- }
394
-
395
-
396
- // mjs - clear the hovering timeout, just to be sure
397
- $('.'+this.options.hoveringClass).mouseleave().removeClass(this.options.hoveringClass);
398
- this.mouseentered = false;
399
- this.hovering && window.clearTimeout(this.hovering);
400
- this.hovering = null;
401
-
402
- $.ui.sortable.prototype._mouseStop.apply(this, arguments);
403
-
404
- },
405
-
406
- // mjs - this function is slightly modified to make it easier to hover over a collapsed element and have it expand
407
- _intersectsWithSides: function(item) {
408
-
409
- var half = this.options.isTree ? .8 : .5;
410
-
411
- var isOverBottomHalf = isOverAxis(this.positionAbs.top + this.offset.click.top, item.top + (item.height*half), item.height),
412
- isOverTopHalf = isOverAxis(this.positionAbs.top + this.offset.click.top, item.top - (item.height*half), item.height),
413
- isOverRightHalf = isOverAxis(this.positionAbs.left + this.offset.click.left, item.left + (item.width/2), item.width),
414
- verticalDirection = this._getDragVerticalDirection(),
415
- horizontalDirection = this._getDragHorizontalDirection();
416
-
417
- if (this.floating && horizontalDirection) {
418
- return ((horizontalDirection == "right" && isOverRightHalf) || (horizontalDirection == "left" && !isOverRightHalf));
419
- } else {
420
- return verticalDirection && ((verticalDirection == "down" && isOverBottomHalf) || (verticalDirection == "up" && isOverTopHalf));
421
- }
422
-
423
- },
424
-
425
- _contactContainers: function(event) {
426
-
427
- if (this.options.protectRoot && this.currentItem[0].parentNode == this.element[0] ) {
428
- return;
429
- }
430
-
431
- $.ui.sortable.prototype._contactContainers.apply(this, arguments);
432
-
433
- },
434
-
435
- _clear: function(event, noPropagation) {
436
-
437
- $.ui.sortable.prototype._clear.apply(this, arguments);
438
-
439
- // mjs - clean last empty ul/ol
440
- for (var i = this.items.length - 1; i >= 0; i--) {
441
- var item = this.items[i].item[0];
442
- this._clearEmpty(item);
443
- }
444
-
445
- },
446
-
447
- serialize: function(options) {
448
-
449
- var o = $.extend({}, this.options, options),
450
- items = this._getItemsAsjQuery(o && o.connected),
451
- str = [];
452
-
453
- $(items).each(function() {
454
- var res = ($(o.item || this).attr(o.attribute || 'id') || '')
455
- .match(o.expression || (/(.+)[-=_](.+)/)),
456
- pid = ($(o.item || this).parent(o.listType)
457
- .parent(o.items)
458
- .attr(o.attribute || 'id') || '')
459
- .match(o.expression || (/(.+)[-=_](.+)/));
460
-
461
- if (res) {
462
- str.push(((o.key || res[1]) + '[' + (o.key && o.expression ? res[1] : res[2]) + ']')
463
- + '='
464
- + (pid ? (o.key && o.expression ? pid[1] : pid[2]) : o.rootID));
465
- }
466
- });
467
-
468
- if(!str.length && o.key) {
469
- str.push(o.key + '=');
470
- }
471
-
472
- return str.join('&');
473
-
474
- },
475
-
476
- toHierarchy: function(options) {
477
-
478
- var o = $.extend({}, this.options, options),
479
- sDepth = o.startDepthCount || 0,
480
- ret = [];
481
-
482
- $(this.element).children(o.items).each(function () {
483
- var level = _recursiveItems(this);
484
- ret.push(level);
485
- });
486
-
487
- return ret;
488
-
489
- function _recursiveItems(item) {
490
- var id = ($(item).attr(o.attribute || 'id') || '').match(o.expression || (/(.+)[-=_](.+)/));
491
- if (id) {
492
- var currentItem = {"id" : id[2]};
493
- if ($(item).children(o.listType).children(o.items).length > 0) {
494
- currentItem.children = [];
495
- $(item).children(o.listType).children(o.items).each(function() {
496
- var level = _recursiveItems(this);
497
- currentItem.children.push(level);
498
- });
499
- }
500
- return currentItem;
501
- }
502
- }
503
- },
504
-
505
- toArray: function(options) {
506
-
507
- var o = $.extend({}, this.options, options),
508
- sDepth = o.startDepthCount || 0,
509
- ret = [],
510
- left = 1;
511
-
512
- if (!o.excludeRoot) {
513
- ret.push({
514
- "item_id": o.rootID,
515
- "parent_id": null,
516
- "depth": sDepth,
517
- "left": left,
518
- "right": ($(o.items, this.element).length + 1) * 2
519
- });
520
- left++
521
- }
522
-
523
- $(this.element).children(o.items).each(function () {
524
- left = _recursiveArray(this, sDepth + 1, left);
525
- });
526
-
527
- ret = ret.sort(function(a,b){ return (a.left - b.left); });
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.listType).children(o.items).length > 0) {
538
- depth ++;
539
- $(item).children(o.listType).children(o.items).each(function () {
540
- right = _recursiveArray($(this), depth, right);
541
- });
542
- depth --;
543
- }
544
-
545
- id = ($(item).attr(o.attribute || 'id')).match(o.expression || (/(.+)[-=_](.+)/));
546
-
547
- if (depth === sDepth + 1) {
548
- pid = o.rootID;
549
- } else {
550
- var parentItem = ($(item).parent(o.listType)
551
- .parent(o.items)
552
- .attr(o.attribute || 'id'))
553
- .match(o.expression || (/(.+)[-=_](.+)/));
554
- pid = parentItem[2];
555
- }
556
-
557
- if (id) {
558
- ret.push({"item_id": id[2], "parent_id": pid, "depth": depth, "left": left, "right": right});
559
- }
560
-
561
- left = right + 1;
562
- return left;
563
- }
564
-
565
- },
566
-
567
- _clearEmpty: function(item) {
568
- var o = this.options;
569
-
570
- var emptyList = $(item).children(o.listType);
571
-
572
- if (emptyList.length && !emptyList.children().length && !o.doNotClear) {
573
- o.isTree && $(item).removeClass(o.branchClass + ' ' + o.expandedClass).addClass(o.leafClass);
574
- emptyList.remove();
575
- } else if (o.isTree && emptyList.length && emptyList.children().length && emptyList.is(':visible')) {
576
- $(item).removeClass(o.leafClass).addClass(o.branchClass + ' ' + o.expandedClass);
577
- } else if (o.isTree && emptyList.length && emptyList.children().length && !emptyList.is(':visible')) {
578
- $(item).removeClass(o.leafClass).addClass(o.branchClass + ' ' + o.collapsedClass);
579
- }
580
-
581
- },
582
-
583
- _getLevel: function(item) {
584
-
585
- var level = 1;
586
-
587
- if (this.options.listType) {
588
- var list = item.closest(this.options.listType);
589
- while (list && list.length > 0 &&
590
- !list.is('.ui-sortable')) {
591
- level++;
592
- list = list.parent().closest(this.options.listType);
593
- }
594
- }
595
-
596
- return level;
597
- },
598
-
599
- _getChildLevels: function(parent, depth) {
600
- var self = this,
601
- o = this.options,
602
- result = 0;
603
- depth = depth || 0;
604
-
605
- $(parent).children(o.listType).children(o.items).each(function (index, child) {
606
- result = Math.max(self._getChildLevels(child, depth + 1), result);
607
- });
608
-
609
- return depth ? result + 1 : result;
610
- },
611
-
612
- _isAllowed: function(parentItem, level, levels) {
613
- var o = this.options,
614
- maxLevels = this.placeholder.closest('.ui-sortable').nestedSortable('option', 'maxLevels'); // this takes into account the maxLevels set to the recipient list
615
-
616
- // mjs - is the root protected?
617
- // mjs - are we nesting too deep?
618
- if ( ! o.isAllowed(this.placeholder, parentItem, this.currentItem)) {
619
- this.placeholder.addClass(o.errorClass);
620
- if (maxLevels < levels && maxLevels != 0) {
621
- this.beyondMaxLevels = levels - maxLevels;
622
- } else {
623
- this.beyondMaxLevels = 1;
624
- }
625
- } else {
626
- if (maxLevels < levels && maxLevels != 0) {
627
- this.placeholder.addClass(o.errorClass);
628
- this.beyondMaxLevels = levels - maxLevels;
629
- } else {
630
- this.placeholder.removeClass(o.errorClass);
631
- this.beyondMaxLevels = 0;
632
- }
633
- }
634
- }
635
-
636
- }));
637
-
638
- $.mjs.nestedSortable.prototype.options = $.extend({}, $.ui.sortable.prototype.options, $.mjs.nestedSortable.prototype.options);
639
- })(jQuery);
13
+ (function( factory ) {
14
+ "use strict";
15
+
16
+ if ( typeof define === "function" && define.amd ) {
17
+
18
+ // AMD. Register as an anonymous module.
19
+ define([
20
+ "jquery",
21
+ "jquery-ui/sortable"
22
+ ], factory );
23
+ } else {
24
+
25
+ // Browser globals
26
+ factory( window.jQuery );
27
+ }
28
+ }(function($) {
29
+ "use strict";
30
+
31
+ function isOverAxis( x, reference, size ) {
32
+ return ( x > reference ) && ( x < ( reference + size ) );
33
+ }
34
+
35
+ $.widget("mjs.nestedSortable", $.extend({}, $.ui.sortable.prototype, {
36
+
37
+ options: {
38
+ disableParentChange: false,
39
+ doNotClear: false,
40
+ expandOnHover: 700,
41
+ isAllowed: function() { return true; },
42
+ isTree: false,
43
+ listType: "ol",
44
+ maxLevels: 0,
45
+ protectRoot: false,
46
+ rootID: null,
47
+ rtl: false,
48
+ startCollapsed: false,
49
+ tabSize: 20,
50
+
51
+ branchClass: "mjs-nestedSortable-branch",
52
+ collapsedClass: "mjs-nestedSortable-collapsed",
53
+ disableNestingClass: "mjs-nestedSortable-no-nesting",
54
+ errorClass: "mjs-nestedSortable-error",
55
+ expandedClass: "mjs-nestedSortable-expanded",
56
+ hoveringClass: "mjs-nestedSortable-hovering",
57
+ leafClass: "mjs-nestedSortable-leaf",
58
+ disabledClass: "mjs-nestedSortable-disabled"
59
+ },
60
+
61
+ _create: function() {
62
+ var self = this,
63
+ err;
64
+
65
+ this.element.data("ui-sortable", this.element.data("mjs-nestedSortable"));
66
+
67
+ // mjs - prevent browser from freezing if the HTML is not correct
68
+ if (!this.element.is(this.options.listType)) {
69
+ err = "nestedSortable: " +
70
+ "Please check that the listType option is set to your actual list type";
71
+
72
+ throw new Error(err);
73
+ }
74
+
75
+ // if we have a tree with expanding/collapsing functionality,
76
+ // force 'intersect' tolerance method
77
+ if (this.options.isTree && this.options.expandOnHover) {
78
+ this.options.tolerance = "intersect";
79
+ }
80
+
81
+ $.ui.sortable.prototype._create.apply(this, arguments);
82
+
83
+ // prepare the tree by applying the right classes
84
+ // (the CSS is responsible for actual hide/show functionality)
85
+ if (this.options.isTree) {
86
+ $(this.items).each(function() {
87
+ var $li = this.item,
88
+ hasCollapsedClass = $li.hasClass(self.options.collapsedClass),
89
+ hasExpandedClass = $li.hasClass(self.options.expandedClass);
90
+
91
+ if ($li.children(self.options.listType).length) {
92
+ $li.addClass(self.options.branchClass);
93
+ // expand/collapse class only if they have children
94
+
95
+ if ( !hasCollapsedClass && !hasExpandedClass ) {
96
+ if (self.options.startCollapsed) {
97
+ $li.addClass(self.options.collapsedClass);
98
+ } else {
99
+ $li.addClass(self.options.expandedClass);
100
+ }
101
+ }
102
+ } else {
103
+ $li.addClass(self.options.leafClass);
104
+ }
105
+ });
106
+ }
107
+ },
108
+
109
+ _destroy: function() {
110
+ this.element
111
+ .removeData("mjs-nestedSortable")
112
+ .removeData("ui-sortable");
113
+ return $.ui.sortable.prototype._destroy.apply(this, arguments);
114
+ },
115
+
116
+ _mouseDrag: function(event) {
117
+ var i,
118
+ item,
119
+ itemElement,
120
+ intersection,
121
+ self = this,
122
+ o = this.options,
123
+ scrolled = false,
124
+ $document = $(document),
125
+ previousTopOffset,
126
+ parentItem,
127
+ level,
128
+ childLevels,
129
+ itemAfter,
130
+ itemBefore,
131
+ newList,
132
+ method,
133
+ a,
134
+ previousItem,
135
+ nextItem,
136
+ helperIsNotSibling;
137
+
138
+ //Compute the helpers position
139
+ this.position = this._generatePosition(event);
140
+ this.positionAbs = this._convertPositionTo("absolute");
141
+
142
+ if (!this.lastPositionAbs) {
143
+ this.lastPositionAbs = this.positionAbs;
144
+ }
145
+
146
+ //Do scrolling
147
+ if (this.options.scroll) {
148
+ if (this.scrollParent[0] !== document && this.scrollParent[0].tagName !== "HTML") {
149
+
150
+ if (
151
+ (
152
+ this.overflowOffset.top +
153
+ this.scrollParent[0].offsetHeight
154
+ ) -
155
+ event.pageY <
156
+ o.scrollSensitivity
157
+ ) {
158
+ scrolled = this.scrollParent.scrollTop() + o.scrollSpeed;
159
+ this.scrollParent.scrollTop(scrolled);
160
+ } else if (
161
+ event.pageY -
162
+ this.overflowOffset.top <
163
+ o.scrollSensitivity
164
+ ) {
165
+ scrolled = this.scrollParent.scrollTop() - o.scrollSpeed;
166
+ this.scrollParent.scrollTop(scrolled);
167
+ }
168
+
169
+ if (
170
+ (
171
+ this.overflowOffset.left +
172
+ this.scrollParent[0].offsetWidth
173
+ ) -
174
+ event.pageX <
175
+ o.scrollSensitivity
176
+ ) {
177
+ scrolled = this.scrollParent.scrollLeft() + o.scrollSpeed;
178
+ this.scrollParent.scrollLeft(scrolled);
179
+ } else if (
180
+ event.pageX -
181
+ this.overflowOffset.left <
182
+ o.scrollSensitivity
183
+ ) {
184
+ scrolled = this.scrollParent.scrollLeft() - o.scrollSpeed;
185
+ this.scrollParent.scrollLeft(scrolled);
186
+ }
187
+
188
+ } else {
189
+
190
+ if (
191
+ event.pageY -
192
+ $document.scrollTop() <
193
+ o.scrollSensitivity
194
+ ) {
195
+ scrolled = $document.scrollTop() - o.scrollSpeed;
196
+ $document.scrollTop(scrolled);
197
+ } else if (
198
+ $(window).height() -
199
+ (
200
+ event.pageY -
201
+ $document.scrollTop()
202
+ ) <
203
+ o.scrollSensitivity
204
+ ) {
205
+ scrolled = $document.scrollTop() + o.scrollSpeed;
206
+ $document.scrollTop(scrolled);
207
+ }
208
+
209
+ if (
210
+ event.pageX -
211
+ $document.scrollLeft() <
212
+ o.scrollSensitivity
213
+ ) {
214
+ scrolled = $document.scrollLeft() - o.scrollSpeed;
215
+ $document.scrollLeft(scrolled);
216
+ } else if (
217
+ $(window).width() -
218
+ (
219
+ event.pageX -
220
+ $document.scrollLeft()
221
+ ) <
222
+ o.scrollSensitivity
223
+ ) {
224
+ scrolled = $document.scrollLeft() + o.scrollSpeed;
225
+ $document.scrollLeft(scrolled);
226
+ }
227
+
228
+ }
229
+
230
+ if (scrolled !== false && $.ui.ddmanager && !o.dropBehaviour) {
231
+ $.ui.ddmanager.prepareOffsets(this, event);
232
+ }
233
+ }
234
+
235
+ //Regenerate the absolute position used for position checks
236
+ this.positionAbs = this._convertPositionTo("absolute");
237
+
238
+ // mjs - find the top offset before rearrangement,
239
+ previousTopOffset = this.placeholder.offset().top;
240
+
241
+ //Set the helper position
242
+ if (!this.options.axis || this.options.axis !== "y") {
243
+ this.helper[0].style.left = this.position.left + "px";
244
+ }
245
+ if (!this.options.axis || this.options.axis !== "x") {
246
+ this.helper[0].style.top = (this.position.top) + "px";
247
+ }
248
+
249
+ // mjs - check and reset hovering state at each cycle
250
+ this.hovering = this.hovering ? this.hovering : null;
251
+ this.mouseentered = this.mouseentered ? this.mouseentered : false;
252
+
253
+ // mjs - let's start caching some variables
254
+ (function() {
255
+ var _parentItem = this.placeholder.parent().parent();
256
+ if (_parentItem && _parentItem.closest(".ui-sortable").length) {
257
+ parentItem = _parentItem;
258
+ }
259
+ }.call(this));
260
+
261
+ level = this._getLevel(this.placeholder);
262
+ childLevels = this._getChildLevels(this.helper);
263
+ newList = document.createElement(o.listType);
264
+
265
+ //Rearrange
266
+ for (i = this.items.length - 1; i >= 0; i--) {
267
+
268
+ //Cache variables and intersection, continue if no intersection
269
+ item = this.items[i];
270
+ itemElement = item.item[0];
271
+ intersection = this._intersectsWithPointer(item);
272
+ if (!intersection) {
273
+ continue;
274
+ }
275
+
276
+ // Only put the placeholder inside the current Container, skip all
277
+ // items form other containers. This works because when moving
278
+ // an item from one container to another the
279
+ // currentContainer is switched before the placeholder is moved.
280
+ //
281
+ // Without this moving items in "sub-sortables" can cause the placeholder to jitter
282
+ // beetween the outer and inner container.
283
+ if (item.instance !== this.currentContainer) {
284
+ continue;
285
+ }
286
+
287
+ // No action if intersected item is disabled
288
+ // and the element above or below in the direction we're going is also disabled
289
+ if (itemElement.className.indexOf(o.disabledClass) !== -1) {
290
+ // Note: intersection hardcoded direction values from
291
+ // jquery.ui.sortable.js:_intersectsWithPointer
292
+ if (intersection === 2) {
293
+ // Going down
294
+ itemAfter = this.items[i + 1];
295
+ if (itemAfter && itemAfter.item.hasClass(o.disabledClass)) {
296
+ continue;
297
+ }
298
+
299
+ } else if (intersection === 1) {
300
+ // Going up
301
+ itemBefore = this.items[i - 1];
302
+ if (itemBefore && itemBefore.item.hasClass(o.disabledClass)) {
303
+ continue;
304
+ }
305
+ }
306
+ }
307
+
308
+ method = intersection === 1 ? "next" : "prev";
309
+
310
+ // cannot intersect with itself
311
+ // no useless actions that have been done before
312
+ // no action if the item moved is the parent of the item checked
313
+ if (itemElement !== this.currentItem[0] &&
314
+ this.placeholder[method]()[0] !== itemElement &&
315
+ !$.contains(this.placeholder[0], itemElement) &&
316
+ (
317
+ this.options.type === "semi-dynamic" ?
318
+ !$.contains(this.element[0], itemElement) :
319
+ true
320
+ )
321
+ ) {
322
+
323
+ // mjs - we are intersecting an element:
324
+ // trigger the mouseenter event and store this state
325
+ if (!this.mouseentered) {
326
+ $(itemElement).mouseenter();
327
+ this.mouseentered = true;
328
+ }
329
+
330
+ // mjs - if the element has children and they are hidden,
331
+ // show them after a delay (CSS responsible)
332
+ if (o.isTree && $(itemElement).hasClass(o.collapsedClass) && o.expandOnHover) {
333
+ if (!this.hovering) {
334
+ $(itemElement).addClass(o.hoveringClass);
335
+ this.hovering = window.setTimeout(function() {
336
+ $(itemElement)
337
+ .removeClass(o.collapsedClass)
338
+ .addClass(o.expandedClass);
339
+
340
+ self.refreshPositions();
341
+ self._trigger("expand", event, self._uiHash());
342
+ }, o.expandOnHover);
343
+ }
344
+ }
345
+
346
+ this.direction = intersection === 1 ? "down" : "up";
347
+
348
+ // mjs - rearrange the elements and reset timeouts and hovering state
349
+ if (this.options.tolerance === "pointer" || this._intersectsWithSides(item)) {
350
+ $(itemElement).mouseleave();
351
+ this.mouseentered = false;
352
+ $(itemElement).removeClass(o.hoveringClass);
353
+ if (this.hovering) {
354
+ window.clearTimeout(this.hovering);
355
+ }
356
+ this.hovering = null;
357
+
358
+ // mjs - do not switch container if
359
+ // it's a root item and 'protectRoot' is true
360
+ // or if it's not a root item but we are trying to make it root
361
+ if (o.protectRoot &&
362
+ !(
363
+ this.currentItem[0].parentNode === this.element[0] &&
364
+ // it's a root item
365
+ itemElement.parentNode !== this.element[0]
366
+ // it's intersecting a non-root item
367
+ )
368
+ ) {
369
+ if (this.currentItem[0].parentNode !== this.element[0] &&
370
+ itemElement.parentNode === this.element[0]
371
+ ) {
372
+
373
+ if ( !$(itemElement).children(o.listType).length) {
374
+ itemElement.appendChild(newList);
375
+ if (o.isTree) {
376
+ $(itemElement)
377
+ .removeClass(o.leafClass)
378
+ .addClass(o.branchClass + " " + o.expandedClass);
379
+ }
380
+ }
381
+
382
+ if (this.direction === "down") {
383
+ a = $(itemElement).prev().children(o.listType);
384
+ } else {
385
+ a = $(itemElement).children(o.listType);
386
+ }
387
+
388
+ if (a[0] !== undefined) {
389
+ this._rearrange(event, null, a);
390
+ }
391
+
392
+ } else {
393
+ this._rearrange(event, item);
394
+ }
395
+ } else if (!o.protectRoot) {
396
+ this._rearrange(event, item);
397
+ }
398
+ } else {
399
+ break;
400
+ }
401
+
402
+ // Clear emtpy ul's/ol's
403
+ this._clearEmpty(itemElement);
404
+
405
+ this._trigger("change", event, this._uiHash());
406
+ break;
407
+ }
408
+ }
409
+
410
+ // mjs - to find the previous sibling in the list,
411
+ // keep backtracking until we hit a valid list item.
412
+ (function() {
413
+ var _previousItem = this.placeholder.prev();
414
+ if (_previousItem.length) {
415
+ previousItem = _previousItem;
416
+ } else {
417
+ previousItem = null;
418
+ }
419
+ }.call(this));
420
+
421
+ if (previousItem != null) {
422
+ while (
423
+ previousItem[0].nodeName.toLowerCase() !== "li" ||
424
+ previousItem[0].className.indexOf(o.disabledClass) !== -1 ||
425
+ previousItem[0] === this.currentItem[0] ||
426
+ previousItem[0] === this.helper[0]
427
+ ) {
428
+ if (previousItem[0].previousSibling) {
429
+ previousItem = $(previousItem[0].previousSibling);
430
+ } else {
431
+ previousItem = null;
432
+ break;
433
+ }
434
+ }
435
+ }
436
+
437
+ // mjs - to find the next sibling in the list,
438
+ // keep stepping forward until we hit a valid list item.
439
+ (function() {
440
+ var _nextItem = this.placeholder.next();
441
+ if (_nextItem.length) {
442
+ nextItem = _nextItem;
443
+ } else {
444
+ nextItem = null;
445
+ }
446
+ }.call(this));
447
+
448
+ if (nextItem != null) {
449
+ while (
450
+ nextItem[0].nodeName.toLowerCase() !== "li" ||
451
+ nextItem[0].className.indexOf(o.disabledClass) !== -1 ||
452
+ nextItem[0] === this.currentItem[0] ||
453
+ nextItem[0] === this.helper[0]
454
+ ) {
455
+ if (nextItem[0].nextSibling) {
456
+ nextItem = $(nextItem[0].nextSibling);
457
+ } else {
458
+ nextItem = null;
459
+ break;
460
+ }
461
+ }
462
+ }
463
+
464
+ this.beyondMaxLevels = 0;
465
+
466
+ // mjs - if the item is moved to the left, send it one level up
467
+ // but only if it's at the bottom of the list
468
+ if (parentItem != null &&
469
+ nextItem == null &&
470
+ !(o.protectRoot && parentItem[0].parentNode == this.element[0]) &&
471
+ (
472
+ o.rtl &&
473
+ (
474
+ this.positionAbs.left +
475
+ this.helper.outerWidth() > parentItem.offset().left +
476
+ parentItem.outerWidth()
477
+ ) ||
478
+ !o.rtl && (this.positionAbs.left < parentItem.offset().left)
479
+ )
480
+ ) {
481
+
482
+ parentItem.after(this.placeholder[0]);
483
+ helperIsNotSibling = !parentItem
484
+ .children(o.listItem)
485
+ .children("li:visible:not(.ui-sortable-helper)")
486
+ .length;
487
+ if (o.isTree && helperIsNotSibling) {
488
+ parentItem
489
+ .removeClass(this.options.branchClass + " " + this.options.expandedClass)
490
+ .addClass(this.options.leafClass);
491
+ }
492
+ if(typeof parentItem !== 'undefined')
493
+ this._clearEmpty(parentItem[0]);
494
+ this._trigger("change", event, this._uiHash());
495
+ // mjs - if the item is below a sibling and is moved to the right,
496
+ // make it a child of that sibling
497
+ } else if (previousItem != null &&
498
+ !previousItem.hasClass(o.disableNestingClass) &&
499
+ (
500
+ previousItem.children(o.listType).length &&
501
+ previousItem.children(o.listType).is(":visible") ||
502
+ !previousItem.children(o.listType).length
503
+ ) &&
504
+ !(o.protectRoot && this.currentItem[0].parentNode === this.element[0]) &&
505
+ (
506
+ o.rtl &&
507
+ (
508
+ this.positionAbs.left +
509
+ this.helper.outerWidth() <
510
+ previousItem.offset().left +
511
+ previousItem.outerWidth() -
512
+ o.tabSize
513
+ ) ||
514
+ !o.rtl &&
515
+ (this.positionAbs.left > previousItem.offset().left + o.tabSize)
516
+ )
517
+ ) {
518
+
519
+ this._isAllowed(previousItem, level, level + childLevels + 1);
520
+
521
+ if (!previousItem.children(o.listType).length) {
522
+ previousItem[0].appendChild(newList);
523
+ if (o.isTree) {
524
+ previousItem
525
+ .removeClass(o.leafClass)
526
+ .addClass(o.branchClass + " " + o.expandedClass);
527
+ }
528
+ }
529
+
530
+ // mjs - if this item is being moved from the top, add it to the top of the list.
531
+ if (previousTopOffset && (previousTopOffset <= previousItem.offset().top)) {
532
+ previousItem.children(o.listType).prepend(this.placeholder);
533
+ } else {
534
+ // mjs - otherwise, add it to the bottom of the list.
535
+ previousItem.children(o.listType)[0].appendChild(this.placeholder[0]);
536
+ }
537
+ if(typeof parentItem !== 'undefined')
538
+ this._clearEmpty(parentItem[0]);
539
+ this._trigger("change", event, this._uiHash());
540
+ } else {
541
+ this._isAllowed(parentItem, level, level + childLevels);
542
+ }
543
+
544
+ //Post events to containers
545
+ this._contactContainers(event);
546
+
547
+ //Interconnect with droppables
548
+ if ($.ui.ddmanager) {
549
+ $.ui.ddmanager.drag(this, event);
550
+ }
551
+
552
+ //Call callbacks
553
+ this._trigger("sort", event, this._uiHash());
554
+
555
+ this.lastPositionAbs = this.positionAbs;
556
+ return false;
557
+
558
+ },
559
+
560
+ _mouseStop: function(event) {
561
+ // mjs - if the item is in a position not allowed, send it back
562
+ if (this.beyondMaxLevels) {
563
+
564
+ this.placeholder.removeClass(this.options.errorClass);
565
+
566
+ if (this.domPosition.prev) {
567
+ $(this.domPosition.prev).after(this.placeholder);
568
+ } else {
569
+ $(this.domPosition.parent).prepend(this.placeholder);
570
+ }
571
+
572
+ this._trigger("revert", event, this._uiHash());
573
+
574
+ }
575
+
576
+ // mjs - clear the hovering timeout, just to be sure
577
+ $("." + this.options.hoveringClass)
578
+ .mouseleave()
579
+ .removeClass(this.options.hoveringClass);
580
+
581
+ this.mouseentered = false;
582
+ if (this.hovering) {
583
+ window.clearTimeout(this.hovering);
584
+ }
585
+ this.hovering = null;
586
+
587
+ this._relocate_event = event;
588
+ this._pid_current = $(this.domPosition.parent).parent().attr("id");
589
+ this._sort_current = this.domPosition.prev ? $(this.domPosition.prev).next().index() : 0;
590
+ $.ui.sortable.prototype._mouseStop.apply(this, arguments); //asybnchronous execution, @see _clear for the relocate event.
591
+ },
592
+
593
+ // mjs - this function is slightly modified
594
+ // to make it easier to hover over a collapsed element and have it expand
595
+ _intersectsWithSides: function(item) {
596
+
597
+ var half = this.options.isTree ? .8 : .5,
598
+ isOverBottomHalf = isOverAxis(
599
+ this.positionAbs.top + this.offset.click.top,
600
+ item.top + (item.height * half),
601
+ item.height
602
+ ),
603
+ isOverTopHalf = isOverAxis(
604
+ this.positionAbs.top + this.offset.click.top,
605
+ item.top - (item.height * half),
606
+ item.height
607
+ ),
608
+ isOverRightHalf = isOverAxis(
609
+ this.positionAbs.left + this.offset.click.left,
610
+ item.left + (item.width / 2),
611
+ item.width
612
+ ),
613
+ verticalDirection = this._getDragVerticalDirection(),
614
+ horizontalDirection = this._getDragHorizontalDirection();
615
+
616
+ if (this.floating && horizontalDirection) {
617
+ return (
618
+ (horizontalDirection === "right" && isOverRightHalf) ||
619
+ (horizontalDirection === "left" && !isOverRightHalf)
620
+ );
621
+ } else {
622
+ return verticalDirection && (
623
+ (verticalDirection === "down" && isOverBottomHalf) ||
624
+ (verticalDirection === "up" && isOverTopHalf)
625
+ );
626
+ }
627
+
628
+ },
629
+
630
+ _contactContainers: function() {
631
+
632
+ if (this.options.protectRoot && this.currentItem[0].parentNode === this.element[0] ) {
633
+ return;
634
+ }
635
+
636
+ $.ui.sortable.prototype._contactContainers.apply(this, arguments);
637
+
638
+ },
639
+
640
+ _clear: function() {
641
+ var i,
642
+ item;
643
+
644
+ $.ui.sortable.prototype._clear.apply(this, arguments);
645
+
646
+ //relocate event
647
+ if (!(this._pid_current === this._uiHash().item.parent().parent().attr("id") &&
648
+ this._sort_current === this._uiHash().item.index())) {
649
+ this._trigger("relocate", this._relocate_event, this._uiHash());
650
+ }
651
+
652
+ // mjs - clean last empty ul/ol
653
+ for (i = this.items.length - 1; i >= 0; i--) {
654
+ item = this.items[i].item[0];
655
+ this._clearEmpty(item);
656
+ }
657
+
658
+ },
659
+
660
+ serialize: function(options) {
661
+
662
+ var o = $.extend({}, this.options, options),
663
+ items = this._getItemsAsjQuery(o && o.connected),
664
+ str = [];
665
+
666
+ $(items).each(function() {
667
+ var res = ($(o.item || this).attr(o.attribute || "id") || "")
668
+ .match(o.expression || (/(.+)[-=_](.+)/)),
669
+ pid = ($(o.item || this).parent(o.listType)
670
+ .parent(o.items)
671
+ .attr(o.attribute || "id") || "")
672
+ .match(o.expression || (/(.+)[-=_](.+)/));
673
+
674
+ if (res) {
675
+ str.push(
676
+ (
677
+ (o.key || res[1]) +
678
+ "[" +
679
+ (o.key && o.expression ? res[1] : res[2]) + "]"
680
+ ) +
681
+ "=" +
682
+ (pid ? (o.key && o.expression ? pid[1] : pid[2]) : o.rootID));
683
+ }
684
+ });
685
+
686
+ if (!str.length && o.key) {
687
+ str.push(o.key + "=");
688
+ }
689
+
690
+ return str.join("&");
691
+
692
+ },
693
+
694
+ toHierarchy: function(options) {
695
+
696
+ var o = $.extend({}, this.options, options),
697
+ ret = [];
698
+
699
+ $(this.element).children(o.items).each(function() {
700
+ var level = _recursiveItems(this);
701
+ ret.push(level);
702
+ });
703
+
704
+ return ret;
705
+
706
+ function _recursiveItems(item) {
707
+ var id = ($(item).attr(o.attribute || "id") || "").match(o.expression || (/(.+)[-=_](.+)/)),
708
+ currentItem;
709
+
710
+ var data = $(item).data();
711
+ if (data.nestedSortableItem) {
712
+ delete data.nestedSortableItem; // Remove the nestedSortableItem object from the data
713
+ }
714
+
715
+ if (id) {
716
+ currentItem = {
717
+ "id": id[2]
718
+ };
719
+
720
+ currentItem = $.extend({}, currentItem, data); // Combine the two objects
721
+
722
+ if ($(item).children(o.listType).children(o.items).length > 0) {
723
+ currentItem.children = [];
724
+ $(item).children(o.listType).children(o.items).each(function() {
725
+ var level = _recursiveItems(this);
726
+ currentItem.children.push(level);
727
+ });
728
+ }
729
+ return currentItem;
730
+ }
731
+ }
732
+ },
733
+
734
+ toArray: function(options) {
735
+
736
+ var o = $.extend({}, this.options, options),
737
+ sDepth = o.startDepthCount || 0,
738
+ ret = [],
739
+ left = 1;
740
+
741
+ if (!o.excludeRoot) {
742
+ ret.push({
743
+ "item_id": o.rootID,
744
+ "parent_id": null,
745
+ "depth": sDepth,
746
+ "left": left,
747
+ "right": ($(o.items, this.element).length + 1) * 2
748
+ });
749
+ left++;
750
+ }
751
+
752
+ $(this.element).children(o.items).each(function() {
753
+ left = _recursiveArray(this, sDepth, left);
754
+ });
755
+
756
+ ret = ret.sort(function(a, b) { return (a.left - b.left); });
757
+
758
+ return ret;
759
+
760
+ function _recursiveArray(item, depth, _left) {
761
+
762
+ var right = _left + 1,
763
+ id,
764
+ pid,
765
+ parentItem;
766
+
767
+ if ($(item).children(o.listType).children(o.items).length > 0) {
768
+ depth++;
769
+ $(item).children(o.listType).children(o.items).each(function() {
770
+ right = _recursiveArray($(this), depth, right);
771
+ });
772
+ depth--;
773
+ }
774
+
775
+ id = ($(item).attr(o.attribute || "id")).match(o.expression || (/(.+)[-=_](.+)/));
776
+
777
+ if (depth === sDepth) {
778
+ pid = o.rootID;
779
+ } else {
780
+ parentItem = ($(item).parent(o.listType)
781
+ .parent(o.items)
782
+ .attr(o.attribute || "id"))
783
+ .match(o.expression || (/(.+)[-=_](.+)/));
784
+ pid = parentItem[2];
785
+ }
786
+
787
+ if (id) {
788
+ var name = $(item).data("name");
789
+ ret.push({
790
+ "id": id[2],
791
+ "parent_id": pid,
792
+ "depth": depth,
793
+ "left": _left,
794
+ "right": right,
795
+ "name":name
796
+ });
797
+ }
798
+
799
+ _left = right + 1;
800
+ return _left;
801
+ }
802
+
803
+ },
804
+
805
+ _clearEmpty: function (item) {
806
+ function replaceClass(elem, search, replace, swap) {
807
+ if (swap) {
808
+ search = [replace, replace = search][0];
809
+ }
810
+
811
+ $(elem).removeClass(search).addClass(replace);
812
+ }
813
+
814
+ var o = this.options,
815
+ childrenList = $(item).children(o.listType),
816
+ hasChildren = childrenList.is(':not(:empty)');
817
+
818
+ var doNotClear =
819
+ o.doNotClear ||
820
+ hasChildren ||
821
+ o.protectRoot && $(item)[0] === this.element[0];
822
+
823
+ if (o.isTree) {
824
+ replaceClass(item, o.branchClass, o.leafClass, doNotClear);
825
+
826
+ if (doNotClear && hasChildren) {
827
+ replaceClass(item, o.collapsedClass, o.expandedClass);
828
+ }
829
+ }
830
+
831
+ if (!doNotClear) {
832
+ childrenList.remove();
833
+ }
834
+ },
835
+
836
+ _getLevel: function(item) {
837
+
838
+ var level = 1,
839
+ list;
840
+
841
+ if (this.options.listType) {
842
+ list = item.closest(this.options.listType);
843
+ while (list && list.length > 0 && !list.is(".ui-sortable")) {
844
+ level++;
845
+ list = list.parent().closest(this.options.listType);
846
+ }
847
+ }
848
+
849
+ return level;
850
+ },
851
+
852
+ _getChildLevels: function(parent, depth) {
853
+ var self = this,
854
+ o = this.options,
855
+ result = 0;
856
+ depth = depth || 0;
857
+
858
+ $(parent).children(o.listType).children(o.items).each(function(index, child) {
859
+ result = Math.max(self._getChildLevels(child, depth + 1), result);
860
+ });
861
+
862
+ return depth ? result + 1 : result;
863
+ },
864
+
865
+ _isAllowed: function(parentItem, level, levels) {
866
+ var o = this.options,
867
+ // this takes into account the maxLevels set to the recipient list
868
+ maxLevels = this
869
+ .placeholder
870
+ .closest(".ui-sortable")
871
+ .nestedSortable("option", "maxLevels"),
872
+
873
+ // Check if the parent has changed to prevent it, when o.disableParentChange is true
874
+ oldParent = this.currentItem.parent().parent(),
875
+ disabledByParentchange = o.disableParentChange && (
876
+ //From somewhere to somewhere else, except the root
877
+ typeof parentItem !== 'undefined' && !oldParent.is(parentItem) ||
878
+ typeof parentItem === 'undefined' && oldParent.is("li") //From somewhere to the root
879
+ );
880
+ // mjs - is the root protected?
881
+ // mjs - are we nesting too deep?
882
+ if (
883
+ disabledByParentchange ||
884
+ !o.isAllowed(this.placeholder, parentItem, this.currentItem)
885
+ ) {
886
+ this.placeholder.addClass(o.errorClass);
887
+ if (maxLevels < levels && maxLevels !== 0) {
888
+ this.beyondMaxLevels = levels - maxLevels;
889
+ } else {
890
+ this.beyondMaxLevels = 1;
891
+ }
892
+ } else {
893
+ if (maxLevels < levels && maxLevels !== 0) {
894
+ this.placeholder.addClass(o.errorClass);
895
+ this.beyondMaxLevels = levels - maxLevels;
896
+ } else {
897
+ this.placeholder.removeClass(o.errorClass);
898
+ this.beyondMaxLevels = 0;
899
+ }
900
+ }
901
+ }
902
+
903
+ }));
904
+
905
+ $.mjs.nestedSortable.prototype.options = $.extend(
906
+ {},
907
+ $.ui.sortable.prototype.options,
908
+ $.mjs.nestedSortable.prototype.options
909
+ );
910
+ }));