1/* 2 * jQuery UI Nested Sortable 3 * v 2.1a / 2016-02-04 4 * https://github.com/ilikenwf/nestedSortable 5 * 6 * Depends on: 7 * jquery.ui.sortable.js 1.10+ 8 * 9 * Copyright (c) 2010-2016 Manuele J Sarfatti and contributors 10 * Licensed under the MIT License 11 * http://www.opensource.org/licenses/mit-license.php 12 */ 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/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(), itemElement]); 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 data = $(item).children('div').data(); 789 var itemObj = $.extend( data, { 790 "id":id[2], 791 "parent_id":pid, 792 "depth":depth, 793 "left":_left, 794 "right":right 795 } ); 796 ret.push( itemObj ); 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.has('li').length; 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 827 if (!doNotClear) { 828 childrenList.parent().removeClass(o.expandedClass); 829 childrenList.remove(); 830 } 831 }, 832 833 _getLevel: function(item) { 834 835 var level = 1, 836 list; 837 838 if (this.options.listType) { 839 list = item.closest(this.options.listType); 840 while (list && list.length > 0 && !list.is(".ui-sortable")) { 841 level++; 842 list = list.parent().closest(this.options.listType); 843 } 844 } 845 846 return level; 847 }, 848 849 _getChildLevels: function(parent, depth) { 850 var self = this, 851 o = this.options, 852 result = 0; 853 depth = depth || 0; 854 855 $(parent).children(o.listType).children(o.items).each(function(index, child) { 856 result = Math.max(self._getChildLevels(child, depth + 1), result); 857 }); 858 859 return depth ? result + 1 : result; 860 }, 861 862 _isAllowed: function(parentItem, level, levels) { 863 var o = this.options, 864 // this takes into account the maxLevels set to the recipient list 865 maxLevels = this 866 .placeholder 867 .closest(".ui-sortable") 868 .nestedSortable("option", "maxLevels"), 869 870 // Check if the parent has changed to prevent it, when o.disableParentChange is true 871 oldParent = this.currentItem.parent().parent(), 872 disabledByParentchange = o.disableParentChange && ( 873 //From somewhere to somewhere else, except the root 874 typeof parentItem !== 'undefined' && !oldParent.is(parentItem) || 875 typeof parentItem === 'undefined' && oldParent.is("li") //From somewhere to the root 876 ); 877 // mjs - is the root protected? 878 // mjs - are we nesting too deep? 879 if ( 880 disabledByParentchange || 881 !o.isAllowed(this.placeholder, parentItem, this.currentItem) 882 ) { 883 this.placeholder.addClass(o.errorClass); 884 if (maxLevels < levels && maxLevels !== 0) { 885 this.beyondMaxLevels = levels - maxLevels; 886 } else { 887 this.beyondMaxLevels = 1; 888 } 889 } else { 890 if (maxLevels < levels && maxLevels !== 0) { 891 this.placeholder.addClass(o.errorClass); 892 this.beyondMaxLevels = levels - maxLevels; 893 } else { 894 this.placeholder.removeClass(o.errorClass); 895 this.beyondMaxLevels = 0; 896 } 897 } 898 } 899 900 })); 901 902 $.mjs.nestedSortable.prototype.options = $.extend( 903 {}, 904 $.ui.sortable.prototype.options, 905 $.mjs.nestedSortable.prototype.options 906 ); 907}));
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.