bs-modal.js
11 years ago
jquery.mjs.nestedSortable.js
11 years ago
jquery.ui.touch-punch.min.js
11 years ago
nestedpages.js
11 years ago
jquery.mjs.nestedSortable.js
665 lines
| 1 | /* |
| 2 | * jQuery UI Nested Sortable |
| 3 | * v 2.0 / 29 oct 2012 |
| 4 | * http://mjsarfatti.com/sandbox/nestedSortable |
| 5 | * |
| 6 | * Depends on: |
| 7 | * jquery.ui.sortable.js 1.10+ |
| 8 | * |
| 9 | * Copyright (c) 2010-2013 Manuele J Sarfatti |
| 10 | * Licensed under the MIT License |
| 11 | * http://www.opensource.org/licenses/mit-license.php |
| 12 | */ |
| 13 | (function( factory ) { |
| 14 | if ( typeof define === "function" && define.amd ) { |
| 15 | |
| 16 | // AMD. Register as an anonymous module. |
| 17 | define([ |
| 18 | "jquery", |
| 19 | "jquery-ui/sortable" |
| 20 | ], factory ); |
| 21 | } else { |
| 22 | |
| 23 | // Browser globals |
| 24 | factory( jQuery ); |
| 25 | } |
| 26 | }(function($) { |
| 27 | |
| 28 | function isOverAxis( x, reference, size ) { |
| 29 | return ( x > reference ) && ( x < ( reference + size ) ); |
| 30 | } |
| 31 | |
| 32 | $.widget("mjs.nestedSortable", $.extend({}, $.ui.sortable.prototype, { |
| 33 | |
| 34 | options: { |
| 35 | disableParentChange: false, |
| 36 | doNotClear: false, |
| 37 | expandOnHover: 700, |
| 38 | isAllowed: function(placeholder, placeholderParent, originalItem) { return true; }, |
| 39 | isTree: false, |
| 40 | listType: 'ol', |
| 41 | maxLevels: 0, |
| 42 | protectRoot: false, |
| 43 | rootID: null, |
| 44 | rtl: false, |
| 45 | startCollapsed: false, |
| 46 | tabSize: 20, |
| 47 | |
| 48 | branchClass: 'mjs-nestedSortable-branch', |
| 49 | collapsedClass: 'mjs-nestedSortable-collapsed', |
| 50 | disableNestingClass: 'mjs-nestedSortable-no-nesting', |
| 51 | errorClass: 'mjs-nestedSortable-error', |
| 52 | expandedClass: 'mjs-nestedSortable-expanded', |
| 53 | hoveringClass: 'mjs-nestedSortable-hovering', |
| 54 | leafClass: 'mjs-nestedSortable-leaf', |
| 55 | disabledClass: 'mjs-nestedSortable-disabled' |
| 56 | }, |
| 57 | |
| 58 | _create: function() { |
| 59 | this.element.data('ui-sortable', this.element.data('mjs-nestedSortable')); |
| 60 | |
| 61 | // mjs - prevent browser from freezing if the HTML is not correct |
| 62 | if (!this.element.is(this.options.listType)) |
| 63 | throw new Error('nestedSortable: Please check that the listType option is set to your actual list type'); |
| 64 | |
| 65 | // mjs - force 'intersect' tolerance method if we have a tree with expanding/collapsing functionality |
| 66 | if (this.options.isTree && this.options.expandOnHover) { |
| 67 | this.options.tolerance = 'intersect'; |
| 68 | } |
| 69 | |
| 70 | $.ui.sortable.prototype._create.apply(this, arguments); |
| 71 | |
| 72 | // mjs - prepare the tree by applying the right classes (the CSS is responsible for actual hide/show functionality) |
| 73 | if (this.options.isTree) { |
| 74 | var self = this; |
| 75 | $(this.items).each(function() { |
| 76 | var $li = this.item; |
| 77 | if ($li.children(self.options.listType).length) { |
| 78 | $li.addClass(self.options.branchClass); |
| 79 | // expand/collapse class only if they have children |
| 80 | if ( ! $li.hasClass( self.options.collapsedClass ) && ( ! $li.hasClass( self.options.expandedClass ) ) ) { |
| 81 | if (self.options.startCollapsed) $li.addClass(self.options.collapsedClass); |
| 82 | else $li.addClass(self.options.expandedClass); |
| 83 | } |
| 84 | } else { |
| 85 | $li.addClass(self.options.leafClass); |
| 86 | } |
| 87 | }); |
| 88 | } |
| 89 | }, |
| 90 | |
| 91 | _destroy: function() { |
| 92 | this.element |
| 93 | .removeData("mjs-nestedSortable") |
| 94 | .removeData("ui-sortable"); |
| 95 | return $.ui.sortable.prototype._destroy.apply(this, arguments); |
| 96 | }, |
| 97 | |
| 98 | _mouseDrag: function(event) { |
| 99 | var i, item, itemElement, intersection, |
| 100 | o = this.options, |
| 101 | scrolled = false; |
| 102 | |
| 103 | //Compute the helpers position |
| 104 | this.position = this._generatePosition(event); |
| 105 | this.positionAbs = this._convertPositionTo("absolute"); |
| 106 | |
| 107 | if (!this.lastPositionAbs) { |
| 108 | this.lastPositionAbs = this.positionAbs; |
| 109 | } |
| 110 | |
| 111 | //Do scrolling |
| 112 | if(this.options.scroll) { |
| 113 | if(this.scrollParent[0] != document && this.scrollParent[0].tagName != 'HTML') { |
| 114 | |
| 115 | if((this.overflowOffset.top + this.scrollParent[0].offsetHeight) - event.pageY < o.scrollSensitivity) { |
| 116 | this.scrollParent[0].scrollTop = scrolled = this.scrollParent[0].scrollTop + o.scrollSpeed; |
| 117 | } else if(event.pageY - this.overflowOffset.top < o.scrollSensitivity) { |
| 118 | this.scrollParent[0].scrollTop = scrolled = this.scrollParent[0].scrollTop - o.scrollSpeed; |
| 119 | } |
| 120 | |
| 121 | if((this.overflowOffset.left + this.scrollParent[0].offsetWidth) - event.pageX < o.scrollSensitivity) { |
| 122 | this.scrollParent[0].scrollLeft = scrolled = this.scrollParent[0].scrollLeft + o.scrollSpeed; |
| 123 | } else if(event.pageX - this.overflowOffset.left < o.scrollSensitivity) { |
| 124 | this.scrollParent[0].scrollLeft = scrolled = this.scrollParent[0].scrollLeft - o.scrollSpeed; |
| 125 | } |
| 126 | |
| 127 | } else { |
| 128 | |
| 129 | if(event.pageY - $(document).scrollTop() < o.scrollSensitivity) { |
| 130 | scrolled = $(document).scrollTop($(document).scrollTop() - o.scrollSpeed); |
| 131 | } else if($(window).height() - (event.pageY - $(document).scrollTop()) < o.scrollSensitivity) { |
| 132 | scrolled = $(document).scrollTop($(document).scrollTop() + o.scrollSpeed); |
| 133 | } |
| 134 | |
| 135 | if(event.pageX - $(document).scrollLeft() < o.scrollSensitivity) { |
| 136 | scrolled = $(document).scrollLeft($(document).scrollLeft() - o.scrollSpeed); |
| 137 | } else if($(window).width() - (event.pageX - $(document).scrollLeft()) < o.scrollSensitivity) { |
| 138 | scrolled = $(document).scrollLeft($(document).scrollLeft() + o.scrollSpeed); |
| 139 | } |
| 140 | |
| 141 | } |
| 142 | |
| 143 | if(scrolled !== false && $.ui.ddmanager && !o.dropBehaviour) |
| 144 | $.ui.ddmanager.prepareOffsets(this, event); |
| 145 | } |
| 146 | |
| 147 | //Regenerate the absolute position used for position checks |
| 148 | this.positionAbs = this._convertPositionTo("absolute"); |
| 149 | |
| 150 | // mjs - find the top offset before rearrangement, |
| 151 | var previousTopOffset = this.placeholder.offset().top; |
| 152 | |
| 153 | //Set the helper position |
| 154 | if(!this.options.axis || this.options.axis !== "y") { |
| 155 | this.helper[0].style.left = this.position.left+"px"; |
| 156 | } |
| 157 | if(!this.options.axis || this.options.axis !== "x") { |
| 158 | this.helper[0].style.top = this.position.top+"px"; |
| 159 | } |
| 160 | |
| 161 | // mjs - check and reset hovering state at each cycle |
| 162 | this.hovering = this.hovering ? this.hovering : null; |
| 163 | this.mouseentered = this.mouseentered ? this.mouseentered : false; |
| 164 | |
| 165 | // mjs - let's start caching some variables |
| 166 | var parentItem = (this.placeholder[0].parentNode.parentNode && |
| 167 | $(this.placeholder[0].parentNode.parentNode).closest('.ui-sortable').length) |
| 168 | ? $(this.placeholder[0].parentNode.parentNode) |
| 169 | : null, |
| 170 | level = this._getLevel(this.placeholder), |
| 171 | childLevels = this._getChildLevels(this.helper); |
| 172 | |
| 173 | var newList = document.createElement(o.listType); |
| 174 | |
| 175 | //Rearrange |
| 176 | for (i = this.items.length - 1; i >= 0; i--) { |
| 177 | |
| 178 | //Cache variables and intersection, continue if no intersection |
| 179 | item = this.items[i]; |
| 180 | itemElement = item.item[0]; |
| 181 | intersection = this._intersectsWithPointer(item); |
| 182 | if (!intersection) { |
| 183 | continue; |
| 184 | } |
| 185 | |
| 186 | // Only put the placeholder inside the current Container, skip all |
| 187 | // items form other containers. This works because when moving |
| 188 | // an item from one container to another the |
| 189 | // currentContainer is switched before the placeholder is moved. |
| 190 | // |
| 191 | // Without this moving items in "sub-sortables" can cause the placeholder to jitter |
| 192 | // beetween the outer and inner container. |
| 193 | if (item.instance !== this.currentContainer) { |
| 194 | continue; |
| 195 | } |
| 196 | |
| 197 | // No action if intersected item is disabled |
| 198 | // and the element above or below in the direction we're going is also disabled |
| 199 | if (itemElement.className.indexOf(o.disabledClass) !== -1) { |
| 200 | // Note: intersection hardcoded direction values from jquery.ui.sortable.js:_intersectsWithPointer |
| 201 | if (intersection === 2) { |
| 202 | // Going down |
| 203 | var itemAfter = this.items[i + 1]; |
| 204 | if (itemAfter && itemAfter.item[0].className.indexOf(o.disabledClass) !== -1){ |
| 205 | continue; |
| 206 | } |
| 207 | |
| 208 | } |
| 209 | else if (intersection === 1) { |
| 210 | // Going up |
| 211 | var itemBefore = this.items[i - 1]; |
| 212 | if (itemBefore && itemBefore.item[0].className.indexOf(o.disabledClass) !== -1){ |
| 213 | continue; |
| 214 | } |
| 215 | } |
| 216 | } |
| 217 | |
| 218 | // cannot intersect with itself |
| 219 | // no useless actions that have been done before |
| 220 | // no action if the item moved is the parent of the item checked |
| 221 | if (itemElement !== this.currentItem[0] && |
| 222 | this.placeholder[intersection === 1 ? "next" : "prev"]()[0] !== itemElement && |
| 223 | !$.contains(this.placeholder[0], itemElement) && |
| 224 | (this.options.type === "semi-dynamic" ? !$.contains(this.element[0], itemElement) : true) |
| 225 | ) { |
| 226 | |
| 227 | // mjs - we are intersecting an element: trigger the mouseenter event and store this state |
| 228 | if (!this.mouseentered) { |
| 229 | $(itemElement).mouseenter(); |
| 230 | this.mouseentered = true; |
| 231 | } |
| 232 | |
| 233 | // mjs - if the element has children and they are hidden, show them after a delay (CSS responsible) |
| 234 | if (o.isTree && $(itemElement).hasClass(o.collapsedClass) && o.expandOnHover) { |
| 235 | if (!this.hovering) { |
| 236 | $(itemElement).addClass(o.hoveringClass); |
| 237 | var self = this; |
| 238 | this.hovering = window.setTimeout(function() { |
| 239 | $(itemElement).removeClass(o.collapsedClass).addClass(o.expandedClass); |
| 240 | self.refreshPositions(); |
| 241 | self._trigger("expand", event, self._uiHash()); |
| 242 | }, o.expandOnHover); |
| 243 | } |
| 244 | } |
| 245 | |
| 246 | this.direction = intersection == 1 ? "down" : "up"; |
| 247 | |
| 248 | // mjs - rearrange the elements and reset timeouts and hovering state |
| 249 | if (this.options.tolerance == "pointer" || this._intersectsWithSides(item)) { |
| 250 | $(itemElement).mouseleave(); |
| 251 | this.mouseentered = false; |
| 252 | $(itemElement).removeClass(o.hoveringClass); |
| 253 | this.hovering && window.clearTimeout(this.hovering); |
| 254 | this.hovering = null; |
| 255 | |
| 256 | // mjs - do not switch container if it's a root item and 'protectRoot' is true |
| 257 | // or if it's not a root item but we are trying to make it root |
| 258 | if (o.protectRoot |
| 259 | && ! (this.currentItem[0].parentNode == this.element[0] // it's a root item |
| 260 | && itemElement.parentNode != this.element[0]) // it's intersecting a non-root item |
| 261 | ) { |
| 262 | if (this.currentItem[0].parentNode != this.element[0] |
| 263 | && itemElement.parentNode == this.element[0] |
| 264 | ) { |
| 265 | |
| 266 | if ( ! $(itemElement).children(o.listType).length) { |
| 267 | itemElement.appendChild(newList); |
| 268 | o.isTree && $(itemElement).removeClass(o.leafClass).addClass(o.branchClass + ' ' + o.expandedClass); |
| 269 | } |
| 270 | |
| 271 | var a = this.direction === "down" ? $(itemElement).prev().children(o.listType) : $(itemElement).children(o.listType); |
| 272 | if (a[0] !== undefined) { |
| 273 | this._rearrange(event, null, a); |
| 274 | } |
| 275 | |
| 276 | } else { |
| 277 | this._rearrange(event, item); |
| 278 | } |
| 279 | } else if ( ! o.protectRoot) { |
| 280 | this._rearrange(event, item); |
| 281 | } |
| 282 | } else { |
| 283 | break; |
| 284 | } |
| 285 | |
| 286 | // Clear emtpy ul's/ol's |
| 287 | this._clearEmpty(itemElement); |
| 288 | |
| 289 | this._trigger("change", event, this._uiHash()); |
| 290 | break; |
| 291 | } |
| 292 | } |
| 293 | |
| 294 | // mjs - to find the previous sibling in the list, keep backtracking until we hit a valid list item. |
| 295 | var previousItem = this.placeholder[0].previousSibling ? $(this.placeholder[0].previousSibling) : null; |
| 296 | if (previousItem != null) { |
| 297 | while (previousItem[0].nodeName.toLowerCase() != 'li' || previousItem[0].className.indexOf(o.disabledClass) !== -1 || previousItem[0] == this.currentItem[0] || previousItem[0] == this.helper[0]) { |
| 298 | if (previousItem[0].previousSibling) { |
| 299 | previousItem = $(previousItem[0].previousSibling); |
| 300 | } else { |
| 301 | previousItem = null; |
| 302 | break; |
| 303 | } |
| 304 | } |
| 305 | } |
| 306 | |
| 307 | // mjs - to find the next sibling in the list, keep stepping forward until we hit a valid list item. |
| 308 | var nextItem = this.placeholder[0].nextSibling ? $(this.placeholder[0].nextSibling) : null; |
| 309 | if (nextItem != null) { |
| 310 | while (nextItem[0].nodeName.toLowerCase() != 'li' || nextItem[0].className.indexOf(o.disabledClass) !== -1 || nextItem[0] == this.currentItem[0] || nextItem[0] == this.helper[0]) { |
| 311 | if (nextItem[0].nextSibling) { |
| 312 | nextItem = $(nextItem[0].nextSibling); |
| 313 | } else { |
| 314 | nextItem = null; |
| 315 | break; |
| 316 | } |
| 317 | } |
| 318 | } |
| 319 | |
| 320 | this.beyondMaxLevels = 0; |
| 321 | |
| 322 | // 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 |
| 323 | if (parentItem != null |
| 324 | && nextItem == null |
| 325 | && ! (o.protectRoot && parentItem[0].parentNode == this.element[0]) |
| 326 | && |
| 327 | (o.rtl && (this.positionAbs.left + this.helper.outerWidth() > parentItem.offset().left + parentItem.outerWidth()) |
| 328 | || ! o.rtl && (this.positionAbs.left < parentItem.offset().left)) |
| 329 | ) { |
| 330 | |
| 331 | parentItem.after(this.placeholder[0]); |
| 332 | if (o.isTree && parentItem.children(o.listItem).children('li:visible:not(.ui-sortable-helper)').length < 1) { |
| 333 | parentItem.removeClass(this.options.branchClass + ' ' + this.options.expandedClass) |
| 334 | .addClass(this.options.leafClass); |
| 335 | } |
| 336 | this._clearEmpty(parentItem[0]); |
| 337 | this._trigger("change", event, this._uiHash()); |
| 338 | } |
| 339 | // mjs - if the item is below a sibling and is moved to the right, make it a child of that sibling |
| 340 | else if (previousItem != null |
| 341 | && ! previousItem.hasClass(o.disableNestingClass) |
| 342 | && |
| 343 | (previousItem.children(o.listType).length && previousItem.children(o.listType).is(':visible') |
| 344 | || ! previousItem.children(o.listType).length) |
| 345 | && ! (o.protectRoot && this.currentItem[0].parentNode == this.element[0]) |
| 346 | && |
| 347 | (o.rtl && (this.positionAbs.left + this.helper.outerWidth() < previousItem.offset().left + previousItem.outerWidth() - o.tabSize) |
| 348 | || ! o.rtl && (this.positionAbs.left > previousItem.offset().left + o.tabSize)) |
| 349 | ) { |
| 350 | |
| 351 | this._isAllowed(previousItem, level, level+childLevels+1); |
| 352 | |
| 353 | if (!previousItem.children(o.listType).length) { |
| 354 | previousItem[0].appendChild(newList); |
| 355 | o.isTree && previousItem.removeClass(o.leafClass).addClass(o.branchClass + ' ' + o.expandedClass); |
| 356 | } |
| 357 | |
| 358 | // mjs - if this item is being moved from the top, add it to the top of the list. |
| 359 | if (previousTopOffset && (previousTopOffset <= previousItem.offset().top)) { |
| 360 | previousItem.children(o.listType).prepend(this.placeholder); |
| 361 | } |
| 362 | // mjs - otherwise, add it to the bottom of the list. |
| 363 | else { |
| 364 | previousItem.children(o.listType)[0].appendChild(this.placeholder[0]); |
| 365 | } |
| 366 | |
| 367 | this._trigger("change", event, this._uiHash()); |
| 368 | } |
| 369 | else { |
| 370 | this._isAllowed(parentItem, level, level+childLevels); |
| 371 | } |
| 372 | |
| 373 | //Post events to containers |
| 374 | this._contactContainers(event); |
| 375 | |
| 376 | //Interconnect with droppables |
| 377 | if($.ui.ddmanager) { |
| 378 | $.ui.ddmanager.drag(this, event); |
| 379 | } |
| 380 | |
| 381 | //Call callbacks |
| 382 | this._trigger('sort', event, this._uiHash()); |
| 383 | |
| 384 | this.lastPositionAbs = this.positionAbs; |
| 385 | return false; |
| 386 | |
| 387 | }, |
| 388 | |
| 389 | _mouseStop: function(event, noPropagation) { |
| 390 | |
| 391 | // mjs - if the item is in a position not allowed, send it back |
| 392 | if (this.beyondMaxLevels) { |
| 393 | |
| 394 | this.placeholder.removeClass(this.options.errorClass); |
| 395 | |
| 396 | if (this.domPosition.prev) { |
| 397 | $(this.domPosition.prev).after(this.placeholder); |
| 398 | } else { |
| 399 | $(this.domPosition.parent).prepend(this.placeholder); |
| 400 | } |
| 401 | |
| 402 | this._trigger("revert", event, this._uiHash()); |
| 403 | |
| 404 | } |
| 405 | |
| 406 | |
| 407 | // mjs - clear the hovering timeout, just to be sure |
| 408 | $('.'+this.options.hoveringClass).mouseleave().removeClass(this.options.hoveringClass); |
| 409 | this.mouseentered = false; |
| 410 | this.hovering && window.clearTimeout(this.hovering); |
| 411 | this.hovering = null; |
| 412 | |
| 413 | $.ui.sortable.prototype._mouseStop.apply(this, arguments); |
| 414 | |
| 415 | var pid = $(this.domPosition.parent).parent().attr("id"); |
| 416 | var sort = this.domPosition.prev ? $(this.domPosition.prev).next().index() : 0; |
| 417 | |
| 418 | if(!(pid == this._uiHash().item.parent().parent().attr("id") && |
| 419 | sort == this._uiHash().item.index())) { |
| 420 | this._trigger("relocate", event, this._uiHash()); |
| 421 | } |
| 422 | |
| 423 | }, |
| 424 | |
| 425 | // mjs - this function is slightly modified to make it easier to hover over a collapsed element and have it expand |
| 426 | _intersectsWithSides: function(item) { |
| 427 | |
| 428 | var half = this.options.isTree ? .8 : .5; |
| 429 | |
| 430 | var isOverBottomHalf = isOverAxis(this.positionAbs.top + this.offset.click.top, item.top + (item.height*half), item.height), |
| 431 | isOverTopHalf = isOverAxis(this.positionAbs.top + this.offset.click.top, item.top - (item.height*half), item.height), |
| 432 | isOverRightHalf = isOverAxis(this.positionAbs.left + this.offset.click.left, item.left + (item.width/2), item.width), |
| 433 | verticalDirection = this._getDragVerticalDirection(), |
| 434 | horizontalDirection = this._getDragHorizontalDirection(); |
| 435 | |
| 436 | if (this.floating && horizontalDirection) { |
| 437 | return ((horizontalDirection == "right" && isOverRightHalf) || (horizontalDirection == "left" && !isOverRightHalf)); |
| 438 | } else { |
| 439 | return verticalDirection && ((verticalDirection == "down" && isOverBottomHalf) || (verticalDirection == "up" && isOverTopHalf)); |
| 440 | } |
| 441 | |
| 442 | }, |
| 443 | |
| 444 | _contactContainers: function(event) { |
| 445 | |
| 446 | if (this.options.protectRoot && this.currentItem[0].parentNode == this.element[0] ) { |
| 447 | return; |
| 448 | } |
| 449 | |
| 450 | $.ui.sortable.prototype._contactContainers.apply(this, arguments); |
| 451 | |
| 452 | }, |
| 453 | |
| 454 | _clear: function(event, noPropagation) { |
| 455 | |
| 456 | $.ui.sortable.prototype._clear.apply(this, arguments); |
| 457 | |
| 458 | // mjs - clean last empty ul/ol |
| 459 | for (var i = this.items.length - 1; i >= 0; i--) { |
| 460 | var item = this.items[i].item[0]; |
| 461 | this._clearEmpty(item); |
| 462 | } |
| 463 | |
| 464 | }, |
| 465 | |
| 466 | serialize: function(options) { |
| 467 | |
| 468 | var o = $.extend({}, this.options, options), |
| 469 | items = this._getItemsAsjQuery(o && o.connected), |
| 470 | str = []; |
| 471 | |
| 472 | $(items).each(function() { |
| 473 | var res = ($(o.item || this).attr(o.attribute || 'id') || '') |
| 474 | .match(o.expression || (/(.+)[-=_](.+)/)), |
| 475 | pid = ($(o.item || this).parent(o.listType) |
| 476 | .parent(o.items) |
| 477 | .attr(o.attribute || 'id') || '') |
| 478 | .match(o.expression || (/(.+)[-=_](.+)/)); |
| 479 | |
| 480 | if (res) { |
| 481 | str.push(((o.key || res[1]) + '[' + (o.key && o.expression ? res[1] : res[2]) + ']') |
| 482 | + '=' |
| 483 | + (pid ? (o.key && o.expression ? pid[1] : pid[2]) : o.rootID)); |
| 484 | } |
| 485 | }); |
| 486 | |
| 487 | if(!str.length && o.key) { |
| 488 | str.push(o.key + '='); |
| 489 | } |
| 490 | |
| 491 | return str.join('&'); |
| 492 | |
| 493 | }, |
| 494 | |
| 495 | toHierarchy: function(options) { |
| 496 | |
| 497 | var o = $.extend({}, this.options, options), |
| 498 | sDepth = o.startDepthCount || 0, |
| 499 | ret = []; |
| 500 | |
| 501 | $(this.element).children(o.items).each(function () { |
| 502 | var level = _recursiveItems(this); |
| 503 | ret.push(level); |
| 504 | }); |
| 505 | |
| 506 | return ret; |
| 507 | |
| 508 | function _recursiveItems(item) { |
| 509 | var id = ($(item).attr(o.attribute || 'id') || '').match(o.expression || (/(.+)[-=_](.+)/)); |
| 510 | if (id) { |
| 511 | var currentItem = {"id" : id[2]}; |
| 512 | if ($(item).children(o.listType).children(o.items).length > 0) { |
| 513 | currentItem.children = []; |
| 514 | $(item).children(o.listType).children(o.items).each(function() { |
| 515 | var level = _recursiveItems(this); |
| 516 | currentItem.children.push(level); |
| 517 | }); |
| 518 | } |
| 519 | return currentItem; |
| 520 | } |
| 521 | } |
| 522 | }, |
| 523 | |
| 524 | toArray: function(options) { |
| 525 | |
| 526 | var o = $.extend({}, this.options, options), |
| 527 | sDepth = o.startDepthCount || 0, |
| 528 | ret = [], |
| 529 | left = 1; |
| 530 | |
| 531 | if (!o.excludeRoot) { |
| 532 | ret.push({ |
| 533 | "item_id": o.rootID, |
| 534 | "parent_id": null, |
| 535 | "depth": sDepth, |
| 536 | "left": left, |
| 537 | "right": ($(o.items, this.element).length + 1) * 2 |
| 538 | }); |
| 539 | left++ |
| 540 | } |
| 541 | |
| 542 | $(this.element).children(o.items).each(function () { |
| 543 | left = _recursiveArray(this, sDepth + 1, left); |
| 544 | }); |
| 545 | |
| 546 | ret = ret.sort(function(a,b){ return (a.left - b.left); }); |
| 547 | |
| 548 | return ret; |
| 549 | |
| 550 | function _recursiveArray(item, depth, left) { |
| 551 | |
| 552 | var right = left + 1, |
| 553 | id, |
| 554 | pid; |
| 555 | |
| 556 | if ($(item).children(o.listType).children(o.items).length > 0) { |
| 557 | depth ++; |
| 558 | $(item).children(o.listType).children(o.items).each(function () { |
| 559 | right = _recursiveArray($(this), depth, right); |
| 560 | }); |
| 561 | depth --; |
| 562 | } |
| 563 | |
| 564 | id = ($(item).attr(o.attribute || 'id')).match(o.expression || (/(.+)[-=_](.+)/)); |
| 565 | |
| 566 | if (depth === sDepth + 1) { |
| 567 | pid = o.rootID; |
| 568 | } else { |
| 569 | var parentItem = ($(item).parent(o.listType) |
| 570 | .parent(o.items) |
| 571 | .attr(o.attribute || 'id')) |
| 572 | .match(o.expression || (/(.+)[-=_](.+)/)); |
| 573 | pid = parentItem[2]; |
| 574 | } |
| 575 | |
| 576 | if (id) { |
| 577 | ret.push({"item_id": id[2], "parent_id": pid, "depth": depth, "left": left, "right": right}); |
| 578 | } |
| 579 | |
| 580 | left = right + 1; |
| 581 | return left; |
| 582 | } |
| 583 | |
| 584 | }, |
| 585 | |
| 586 | _clearEmpty: function(item) { |
| 587 | var o = this.options; |
| 588 | |
| 589 | var emptyList = $(item).children(o.listType); |
| 590 | |
| 591 | if (emptyList.length && !emptyList.children().length && !o.doNotClear) { |
| 592 | o.isTree && $(item).removeClass(o.branchClass + ' ' + o.expandedClass).addClass(o.leafClass); |
| 593 | emptyList.remove(); |
| 594 | } else if (o.isTree && emptyList.length && emptyList.children().length && emptyList.is(':visible')) { |
| 595 | $(item).removeClass(o.leafClass).addClass(o.branchClass + ' ' + o.expandedClass); |
| 596 | } else if (o.isTree && emptyList.length && emptyList.children().length && !emptyList.is(':visible')) { |
| 597 | $(item).removeClass(o.leafClass).addClass(o.branchClass + ' ' + o.collapsedClass); |
| 598 | } |
| 599 | |
| 600 | }, |
| 601 | |
| 602 | _getLevel: function(item) { |
| 603 | |
| 604 | var level = 1; |
| 605 | |
| 606 | if (this.options.listType) { |
| 607 | var list = item.closest(this.options.listType); |
| 608 | while (list && list.length > 0 && |
| 609 | !list.is('.ui-sortable')) { |
| 610 | level++; |
| 611 | list = list.parent().closest(this.options.listType); |
| 612 | } |
| 613 | } |
| 614 | |
| 615 | return level; |
| 616 | }, |
| 617 | |
| 618 | _getChildLevels: function(parent, depth) { |
| 619 | var self = this, |
| 620 | o = this.options, |
| 621 | result = 0; |
| 622 | depth = depth || 0; |
| 623 | |
| 624 | $(parent).children(o.listType).children(o.items).each(function (index, child) { |
| 625 | result = Math.max(self._getChildLevels(child, depth + 1), result); |
| 626 | }); |
| 627 | |
| 628 | return depth ? result + 1 : result; |
| 629 | }, |
| 630 | |
| 631 | _isAllowed: function(parentItem, level, levels) { |
| 632 | var o = this.options, |
| 633 | maxLevels = this.placeholder.closest('.ui-sortable').nestedSortable('option', 'maxLevels'); // this takes into account the maxLevels set to the recipient list |
| 634 | |
| 635 | // Check if the parent has changed to prevent it, when o.disableParentChange is true |
| 636 | var oldParent = this.currentItem.parent().parent(); |
| 637 | var disabledByParentchange = o.disableParentChange && ( |
| 638 | parentItem !== null && !oldParent.is(parentItem)//From somewhere to somewhere else, except the root |
| 639 | || parentItem === null && oldParent.is('li') //From somewhere to the root |
| 640 | ); |
| 641 | // mjs - is the root protected? |
| 642 | // mjs - are we nesting too deep? |
| 643 | if (disabledByParentchange || ! o.isAllowed(this.placeholder, parentItem, this.currentItem)) { |
| 644 | this.placeholder.addClass(o.errorClass); |
| 645 | if (maxLevels < levels && maxLevels != 0) { |
| 646 | this.beyondMaxLevels = levels - maxLevels; |
| 647 | } else { |
| 648 | this.beyondMaxLevels = 1; |
| 649 | } |
| 650 | } else { |
| 651 | if (maxLevels < levels && maxLevels != 0) { |
| 652 | this.placeholder.addClass(o.errorClass); |
| 653 | this.beyondMaxLevels = levels - maxLevels; |
| 654 | } else { |
| 655 | this.placeholder.removeClass(o.errorClass); |
| 656 | this.beyondMaxLevels = 0; |
| 657 | } |
| 658 | } |
| 659 | } |
| 660 | |
| 661 | })); |
| 662 | |
| 663 | $.mjs.nestedSortable.prototype.options = $.extend({}, $.ui.sortable.prototype.options, $.mjs.nestedSortable.prototype.options); |
| 664 | })); |
| 665 |