Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP

Loading…

2.2/Develop - Updates js sort tree #1383

Merged
merged 1 commit into from

2 participants

@ryun

updated nested sortable from 1.2.2 to 1.4
added the ability to pass options to nestedSortable with pyro.sort_tree
optimized pyro.sort_tree selectors and refresh_tree, no more setTimeout needed

@ryun ryun added the ability to pass options to nestedSortable with pyro.sort_tree
optimized pyro.sort_tree selectors and refresh_tree function, no more setTimeout needed
ae369fc
@philsturgeon philsturgeon merged commit bbbd14b into pyrocms:2.2/develop
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Commits on Apr 30, 2012
  1. @ryun

    added the ability to pass options to nestedSortable with pyro.sort_tree

    ryun authored
    optimized pyro.sort_tree selectors and refresh_tree function, no more setTimeout needed
This page is out of date. Refresh to see the latest.
View
353 system/cms/themes/pyrocms/js/jquery/jquery.ui.nestedSortable.js
@@ -1,34 +1,48 @@
/*
* jQuery UI Nested Sortable
- * v 1.2.2 / 19 feb 2011
- * http://mjsarfatti.com/sandbox/nestedSortable
+ * v 1.4 / 30 dec 2011
+ * http://mjsarfatti.com/code/nestedSortable
*
- * Depends:
+ * Depends on:
* jquery.ui.sortable.js 1.8+
*
- * License CC BY-SA 3.0
- * Copyright 2010-2011, Manuele J Sarfatti
+ * Copyright (c) 2010-2012 Manuele J Sarfatti
+ * Licensed under the MIT License
+ * http://www.opensource.org/licenses/mit-license.php
*/
(function($) {
- $.widget("ui.nestedSortable", $.extend({}, $.ui.sortable.prototype, {
+ $.widget("mjs.nestedSortable", $.extend({}, $.ui.sortable.prototype, {
options: {
tabSize: 20,
- disableNesting: 'ui-nestedSortable-no-nesting',
- errorClass: 'ui-nestedSortable-error',
+ disableNesting: 'mjs-nestedSortable-no-nesting',
+ errorClass: 'mjs-nestedSortable-error',
listType: 'ol',
- noJumpFix: '0'
+ maxLevels: 0,
+ protectRoot: false,
+ rootID: null,
+ rtl: false,
+ isAllowed: function(item, parent) { return true; }
},
- _create: function(){
- if (this.noJumpFix == false)
- this.element.height(this.element.height());
+ _create: function() {
this.element.data('sortable', this.element.data('nestedSortable'));
+
+ if (!this.element.is(this.options.listType))
+ throw new Error('nestedSortable: Please check the listType option is set to your actual list type');
+
return $.ui.sortable.prototype._create.apply(this, arguments);
},
+ destroy: function() {
+ this.element
+ .removeData("nestedSortable")
+ .unbind(".nestedSortable");
+ return $.ui.sortable.prototype.destroy.apply(this, arguments);
+ },
+
_mouseDrag: function(event) {
//Compute the helpers position
@@ -88,14 +102,17 @@
if(itemElement != this.currentItem[0] //cannot intersect with itself
&& this.placeholder[intersection == 1 ? "next" : "prev"]()[0] != itemElement //no useless actions that have been done before
- && !$.ui.contains(this.placeholder[0], itemElement) //no action if the item moved is the parent of the item checked
- && (this.options.type == 'semi-dynamic' ? !$.ui.contains(this.element[0], itemElement) : true)
+ && !$.contains(this.placeholder[0], itemElement) //no action if the item moved is the parent of the item checked
+ && (this.options.type == 'semi-dynamic' ? !$.contains(this.element[0], itemElement) : true)
//&& itemElement.parentNode == this.placeholder[0].parentNode // only rearrange items within the same container
) {
+ $(itemElement).mouseenter();
+
this.direction = intersection == 1 ? "down" : "up";
if (this.options.tolerance == "pointer" || this._intersectsWithSides(item)) {
+ $(itemElement).mouseleave();
this._rearrange(event, item);
} else {
break;
@@ -109,44 +126,50 @@
}
}
- // Get the real previous item
- itemBefore = this.placeholder[0].previousSibling;
- while (itemBefore != null) {
- if (itemBefore.nodeType == 1 && itemBefore != this.currentItem[0]) {
- break;
- } else {
- itemBefore = itemBefore.previousSibling;
+ var parentItem = (this.placeholder[0].parentNode.parentNode &&
+ $(this.placeholder[0].parentNode.parentNode).closest('.ui-sortable').length)
+ ? $(this.placeholder[0].parentNode.parentNode)
+ : null,
+ level = this._getLevel(this.placeholder),
+ childLevels = this._getChildLevels(this.helper),
+ previousItem = this.placeholder[0].previousSibling ? $(this.placeholder[0].previousSibling) : null;
+
+ if (previousItem != null) {
+ while (previousItem[0].nodeName.toLowerCase() != 'li' || previousItem[0] == this.currentItem[0]) {
+ if (previousItem[0].previousSibling) {
+ previousItem = $(previousItem[0].previousSibling);
+ } else {
+ previousItem = null;
+ break;
+ }
}
}
- parentItem = this.placeholder[0].parentNode.parentNode;
- newList = document.createElement(o.listType);
-
- // Make/delete nested ul's/ol's
- if (parentItem != null && parentItem.nodeName == 'LI' && $(parentItem).closest('.ui-sortable').length && this.positionAbs.left < $(parentItem).offset().left) {
- $(parentItem).after(this.placeholder[0]);
- this._clearEmpty(parentItem);
- } else if (itemBefore != null && itemBefore.nodeName == 'LI' && this.positionAbs.left > $(itemBefore).offset().left + this.options.tabSize) {
- if (!($(itemBefore).hasClass(this.options.disableNesting))) {
- if ($(this.placeholder[0]).hasClass(this.options.errorClass)) {
- $(this.placeholder[0]).css('marginLeft', 0).removeClass(this.options.errorClass);
- }
- if (itemBefore.children[1] == null) {
- itemBefore.appendChild(newList);
- }
- itemBefore.children[1].appendChild(this.placeholder[0]);
- } else {
- $(this.placeholder[0]).addClass(this.options.errorClass).css('marginLeft', this.options.tabSize);
- }
- } else if (itemBefore != null) {
- if ($(this.placeholder[0]).hasClass(this.options.errorClass)) {
- $(this.placeholder[0]).css('marginLeft', 0).removeClass(this.options.errorClass);
- }
- $(itemBefore).after(this.placeholder[0]);
- } else {
- if ($(this.placeholder[0]).hasClass(this.options.errorClass)) {
- $(this.placeholder[0]).css('marginLeft', 0).removeClass(this.options.errorClass);
+ var newList = document.createElement(o.listType);
+
+ this.beyondMaxLevels = 0;
+
+ // If the item is moved to the left, send it to its parent level
+ if (parentItem != null &&
+ (o.rtl && (this.positionAbs.left + this.helper.outerWidth() > parentItem.offset().left + parentItem.outerWidth()) ||
+ !o.rtl && (this.positionAbs.left < parentItem.offset().left))) {
+ parentItem.after(this.placeholder[0]);
+ this._clearEmpty(parentItem[0]);
+ this._trigger("change", event, this._uiHash());
+ }
+ // If the item is below another one and is moved to the right, make it a children of it
+ else if (previousItem != null &&
+ (o.rtl && (this.positionAbs.left + this.helper.outerWidth() < previousItem.offset().left + previousItem.outerWidth() - o.tabSize) ||
+ !o.rtl && (this.positionAbs.left > previousItem.offset().left + o.tabSize))) {
+ this._isAllowed(previousItem, level, level+childLevels+1);
+ if (!previousItem.children(o.listType).length) {
+ previousItem[0].appendChild(newList);
}
+ previousItem.children(o.listType)[0].appendChild(this.placeholder[0]);
+ this._trigger("change", event, this._uiHash());
+ }
+ else {
+ this._isAllowed(parentItem, level, level+childLevels);
}
//Post events to containers
@@ -163,15 +186,52 @@
},
- serialize: function(o) {
+ _mouseStop: function(event, noPropagation) {
+
+ // If the item is in a position not allowed, send it back
+ if (this.beyondMaxLevels) {
+
+ this.placeholder.removeClass(this.options.errorClass);
+
+ if (this.domPosition.prev) {
+ $(this.domPosition.prev).after(this.placeholder);
+ } else {
+ $(this.domPosition.parent).prepend(this.placeholder);
+ }
+
+ this._trigger("revert", event, this._uiHash());
+
+ }
+
+ // Clean last empty ul/ol
+ for (var i = this.items.length - 1; i >= 0; i--) {
+ var item = this.items[i].item[0];
+ this._clearEmpty(item);
+ }
+
+ $.ui.sortable.prototype._mouseStop.apply(this, arguments);
+
+ },
+
+ serialize: function(options) {
- var items = this._getItemsAsjQuery(o && o.connected);
- var str = []; o = o || {};
+ var o = $.extend({}, this.options, options),
+ items = this._getItemsAsjQuery(o && o.connected),
+ str = [];
$(items).each(function() {
- var res = ($(o.item || this).attr(o.attribute || 'id') || '').match(o.expression || (/(.+)[-=_](.+)/));
- var pid = ($(o.item || this).parent(o.listType).parent('li').attr(o.attribute || 'id') || '').match(o.expression || (/(.+)[-=_](.+)/));
- if(res) str.push((o.key || res[1]+'['+(o.key && o.expression ? res[1] : res[2])+']')+'='+(pid ? (o.key && o.expression ? pid[1] : pid[2]) : 'root'));
+ var res = ($(o.item || this).attr(o.attribute || 'id') || '')
+ .match(o.expression || (/(.+)[-=_](.+)/)),
+ pid = ($(o.item || this).parent(o.listType)
+ .parent(o.items)
+ .attr(o.attribute || 'id') || '')
+ .match(o.expression || (/(.+)[-=_](.+)/));
+
+ if (res) {
+ str.push(((o.key || res[1]) + '[' + (o.key && o.expression ? res[1] : res[2]) + ']')
+ + '='
+ + (pid ? (o.key && o.expression ? pid[1] : pid[2]) : o.rootID));
+ }
});
if(!str.length && o.key) {
@@ -182,140 +242,159 @@
},
- toHierarchy: function(o) {
+ toHierarchy: function(options) {
- o = o || {};
- var sDepth = o.startDepthCount || 0;
- var ret = [];
+ var o = $.extend({}, this.options, options),
+ sDepth = o.startDepthCount || 0,
+ ret = [];
- $(this.element).children('li').each(function() {
- var level = _recursiveItems($(this));
+ $(this.element).children(o.items).each(function () {
+ var level = _recursiveItems(this);
ret.push(level);
});
return ret;
- function _recursiveItems(li) {
- var id = $(li).attr("id");
- var item = {"id" : id};
- if ($(li).children(o.listType).children('li').length > 0) {
- item.children = [];
- $(li).children(o.listType).children('li').each(function() {
- var level = _recursiveItems($(this));
- item.children.push(level);
- });
+ function _recursiveItems(item) {
+ var id = ($(item).attr(o.attribute || 'id') || '').match(o.expression || (/(.+)[-=_](.+)/));
+ if (id) {
+ var currentItem = {"id" : id[2]};
+ if ($(item).children(o.listType).children(o.items).length > 0) {
+ currentItem.children = [];
+ $(item).children(o.listType).children(o.items).each(function() {
+ var level = _recursiveItems(this);
+ currentItem.children.push(level);
+ });
+ }
+ return currentItem;
}
- return item;
}
- },
+ },
- toArray: function(o) {
+ toArray: function(options) {
- o = o || {};
- var sDepth = o.startDepthCount || 0;
- var ret = [];
- var left = 2;
+ var o = $.extend({}, this.options, options),
+ sDepth = o.startDepthCount || 0,
+ ret = [],
+ left = 2;
- ret.push({"item_id": 'root', "parent_id": 'none', "depth": sDepth, "left": '1', "right": ($('li', this.element).length + 1) * 2});
+ ret.push({
+ "item_id": o.rootID,
+ "parent_id": 'none',
+ "depth": sDepth,
+ "left": '1',
+ "right": ($(o.items, this.element).length + 1) * 2
+ });
- $(this.element).children('li').each(function() {
- left = _recursiveArray($(this), sDepth + 1, left);
+ $(this.element).children(o.items).each(function () {
+ left = _recursiveArray(this, sDepth + 1, left);
});
+ ret = ret.sort(function(a,b){ return (a.left - b.left); });
+
return ret;
function _recursiveArray(item, depth, left) {
- right = left + 1;
+ var right = left + 1,
+ id,
+ pid;
- if ($(item).children(o.listType).children('li').length > 0) {
+ if ($(item).children(o.listType).children(o.items).length > 0) {
depth ++;
- $(item).children(o.listType).children('li').each(function() {
+ $(item).children(o.listType).children(o.items).each(function () {
right = _recursiveArray($(this), depth, right);
});
depth --;
}
- id = $(item).attr('id').match(o.expression || (/(.+)[-=_](.+)/));
+ id = ($(item).attr(o.attribute || 'id')).match(o.expression || (/(.+)[-=_](.+)/));
- if (depth === sDepth + 1) pid = 'root';
- else {
- parentItem = $(item).parent(o.listType).parent('li').attr('id').match(o.expression || (/(.+)[-=_](.+)/));
+ if (depth === sDepth + 1) {
+ pid = o.rootID;
+ } else {
+ var parentItem = ($(item).parent(o.listType)
+ .parent(o.items)
+ .attr(o.attribute || 'id'))
+ .match(o.expression || (/(.+)[-=_](.+)/));
pid = parentItem[2];
}
- ret.push({"item_id": id[2], "parent_id": pid, "depth": depth, "left": left, "right": right});
+ if (id) {
+ ret.push({"item_id": id[2], "parent_id": pid, "depth": depth, "left": left, "right": right});
+ }
- return left = right + 1;
+ left = right + 1;
+ return left;
}
},
- _createPlaceholder: function(that) {
-
- var self = that || this, o = self.options;
-
- if(!o.placeholder || o.placeholder.constructor == String) {
- var className = o.placeholder;
- o.placeholder = {
- element: function() {
+ _clearEmpty: function(item) {
- var el = $(document.createElement(self.currentItem[0].nodeName))
- .addClass(className || self.currentItem[0].className+" ui-sortable-placeholder")
- .removeClass("ui-sortable-helper")[0];
+ var emptyList = $(item).children(this.options.listType);
+ if (emptyList.length && !emptyList.children('li').length) {
+ emptyList.remove();
+ }
- if(!className)
- el.style.visibility = "hidden";
+ },
- return el;
- },
- update: function(container, p) {
+ _getLevel: function(item) {
- // 1. If a className is set as 'placeholder option, we don't force sizes - the class is responsible for that
- // 2. The option 'forcePlaceholderSize can be enabled to force it even if a class name is specified
- if(className && !o.forcePlaceholderSize) return;
+ var level = 1;
- //If the element doesn't have an actual height by itself (without styles coming from a stylesheet), it receives the inline height from the dragged item
- if(!p.height() || p.css('height') == 'auto') { p.height(self.currentItem.height()); }
- if(!p.width()) { p.width(self.currentItem.width()); }
- }
- };
+ if (this.options.listType) {
+ var list = item.closest(this.options.listType);
+ while (!list.is('.ui-sortable')) {
+ level++;
+ list = list.parent().closest(this.options.listType);
+ }
}
- //Create the placeholder
- self.placeholder = $(o.placeholder.element.call(self.element, self.currentItem));
-
- //Append it after the actual current item
- self.currentItem.after(self.placeholder);
-
- //Update the size of the placeholder (TODO: Logic to fuzzy, see line 316/317)
- o.placeholder.update(self, self.placeholder);
-
+ return level;
},
- _clear: function(event, noPropagation) {
-
- $.ui.sortable.prototype._clear.apply(this, arguments);
+ _getChildLevels: function(parent, depth) {
+ var self = this,
+ o = this.options,
+ result = 0;
+ depth = depth || 0;
- // Clean last empty ul/ol
- for (var i = this.items.length - 1; i >= 0; i--) {
- var item = this.items[i].item[0];
- this._clearEmpty(item);
- }
- return true;
+ $(parent).children(o.listType).children(o.items).each(function (index, child) {
+ result = Math.max(self._getChildLevels(child, depth + 1), result);
+ });
+ return depth ? result + 1 : result;
},
- _clearEmpty: function(item) {
-
- if (item.children[1] && item.children[1].children.length == 0) {
- item.removeChild(item.children[1]);
+ _isAllowed: function(parentItem, level, levels) {
+ var o = this.options,
+ isRoot = $(this.domPosition.parent).hasClass('ui-sortable') ? true : false;
+
+ // Is the root protected?
+ // Are we trying to nest under a no-nest?
+ // Are we nesting too deep?
+ if (!o.isAllowed(parentItem, this.placeholder) ||
+ parentItem && parentItem.hasClass(o.disableNesting) ||
+ o.protectRoot && (parentItem == null && !isRoot || isRoot && level > 1)) {
+ this.placeholder.addClass(o.errorClass);
+ if (o.maxLevels < levels && o.maxLevels != 0) {
+ this.beyondMaxLevels = levels - o.maxLevels;
+ } else {
+ this.beyondMaxLevels = 1;
+ }
+ } else {
+ if (o.maxLevels < levels && o.maxLevels != 0) {
+ this.placeholder.addClass(o.errorClass);
+ this.beyondMaxLevels = levels - o.maxLevels;
+ } else {
+ this.placeholder.removeClass(o.errorClass);
+ this.beyondMaxLevels = 0;
+ }
}
-
}
}));
- $.ui.nestedSortable.prototype.options = $.extend({}, $.ui.sortable.prototype.options, $.ui.nestedSortable.prototype.options);
-
+ $.mjs.nestedSortable.prototype.options = $.extend({}, $.ui.sortable.prototype.options, $.mjs.nestedSortable.prototype.options);
})(jQuery);
View
32 system/cms/themes/pyrocms/js/scripts.js
@@ -243,8 +243,11 @@ jQuery(function($) {
// Used by Pages and Navigation and is available for third-party add-ons.
// Module must load jquery/jquery.ui.nestedSortable.js and jquery/jquery.cooki.js
- pyro.sort_tree = function($item_list, $url, $cookie, data_callback, post_sort_callback)
+ pyro.sort_tree = function($item_list, $url, $cookie, data_callback, post_sort_callback, sortable_opts)
{
+ // set options or create a empty object to merge with defaults
+ sortable_opts = sortable_opts || {};
+
// collapse all ordered lists but the top level
$item_list.find('ul').children().hide();
@@ -252,13 +255,16 @@ jQuery(function($) {
var refresh_tree = function() {
// add the minus icon to all parent items that now have visible children
- $item_list.parent().find('ul li:has(li:visible)').removeClass().addClass('minus');
+ $item_list.find('li:has(li:visible)').removeClass().addClass('minus');
// add the plus icon to all parent items with hidden children
- $item_list.parent().find('ul li:has(li:hidden)').removeClass().addClass('plus');
-
+ $item_list.find('li:has(li:hidden)').removeClass().addClass('plus');
+
+ // Remove any empty ul elements
+ $('.plus, .minus').find('ul').not(':has(li)').remove();
+
// remove the class if the child was removed
- $item_list.parent().find('ul li:not(:has(ul))').removeClass();
+ $item_list.find("li:not(:has(ul li))").removeClass();
// call the post sort callback
post_sort_callback && post_sort_callback();
@@ -288,8 +294,9 @@ jQuery(function($) {
return false;
});
-
- $item_list.nestedSortable({
+
+ // Defaults for nestedSortable
+ var default_opts = {
delay: 100,
disableNesting: 'no-nest',
forcePlaceholderSize: true,
@@ -302,7 +309,7 @@ jQuery(function($) {
listType: 'ul',
tolerance: 'pointer',
toleranceElement: '> div',
- stop: function(event, ui) {
+ update: function(event, ui) {
post = {};
// create the array using the toHierarchy method
@@ -313,14 +320,15 @@ jQuery(function($) {
post.data = data_callback(event, ui);
}
- // refresh the tree icons - needs a timeout to allow nestedSort
- // to remove unused elements before we check for their existence
- setTimeout(refresh_tree, 5);
+ // Refresh UI (no more timeout needed)
+ refresh_tree();
$.post(SITE_URL + $url, post );
}
- });
+ };
+ // init nestedSortable with options
+ $item_list.nestedSortable($.extend({}, default_opts, sortable_opts));
}
pyro.chosen = function()
Something went wrong with that request. Please try again.