jquery-mjs-nestedSortable-rails 1.3.5 → 2.0.0.alpha1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: e55e5f4608112415a1fab82f98657d011f76308d
4
- data.tar.gz: c58df99bad0cc6794589683aa971e4e2c4a94797
3
+ metadata.gz: 30708b0dddfcc4c9c3f0f03ac0befee8d257bbe0
4
+ data.tar.gz: 9190fc9b9c90ca28c8296ae27b5356b928b524b5
5
5
  SHA512:
6
- metadata.gz: 9e0f8f5c59a5d98b124c4a362b56162b5ea08deca189177c6c013ac2b8c5d043b118ef671e8a639244b4eae778c6429fad30331b1888cb7b431d1d462e6229a9
7
- data.tar.gz: 56f0a57a6827718be3337394292c612381194fc740bfd6ab3e858d06b31fe461b1b4bbc1dfa9c773a50c1670080c291c8e641030076c1987eac5d05fea21dc47
6
+ metadata.gz: 0c351d6d07e0c73e82157b7afba30ea22e7572154c038d0c4e7ca11d6c7b48cb5550f19725f403195e7db28e02720d925fb57634ed398f9f12f8f72727078fea
7
+ data.tar.gz: 6ffdac5aaff84066300c0ce0d08e6524d40c18a3d68cd38dd382f8fcd3c367cbade2bd4cd76afffd23f69e7e801dab9e10d4db980fd62aab82c586905e1928e3
data/README.md CHANGED
@@ -5,6 +5,16 @@ Rails version of [mjs.nestedSortable](https://github.com/mjsarfatti/nestedSortab
5
5
  *nestedSortable* is a jQuery plugin that extends jQuery Sortable UI functionalities to nested lists.
6
6
  *Note:* **Version 2.0** *is published in branch '2.0alpha' and is ready for testing! At the moment it has only been tested in Firefox and Chrome, if you work with IE feel free to give it a shot and let me know if something goes wrong.*
7
7
 
8
+ ## What's new in version 2.0
9
+
10
+ The biggest change is that your nested list can now behave as a tree with expand/collapse funcionality. Simply set `isTree` to **true** in the options and you are good to go! Check the [demo](http://mjsarfatti.com/sandbox/nestedSortable) out to see what can be done with nestedSortable and a little CSS. (Note that all **nestedSortable** does is to assign/remove classes on the fly)
11
+ Also:
12
+ - **isAllowed** function finally works as expected, see the docs below
13
+ - Fixed: a small bug in the **protectRoot** function
14
+ - Changed: no drop zone will appear at all if you try to nest an item under another one that has the *no-nesting* class.
15
+ - Added: **doNotClear** option to prevent the plugin from deleting empty lists
16
+
17
+
8
18
  ## Features
9
19
 
10
20
  - Designed to work seamlessly with the [nested](http://articles.sitepoint.com/article/hierarchical-data-database "A Sitepoint tutorial on PHP, MYSQL and nested sets") [set](http://en.wikipedia.org/wiki/Nested_set_model "Wikipedia article on nested sets") model (have a look at the `toArray` method)
@@ -14,11 +24,13 @@ Rails version of [mjs.nestedSortable](https://github.com/mjsarfatti/nestedSortab
14
24
  - It is possible to define elements that will not accept a new nested item/list and a maximum depth for nested items
15
25
  - The root level can be protected
16
26
 
27
+
17
28
  ## Installation
18
29
 
19
30
  Add this line to your application's Gemfile:
20
31
 
21
- gem 'jquery-mjs-nestedSortable-rails'
32
+ gem 'jquery-mjs-nestedSortable-rails', '~> 2.0.0.pre.alpha.pre.1'
33
+ gem 'jquery-mjs-nestedSortable-rails', '~> 1.3.5'
22
34
 
23
35
  And then execute:
24
36
 
@@ -68,27 +80,54 @@ Please note: every `<li>` must have either one or two direct children, the first
68
80
 
69
81
  Also, the default list type is `<ol>`.
70
82
 
83
+ *This is the bare minimum to have a working nestedSortable. Check the [demo](http://mjsarfatti.com/sandbox/nestedSortable) out to see what can be accomplished with a little more.*
84
+
71
85
  ## Custom Options
72
86
 
73
87
  <dl>
74
- <dt>tabSize</dt>
75
- <dd>How far right or left (in pixels) the item has to travel in order to be nested or to be sent outside its current list. Default: <b>20</b></dd>
76
- <dt>disableNesting</dt>
77
- <dd>The class name of the items that will not accept nested lists. Default: <b>ui-nestedSortable-no-nesting</b></dd>
78
- <dt>errorClass</dt>
79
- <dd>The class given to the placeholder in case of error. Default: <b>ui-nestedSortable-error</b></dd>
88
+ <dt>doNotClear (2.0)</dt>
89
+ <dd>Set this to true if you don't want empty lists to be removed. Default: <b>false</b></dd>
90
+ <dt>expandOnHover (2.0)</dt>
91
+ <dd>How long (in ms) to wait before expanding a collapsed node (useful only if <code>isTree: true</code>). Default: <b>700</b></dd>
92
+ <dt>isAllowed (function)</dt>
93
+ <dd>You can specify a custom function to verify if a drop location is allowed. Default: <b>function (placeholder, placeholderParent, currentItem) { return true; }</b></dd>
94
+ <dt>isTree (2.0)</dt>
95
+ <dd>Set this to true if you want to use the new tree functionality. Default: <b>false</b></dd>
80
96
  <dt>listType</dt>
81
97
  <dd>The list type used (ordered or unordered). Default: <b>ol</b></dd>
82
98
  <dt>maxLevels</dt>
83
99
  <dd>The maximum depth of nested items the list can accept. If set to '0' the levels are unlimited. Default: <b>0</b></dd>
84
100
  <dt>protectRoot</dt>
85
- <dd>Wether to protect the root level (i.e. root items can be sorted but not nested, sub-items cannot become root items). Default: <b>false</b></dd>
101
+ <dd>Whether to protect the root level (i.e. root items can be sorted but not nested, sub-items cannot become root items). Default: <b>false</b></dd>
86
102
  <dt>rootID</dt>
87
103
  <dd>The id given to the root element (set this to whatever suits your data structure). Default: <b>null</b></dd>
88
104
  <dt>rtl</dt>
89
105
  <dd>Set this to true if you have a right-to-left page. Default: <b>false</b></dd>
90
- <dt>isAllowed (function)</dt>
91
- <dd>You can specify a custom function to verify if a drop location is allowed. Default: <b>function(item, parent) { return true; }</b></dd>
106
+ <dt>startCollapsed (2.0)</dt>
107
+ <dd>Set this to true if you want the plugin to collapse the tree on page load. Default: <b>false</b></dd>
108
+ <dt>tabSize</dt>
109
+ <dd>How far right or left (in pixels) the item has to travel in order to be nested or to be sent outside its current list. Default: <b>20</b></dd>
110
+ <dt>excludeRoot</dt>
111
+ <dd>Exlude the root item from the <code>toArray</code> output</dd>
112
+ </dl>
113
+
114
+ ## Custom Classes (you will set them in the options as well)
115
+
116
+ <dl>
117
+ <dt>branchClass (2.0)</dt>
118
+ <dd>Given to all items that have children. Default: <b>mjs-nestedSortable-branch</b></dd>
119
+ <dt>collapsedClass (2.0)</dt>
120
+ <dd>Given to branches that are collapsed. It will be switched to <b>expandedClass</b> when hovering for more then <b>expandOnHover</b> ms. Default: <b>mjs-nestedSortable-collapsed</b></dd>
121
+ <dt>disableNestingClass</dt>
122
+ <dd>Given to items that will not accept children. Default: <b>mjs-nestedSortable-no-nesting</b></dd>
123
+ <dt>errorClass</dt>
124
+ <dd>Given to the placeholder in case of error. Default: <b>mjs-nestedSortable-error</b></dd>
125
+ <dt>expandedClass (2.0)</dt>
126
+ <dd>Given to branches that are expanded. Default: <b>mjs-nestedSortable-expanded</b></dd>
127
+ <dt>hoveringClass (2.0)</dt>
128
+ <dd>Given to collapsed branches when dragging an item over them. Default: <b>mjs-nestedSortable-hovering</b></dd>
129
+ <dt>leafClass (2.0)<dt>
130
+ <dd>Given to items that do not have children. Default: <b>mjs-nestedSortable-leaf</b></dd>
92
131
  </dl>
93
132
 
94
133
  ## Custom Methods
@@ -132,18 +171,22 @@ Also, the default list type is `<ol>`.
132
171
 
133
172
  ## Requirements
134
173
 
135
- jQuery 1.4+
136
- jQuery UI Sortable 1.8+
174
+ jQuery UI Sortable 1.10+ (might work with 1.9, but not tested)
137
175
 
138
176
  ## Browser Compatibility
139
177
 
140
- Tested with: IE 6/7/8, Firefox 3.6/4, Chrome, Safari 3
178
+ Tested with: Firefox, Chrome
179
+ **NOTE: This is still an alpha version, please test thoroughly in whichever version of IE you target**
141
180
 
142
181
  ## License
143
182
 
144
183
  This work is licensed under the MIT License.
184
+ Which means you can do pretty much whatever you want with it.
185
+
186
+ Nonetheless if this plugin saved you money, saved you time or saved your life please take a moment to think about the work I've been doing for you and consider sharing a bit of your joy with me. Your donation, however small, will be greatly appreciated.
187
+ Thank you.
145
188
 
146
- This work is *pizzaware*. If it saved your life, or you just feel good at heart, please consider offering me a pizza. This can be done in two ways: (1) follow [this link](https://www.paypal.com/cgi-bin/webscr?cmd=_donations&business=RSJEW3N9PRMYY&lc=IT&item_name=Manuele%20Sarfatti&currency_code=EUR&bn=PP%2dDonationsBF%3abtn_donateCC_LG%2egif%3aNonHosted) to donate through paypal; (2) send me cash via traditional mail to my home address in Italy. Is the second method legal? It is in Italy if you use Posta assicurata. You should check with your local laws if you live elsewhere.
189
+ [Donate with PayPal](https://www.paypal.com/cgi-bin/webscr?cmd=_donations&business=RSJEW3N9PRMYY&lc=IT&item_name=Manuele%20Sarfatti&currency_code=EUR&bn=PP%2dDonationsBF%3abtn_donateCC_LG%2egif%3aNonHosted)
147
190
 
148
191
  ## Contributing
149
192
 
@@ -2,7 +2,7 @@ module Jquery
2
2
  module Mjs
3
3
  module NestedSortable
4
4
  module Rails
5
- VERSION = "1.3.5"
5
+ VERSION = "2.0.0.alpha1"
6
6
  end
7
7
  end
8
8
  end
@@ -1,52 +1,90 @@
1
1
  /*
2
2
  * jQuery UI Nested Sortable
3
- * v 1.3.5 / 21 jun 2012
4
- * http://mjsarfatti.com/code/nestedSortable
3
+ * v 2.0 / 29 oct 2012
4
+ * http://mjsarfatti.com/sandbox/nestedSortable
5
5
  *
6
6
  * Depends on:
7
- * jquery.ui.sortable.js 1.8+
7
+ * jquery.ui.sortable.js 1.10+
8
8
  *
9
- * Copyright (c) 2010-2012 Manuele J Sarfatti
9
+ * Copyright (c) 2010-2013 Manuele J Sarfatti
10
10
  * Licensed under the MIT License
11
11
  * http://www.opensource.org/licenses/mit-license.php
12
12
  */
13
13
 
14
14
  (function ($) {
15
15
 
16
+ function isOverAxis(x, reference, size) {
17
+ return ( x > reference ) && ( x < ( reference + size ) );
18
+ }
19
+
16
20
  $.widget("mjs.nestedSortable", $.extend({}, $.ui.sortable.prototype, {
17
21
 
18
22
  options: {
19
- tabSize: 20,
20
- disableNesting: 'mjs-nestedSortable-no-nesting',
21
- errorClass: 'mjs-nestedSortable-error',
22
23
  doNotClear: false,
24
+ expandOnHover: 700,
25
+ isAllowed: function (placeholder, placeholderParent, originalItem) {
26
+ return true;
27
+ },
28
+ isTree: false,
23
29
  listType: 'ol',
24
30
  maxLevels: 0,
25
31
  protectRoot: false,
26
32
  rootID: null,
27
33
  rtl: false,
28
- isAllowed: function (item, parent) {
29
- return true;
30
- }
34
+ startCollapsed: false,
35
+ tabSize: 20,
36
+
37
+ branchClass: 'mjs-nestedSortable-branch',
38
+ collapsedClass: 'mjs-nestedSortable-collapsed',
39
+ disableNestingClass: 'mjs-nestedSortable-no-nesting',
40
+ errorClass: 'mjs-nestedSortable-error',
41
+ expandedClass: 'mjs-nestedSortable-expanded',
42
+ hoveringClass: 'mjs-nestedSortable-hovering',
43
+ leafClass: 'mjs-nestedSortable-leaf'
31
44
  },
32
45
 
33
46
  _create: function () {
34
- this.element.data('sortable', this.element.data('nestedSortable'));
47
+ this.element.data('ui-sortable', this.element.data('mjs-nestedSortable'));
35
48
 
49
+ // mjs - prevent browser from freezing if the HTML is not correct
36
50
  if (!this.element.is(this.options.listType))
37
- throw new Error('nestedSortable: Please check the listType option is set to your actual list type');
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
+ }
38
57
 
39
- return $.ui.sortable.prototype._create.apply(this, arguments);
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
+ }
40
75
  },
41
76
 
42
- destroy: function () {
77
+ _destroy: function () {
43
78
  this.element
44
- .removeData("nestedSortable")
45
- .unbind(".nestedSortable");
46
- return $.ui.sortable.prototype.destroy.apply(this, arguments);
79
+ .removeData("mjs-nestedSortable")
80
+ .removeData("ui-sortable");
81
+ return $.ui.sortable.prototype._destroy.apply(this, arguments);
47
82
  },
48
83
 
49
84
  _mouseDrag: function (event) {
85
+ var i, item, itemElement, intersection,
86
+ o = this.options,
87
+ scrolled = false;
50
88
 
51
89
  //Compute the helpers position
52
90
  this.position = this._generatePosition(event);
@@ -56,34 +94,35 @@
56
94
  this.lastPositionAbs = this.positionAbs;
57
95
  }
58
96
 
59
- var o = this.options;
60
-
61
97
  //Do scrolling
62
98
  if (this.options.scroll) {
63
- var scrolled = false;
64
99
  if (this.scrollParent[0] != document && this.scrollParent[0].tagName != 'HTML') {
65
100
 
66
- if ((this.overflowOffset.top + this.scrollParent[0].offsetHeight) - event.pageY < o.scrollSensitivity)
101
+ if ((this.overflowOffset.top + this.scrollParent[0].offsetHeight) - event.pageY < o.scrollSensitivity) {
67
102
  this.scrollParent[0].scrollTop = scrolled = this.scrollParent[0].scrollTop + o.scrollSpeed;
68
- else if (event.pageY - this.overflowOffset.top < o.scrollSensitivity)
103
+ } else if (event.pageY - this.overflowOffset.top < o.scrollSensitivity) {
69
104
  this.scrollParent[0].scrollTop = scrolled = this.scrollParent[0].scrollTop - o.scrollSpeed;
105
+ }
70
106
 
71
- if ((this.overflowOffset.left + this.scrollParent[0].offsetWidth) - event.pageX < o.scrollSensitivity)
107
+ if ((this.overflowOffset.left + this.scrollParent[0].offsetWidth) - event.pageX < o.scrollSensitivity) {
72
108
  this.scrollParent[0].scrollLeft = scrolled = this.scrollParent[0].scrollLeft + o.scrollSpeed;
73
- else if (event.pageX - this.overflowOffset.left < o.scrollSensitivity)
109
+ } else if (event.pageX - this.overflowOffset.left < o.scrollSensitivity) {
74
110
  this.scrollParent[0].scrollLeft = scrolled = this.scrollParent[0].scrollLeft - o.scrollSpeed;
111
+ }
75
112
 
76
113
  } else {
77
114
 
78
- if (event.pageY - $(document).scrollTop() < o.scrollSensitivity)
115
+ if (event.pageY - $(document).scrollTop() < o.scrollSensitivity) {
79
116
  scrolled = $(document).scrollTop($(document).scrollTop() - o.scrollSpeed);
80
- else if ($(window).height() - (event.pageY - $(document).scrollTop()) < o.scrollSensitivity)
117
+ } else if ($(window).height() - (event.pageY - $(document).scrollTop()) < o.scrollSensitivity) {
81
118
  scrolled = $(document).scrollTop($(document).scrollTop() + o.scrollSpeed);
119
+ }
82
120
 
83
- if (event.pageX - $(document).scrollLeft() < o.scrollSensitivity)
121
+ if (event.pageX - $(document).scrollLeft() < o.scrollSensitivity) {
84
122
  scrolled = $(document).scrollLeft($(document).scrollLeft() - o.scrollSpeed);
85
- else if ($(window).width() - (event.pageX - $(document).scrollLeft()) < o.scrollSensitivity)
123
+ } else if ($(window).width() - (event.pageX - $(document).scrollLeft()) < o.scrollSensitivity) {
86
124
  scrolled = $(document).scrollLeft($(document).scrollLeft() + o.scrollSpeed);
125
+ }
87
126
 
88
127
  }
89
128
 
@@ -94,34 +133,116 @@
94
133
  //Regenerate the absolute position used for position checks
95
134
  this.positionAbs = this._convertPositionTo("absolute");
96
135
 
97
- // Find the top offset before rearrangement,
136
+ // mjs - find the top offset before rearrangement,
98
137
  var previousTopOffset = this.placeholder.offset().top;
99
138
 
100
139
  //Set the helper position
101
- if (!this.options.axis || this.options.axis != "y") this.helper[0].style.left = this.position.left + 'px';
102
- if (!this.options.axis || this.options.axis != "x") this.helper[0].style.top = this.position.top + 'px';
140
+ if (!this.options.axis || this.options.axis !== "y") {
141
+ this.helper[0].style.left = this.position.left + "px";
142
+ }
143
+ if (!this.options.axis || this.options.axis !== "x") {
144
+ this.helper[0].style.top = this.position.top + "px";
145
+ }
146
+
147
+ // mjs - check and reset hovering state at each cycle
148
+ this.hovering = this.hovering ? this.hovering : null;
149
+ this.mouseentered = this.mouseentered ? this.mouseentered : false;
150
+
151
+ // mjs - let's start caching some variables
152
+ var parentItem = (this.placeholder[0].parentNode.parentNode &&
153
+ $(this.placeholder[0].parentNode.parentNode).closest('.ui-sortable').length)
154
+ ? $(this.placeholder[0].parentNode.parentNode)
155
+ : null,
156
+ level = this._getLevel(this.placeholder),
157
+ childLevels = this._getChildLevels(this.helper);
158
+
159
+ var newList = document.createElement(o.listType);
103
160
 
104
161
  //Rearrange
105
- for (var i = this.items.length - 1; i >= 0; i--) {
162
+ for (i = this.items.length - 1; i >= 0; i--) {
106
163
 
107
164
  //Cache variables and intersection, continue if no intersection
108
- var item = this.items[i], itemElement = item.item[0], intersection = this._intersectsWithPointer(item);
109
- if (!intersection) continue;
110
-
111
- if (itemElement != this.currentItem[0] //cannot intersect with itself
112
- && this.placeholder[intersection == 1 ? "next" : "prev"]()[0] != itemElement //no useless actions that have been done before
113
- && !$.contains(this.placeholder[0], itemElement) //no action if the item moved is the parent of the item checked
114
- && (this.options.type == 'semi-dynamic' ? !$.contains(this.element[0], itemElement) : true)
115
- //&& itemElement.parentNode == this.placeholder[0].parentNode // only rearrange items within the same container
165
+ item = this.items[i];
166
+ itemElement = item.item[0];
167
+ intersection = this._intersectsWithPointer(item);
168
+ if (!intersection) {
169
+ continue;
170
+ }
171
+
172
+ // Only put the placeholder inside the current Container, skip all
173
+ // items form other containers. This works because when moving
174
+ // an item from one container to another the
175
+ // currentContainer is switched before the placeholder is moved.
176
+ //
177
+ // Without this moving items in "sub-sortables" can cause the placeholder to jitter
178
+ // beetween the outer and inner container.
179
+ if (item.instance !== this.currentContainer) {
180
+ continue;
181
+ }
182
+
183
+ // cannot intersect with itself
184
+ // no useless actions that have been done before
185
+ // no action if the item moved is the parent of the item checked
186
+ if (itemElement !== this.currentItem[0] &&
187
+ this.placeholder[intersection === 1 ? "next" : "prev"]()[0] !== itemElement && !$.contains(this.placeholder[0], itemElement) &&
188
+ (this.options.type === "semi-dynamic" ? !$.contains(this.element[0], itemElement) : true)
116
189
  ) {
117
190
 
118
- $(itemElement).mouseenter();
191
+ // mjs - we are intersecting an element: trigger the mouseenter event and store this state
192
+ if (!this.mouseentered) {
193
+ $(itemElement).mouseenter();
194
+ this.mouseentered = true;
195
+ }
196
+
197
+ // mjs - if the element has children and they are hidden, show them after a delay (CSS responsible)
198
+ if (o.isTree && $(itemElement).hasClass(o.collapsedClass) && o.expandOnHover) {
199
+ if (!this.hovering) {
200
+ $(itemElement).addClass(o.hoveringClass);
201
+ var self = this;
202
+ this.hovering = window.setTimeout(function () {
203
+ $(itemElement).removeClass(o.collapsedClass).addClass(o.expandedClass);
204
+ self.refreshPositions();
205
+ self._trigger("expand", event, self._uiHash());
206
+ }, o.expandOnHover);
207
+ }
208
+ }
119
209
 
120
210
  this.direction = intersection == 1 ? "down" : "up";
121
211
 
212
+ // mjs - rearrange the elements and reset timeouts and hovering state
122
213
  if (this.options.tolerance == "pointer" || this._intersectsWithSides(item)) {
123
214
  $(itemElement).mouseleave();
124
- this._rearrange(event, item);
215
+ this.mouseentered = false;
216
+ $(itemElement).removeClass(o.hoveringClass);
217
+ this.hovering && window.clearTimeout(this.hovering);
218
+ this.hovering = null;
219
+
220
+ // mjs - do not switch container if it's a root item and 'protectRoot' is true
221
+ // or if it's not a root item but we are trying to make it root
222
+ if (o.protectRoot
223
+ && !(this.currentItem[0].parentNode == this.element[0] // it's a root item
224
+ && itemElement.parentNode != this.element[0]) // it's intersecting a non-root item
225
+ ) {
226
+ if (this.currentItem[0].parentNode != this.element[0]
227
+ && itemElement.parentNode == this.element[0]
228
+ ) {
229
+
230
+ if (!$(itemElement).children(o.listType).length) {
231
+ itemElement.appendChild(newList);
232
+ o.isTree && $(itemElement).removeClass(o.leafClass).addClass(o.branchClass + ' ' + o.expandedClass);
233
+ }
234
+
235
+ var a = this.direction === "down" ? $(itemElement).prev().children(o.listType) : $(itemElement).children(o.listType);
236
+ if (a[0] !== undefined) {
237
+ this._rearrange(event, null, a);
238
+ }
239
+
240
+ } else {
241
+ this._rearrange(event, item);
242
+ }
243
+ } else if (!o.protectRoot) {
244
+ this._rearrange(event, item);
245
+ }
125
246
  } else {
126
247
  break;
127
248
  }
@@ -134,14 +255,7 @@
134
255
  }
135
256
  }
136
257
 
137
- var parentItem = (this.placeholder[0].parentNode.parentNode &&
138
- $(this.placeholder[0].parentNode.parentNode).closest('.ui-sortable').length)
139
- ? $(this.placeholder[0].parentNode.parentNode)
140
- : null,
141
- level = this._getLevel(this.placeholder),
142
- childLevels = this._getChildLevels(this.helper);
143
-
144
- // To find the previous sibling in the list, keep backtracking until we hit a valid list item.
258
+ // mjs - to find the previous sibling in the list, keep backtracking until we hit a valid list item.
145
259
  var previousItem = this.placeholder[0].previousSibling ? $(this.placeholder[0].previousSibling) : null;
146
260
  if (previousItem != null) {
147
261
  while (previousItem[0].nodeName.toLowerCase() != 'li' || previousItem[0] == this.currentItem[0] || previousItem[0] == this.helper[0]) {
@@ -154,7 +268,7 @@
154
268
  }
155
269
  }
156
270
 
157
- // To find the next sibling in the list, keep stepping forward until we hit a valid list item.
271
+ // mjs - to find the next sibling in the list, keep stepping forward until we hit a valid list item.
158
272
  var nextItem = this.placeholder[0].nextSibling ? $(this.placeholder[0].nextSibling) : null;
159
273
  if (nextItem != null) {
160
274
  while (nextItem[0].nodeName.toLowerCase() != 'li' || nextItem[0] == this.currentItem[0] || nextItem[0] == this.helper[0]) {
@@ -167,34 +281,53 @@
167
281
  }
168
282
  }
169
283
 
170
- var newList = document.createElement(o.listType);
171
-
172
284
  this.beyondMaxLevels = 0;
173
285
 
174
- // If the item is moved to the left, send it to its parent's level unless there are siblings below it.
175
- if (parentItem != null && nextItem == null &&
176
- (o.rtl && (this.positionAbs.left + this.helper.outerWidth() > parentItem.offset().left + parentItem.outerWidth()) ||
177
- !o.rtl && (this.positionAbs.left < parentItem.offset().left))) {
286
+ // 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
287
+ if (parentItem != null
288
+ && nextItem == null
289
+ && !(o.protectRoot && parentItem[0].parentNode == this.element[0])
290
+ &&
291
+ (o.rtl && (this.positionAbs.left + this.helper.outerWidth() > parentItem.offset().left + parentItem.outerWidth())
292
+ || !o.rtl && (this.positionAbs.left < parentItem.offset().left))
293
+ ) {
294
+
178
295
  parentItem.after(this.placeholder[0]);
296
+ if (o.isTree && parentItem.children(o.listItem).children('li:visible:not(.ui-sortable-helper)').length < 1) {
297
+ parentItem.removeClass(this.options.branchClass + ' ' + this.options.expandedClass)
298
+ .addClass(this.options.leafClass);
299
+ }
179
300
  this._clearEmpty(parentItem[0]);
180
301
  this._trigger("change", event, this._uiHash());
181
302
  }
182
- // If the item is below a sibling and is moved to the right, make it a child of that sibling.
183
- else if (previousItem != null &&
184
- (o.rtl && (this.positionAbs.left + this.helper.outerWidth() < previousItem.offset().left + previousItem.outerWidth() - o.tabSize) ||
185
- !o.rtl && (this.positionAbs.left > previousItem.offset().left + o.tabSize))) {
303
+ // mjs - if the item is below a sibling and is moved to the right, make it a child of that sibling
304
+ else if (previousItem != null
305
+ && !previousItem.hasClass(o.disableNestingClass)
306
+ &&
307
+ (previousItem.children(o.listType).length && previousItem.children(o.listType).is(':visible')
308
+ || !previousItem.children(o.listType).length)
309
+ && !(o.protectRoot && this.currentItem[0].parentNode == this.element[0])
310
+ &&
311
+ (o.rtl && (this.positionAbs.left + this.helper.outerWidth() < previousItem.offset().left + previousItem.outerWidth() - o.tabSize)
312
+ || !o.rtl && (this.positionAbs.left > previousItem.offset().left + o.tabSize))
313
+ ) {
314
+
186
315
  this._isAllowed(previousItem, level, level + childLevels + 1);
316
+
187
317
  if (!previousItem.children(o.listType).length) {
188
318
  previousItem[0].appendChild(newList);
319
+ o.isTree && previousItem.removeClass(o.leafClass).addClass(o.branchClass + ' ' + o.expandedClass);
189
320
  }
190
- // If this item is being moved from the top, add it to the top of the list.
321
+
322
+ // mjs - if this item is being moved from the top, add it to the top of the list.
191
323
  if (previousTopOffset && (previousTopOffset <= previousItem.offset().top)) {
192
324
  previousItem.children(o.listType).prepend(this.placeholder);
193
325
  }
194
- // Otherwise, add it to the bottom of the list.
326
+ // mjs - otherwise, add it to the bottom of the list.
195
327
  else {
196
328
  previousItem.children(o.listType)[0].appendChild(this.placeholder[0]);
197
329
  }
330
+
198
331
  this._trigger("change", event, this._uiHash());
199
332
  }
200
333
  else {
@@ -205,7 +338,9 @@
205
338
  this._contactContainers(event);
206
339
 
207
340
  //Interconnect with droppables
208
- if ($.ui.ddmanager) $.ui.ddmanager.drag(this, event);
341
+ if ($.ui.ddmanager) {
342
+ $.ui.ddmanager.drag(this, event);
343
+ }
209
344
 
210
345
  //Call callbacks
211
346
  this._trigger('sort', event, this._uiHash());
@@ -217,7 +352,7 @@
217
352
 
218
353
  _mouseStop: function (event, noPropagation) {
219
354
 
220
- // If the item is in a position not allowed, send it back
355
+ // mjs - if the item is in a position not allowed, send it back
221
356
  if (this.beyondMaxLevels) {
222
357
 
223
358
  this.placeholder.removeClass(this.options.errorClass);
@@ -232,14 +367,56 @@
232
367
 
233
368
  }
234
369
 
235
- // Clean last empty ul/ol
370
+
371
+ // mjs - clear the hovering timeout, just to be sure
372
+ $('.' + this.options.hoveringClass).mouseleave().removeClass(this.options.hoveringClass);
373
+ this.mouseentered = false;
374
+ this.hovering && window.clearTimeout(this.hovering);
375
+ this.hovering = null;
376
+
377
+ $.ui.sortable.prototype._mouseStop.apply(this, arguments);
378
+
379
+ },
380
+
381
+ // mjs - this function is slightly modified to make it easier to hover over a collapsed element and have it expand
382
+ _intersectsWithSides: function (item) {
383
+
384
+ var half = this.options.isTree ? .8 : .5;
385
+
386
+ var isOverBottomHalf = isOverAxis(this.positionAbs.top + this.offset.click.top, item.top + (item.height * half), item.height),
387
+ isOverTopHalf = isOverAxis(this.positionAbs.top + this.offset.click.top, item.top - (item.height * half), item.height),
388
+ isOverRightHalf = isOverAxis(this.positionAbs.left + this.offset.click.left, item.left + (item.width / 2), item.width),
389
+ verticalDirection = this._getDragVerticalDirection(),
390
+ horizontalDirection = this._getDragHorizontalDirection();
391
+
392
+ if (this.floating && horizontalDirection) {
393
+ return ((horizontalDirection == "right" && isOverRightHalf) || (horizontalDirection == "left" && !isOverRightHalf));
394
+ } else {
395
+ return verticalDirection && ((verticalDirection == "down" && isOverBottomHalf) || (verticalDirection == "up" && isOverTopHalf));
396
+ }
397
+
398
+ },
399
+
400
+ _contactContainers: function (event) {
401
+
402
+ if (this.options.protectRoot && this.currentItem[0].parentNode == this.element[0]) {
403
+ return;
404
+ }
405
+
406
+ $.ui.sortable.prototype._contactContainers.apply(this, arguments);
407
+
408
+ },
409
+
410
+ _clear: function (event, noPropagation) {
411
+
412
+ $.ui.sortable.prototype._clear.apply(this, arguments);
413
+
414
+ // mjs - clean last empty ul/ol
236
415
  for (var i = this.items.length - 1; i >= 0; i--) {
237
416
  var item = this.items[i].item[0];
238
417
  this._clearEmpty(item);
239
418
  }
240
419
 
241
- $.ui.sortable.prototype._mouseStop.apply(this, arguments);
242
-
243
420
  },
244
421
 
245
422
  serialize: function (options) {
@@ -305,15 +482,18 @@
305
482
  var o = $.extend({}, this.options, options),
306
483
  sDepth = o.startDepthCount || 0,
307
484
  ret = [],
308
- left = 2;
309
-
310
- ret.push({
311
- "item_id": o.rootID,
312
- "parent_id": 'none',
313
- "depth": sDepth,
314
- "left": '1',
315
- "right": ($(o.items, this.element).length + 1) * 2
316
- });
485
+ left = 1;
486
+
487
+ if (!o.excludeRoot) {
488
+ ret.push({
489
+ "item_id": o.rootID,
490
+ "parent_id": null,
491
+ "depth": sDepth,
492
+ "left": left,
493
+ "right": ($(o.items, this.element).length + 1) * 2
494
+ });
495
+ left++
496
+ }
317
497
 
318
498
  $(this.element).children(o.items).each(function () {
319
499
  left = _recursiveArray(this, sDepth + 1, left);
@@ -362,10 +542,17 @@
362
542
  },
363
543
 
364
544
  _clearEmpty: function (item) {
545
+ var o = this.options;
546
+
547
+ var emptyList = $(item).children(o.listType);
365
548
 
366
- var emptyList = $(item).children(this.options.listType);
367
- if (emptyList.length && !emptyList.children().length && !this.options.doNotClear) {
549
+ if (emptyList.length && !emptyList.children().length && !o.doNotClear) {
550
+ o.isTree && $(item).removeClass(o.branchClass + ' ' + o.expandedClass).addClass(o.leafClass);
368
551
  emptyList.remove();
552
+ } else if (o.isTree && emptyList.length && emptyList.children().length && emptyList.is(':visible')) {
553
+ $(item).removeClass(o.leafClass).addClass(o.branchClass + ' ' + o.expandedClass);
554
+ } else if (o.isTree && emptyList.length && emptyList.children().length && !emptyList.is(':visible')) {
555
+ $(item).removeClass(o.leafClass).addClass(o.branchClass + ' ' + o.collapsedClass);
369
556
  }
370
557
 
371
558
  },
@@ -400,15 +587,11 @@
400
587
 
401
588
  _isAllowed: function (parentItem, level, levels) {
402
589
  var o = this.options,
403
- isRoot = $(this.domPosition.parent).hasClass('ui-sortable') ? true : false,
404
590
  maxLevels = this.placeholder.closest('.ui-sortable').nestedSortable('option', 'maxLevels'); // this takes into account the maxLevels set to the recipient list
405
591
 
406
- // Is the root protected?
407
- // Are we trying to nest under a no-nest?
408
- // Are we nesting too deep?
409
- if (!o.isAllowed(this.currentItem, parentItem) ||
410
- parentItem && parentItem.hasClass(o.disableNesting) ||
411
- o.protectRoot && (parentItem == null && !isRoot || isRoot && level > 1)) {
592
+ // mjs - is the root protected?
593
+ // mjs - are we nesting too deep?
594
+ if (!o.isAllowed(this.placeholder, parentItem, this.currentItem)) {
412
595
  this.placeholder.addClass(o.errorClass);
413
596
  if (maxLevels < levels && maxLevels != 0) {
414
597
  this.beyondMaxLevels = levels - maxLevels;
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: jquery-mjs-nestedSortable-rails
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.3.5
4
+ version: 2.0.0.alpha1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Michael Sprauer
@@ -73,9 +73,9 @@ required_ruby_version: !ruby/object:Gem::Requirement
73
73
  version: '0'
74
74
  required_rubygems_version: !ruby/object:Gem::Requirement
75
75
  requirements:
76
- - - '>='
76
+ - - '>'
77
77
  - !ruby/object:Gem::Version
78
- version: '0'
78
+ version: 1.3.1
79
79
  requirements: []
80
80
  rubyforge_project:
81
81
  rubygems_version: 2.2.2