rails_admin_sort_embedded 0.0.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/.gitignore +18 -0
- data/.ruby-gemset +1 -0
- data/.ruby-version +2 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +22 -0
- data/README.md +65 -0
- data/Rakefile +1 -0
- data/app/assets/javascripts/rails_admin/jquery.mjs.nestedSortable.js +639 -0
- data/app/assets/javascripts/rails_admin/rails_admin_sort_embedded.js.coffee +71 -0
- data/app/assets/stylesheets/rails_admin/rails_admin_sort_embedded.css.scss +199 -0
- data/app/views/rails_admin/main/sort_embedded.html.haml +15 -0
- data/config/locales/en.nested_set.yml +9 -0
- data/config/locales/es.nested_set.yml +9 -0
- data/config/locales/ru.nested_set.yml +9 -0
- data/lib/rails_admin_sort_embedded.rb +14 -0
- data/lib/rails_admin_sort_embedded/action.rb +83 -0
- data/lib/rails_admin_sort_embedded/configuration.rb +21 -0
- data/lib/rails_admin_sort_embedded/engine.rb +12 -0
- data/lib/rails_admin_sort_embedded/helper.rb +118 -0
- data/lib/rails_admin_sort_embedded/model.rb +9 -0
- data/lib/rails_admin_sort_embedded/version.rb +3 -0
- data/rails_admin_sort_embedded.gemspec +21 -0
- metadata +81 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 405deb9b31767b85e60750d868aa9e6cc70ed84a
|
4
|
+
data.tar.gz: 3743879dd0ba645241c870ac8e57aa7b481036c7
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: eed0c51f5b067ebe1bc3b0de16445b93b5a6388ba15f1322834836445a2d26df57c615a16da3b7322edb95670b23ae580d6ea7c21f72d1bd322c4e00ff72fbe7
|
7
|
+
data.tar.gz: 736c09f9fe8ce410bc29506055221b7b3700d178abfe9855289983592d99f4711fcbe5a67d7565fdc02934817420776385eb6f187b43e7391ddc8e9c88fd4f28
|
data/.gitignore
ADDED
data/.ruby-gemset
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
rails_admin_sort_embedded
|
data/.ruby-version
ADDED
data/Gemfile
ADDED
data/LICENSE.txt
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
Copyright (c) 2013 Gleb Tv
|
2
|
+
|
3
|
+
MIT License
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
6
|
+
a copy of this software and associated documentation files (the
|
7
|
+
"Software"), to deal in the Software without restriction, including
|
8
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
9
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
10
|
+
permit persons to whom the Software is furnished to do so, subject to
|
11
|
+
the following conditions:
|
12
|
+
|
13
|
+
The above copyright notice and this permission notice shall be
|
14
|
+
included in all copies or substantial portions of the Software.
|
15
|
+
|
16
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
17
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
18
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
19
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
20
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
21
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
22
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,65 @@
|
|
1
|
+
# RailsAdminSortEmbedded
|
2
|
+
|
3
|
+
Sort for Embedded documents in mongoid. Field order is need.
|
4
|
+
|
5
|
+
Bonus features:
|
6
|
+
|
7
|
+
* special case support for `cover` and `image` columns
|
8
|
+
|
9
|
+
## Installation
|
10
|
+
|
11
|
+
Add this line to your application's Gemfile:
|
12
|
+
|
13
|
+
gem 'rails_admin_sort_embedded', :github => 'ack43/rails_admin_sort_embedded'
|
14
|
+
|
15
|
+
And then execute:
|
16
|
+
|
17
|
+
$ bundle
|
18
|
+
|
19
|
+
## Usage with rails_admin
|
20
|
+
|
21
|
+
Add the sort_embedded action for each model or only for models you need
|
22
|
+
|
23
|
+
RailsAdmin.config do |config|
|
24
|
+
config.actions do
|
25
|
+
......
|
26
|
+
sort_embedded do
|
27
|
+
visible do
|
28
|
+
%w(Page).include? bindings[:abstract_model].model_name
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
In embedded model:
|
35
|
+
|
36
|
+
|
37
|
+
field :order, type: Integer, default: 0
|
38
|
+
scope :sorted, -> { order_by([:order, :asc]) } #optional
|
39
|
+
|
40
|
+
In parent model:
|
41
|
+
|
42
|
+
embeds_many :method_name
|
43
|
+
rails_admin do ...
|
44
|
+
sort_embedded({
|
45
|
+
fields: [:method_name],
|
46
|
+
toggle_fields: [:enabled],
|
47
|
+
thumbnail_fields: [:image, :cover],
|
48
|
+
thumbnail_size: :thumb,
|
49
|
+
thumbnail_gem: :paperclip, # or :carrierwave
|
50
|
+
})
|
51
|
+
end
|
52
|
+
|
53
|
+
## Contributing
|
54
|
+
|
55
|
+
1. Fork it
|
56
|
+
2. Create your feature branch (`git checkout -b my-new-feature`)
|
57
|
+
3. Commit your changes (`git commit -am 'Add some feature'`)
|
58
|
+
4. Push to the branch (`git push origin my-new-feature`)
|
59
|
+
5. Create new Pull Request
|
60
|
+
|
61
|
+
## Credits
|
62
|
+
|
63
|
+
Some ideas and code for this gem are taken from:
|
64
|
+
|
65
|
+
https://github.com/rs-pro/rails_admin_nested_set
|
data/Rakefile
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
require "bundler/gem_tasks"
|
@@ -0,0 +1,639 @@
|
|
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
|
+
|
14
|
+
(function($) {
|
15
|
+
|
16
|
+
function isOverAxis( x, reference, size ) {
|
17
|
+
return ( x > reference ) && ( x < ( reference + size ) );
|
18
|
+
}
|
19
|
+
|
20
|
+
$.widget("mjs.nestedSortable", $.extend({}, $.ui.sortable.prototype, {
|
21
|
+
|
22
|
+
options: {
|
23
|
+
doNotClear: false,
|
24
|
+
expandOnHover: 700,
|
25
|
+
isAllowed: function(placeholder, placeholderParent, originalItem) { return true; },
|
26
|
+
isTree: false,
|
27
|
+
listType: 'ol',
|
28
|
+
maxLevels: 0,
|
29
|
+
protectRoot: false,
|
30
|
+
rootID: null,
|
31
|
+
rtl: false,
|
32
|
+
startCollapsed: false,
|
33
|
+
tabSize: 20,
|
34
|
+
scrollX: true, //if true, the page scrolls when a list item is dragged past the width of the container
|
35
|
+
scrollY: true, //if true, the page scrolls when a list item is dragged past the height of the container
|
36
|
+
branchClass: 'mjs-nestedSortable-branch',
|
37
|
+
collapsedClass: 'mjs-nestedSortable-collapsed',
|
38
|
+
disableNestingClass: 'mjs-nestedSortable-no-nesting',
|
39
|
+
errorClass: 'mjs-nestedSortable-error',
|
40
|
+
expandedClass: 'mjs-nestedSortable-expanded',
|
41
|
+
hoveringClass: 'mjs-nestedSortable-hovering',
|
42
|
+
leafClass: 'mjs-nestedSortable-leaf',
|
43
|
+
disabledClass: 'mjs-nestedSortable-disabled'
|
44
|
+
},
|
45
|
+
|
46
|
+
_create: function() {
|
47
|
+
this.element.data('ui-sortable', this.element.data('mjs-nestedSortable'));
|
48
|
+
|
49
|
+
// mjs - prevent browser from freezing if the HTML is not correct
|
50
|
+
if (!this.element.is(this.options.listType))
|
51
|
+
throw new Error('nestedSortable: Please check that the listType option is set to your actual list type');
|
52
|
+
|
53
|
+
// mjs - force 'intersect' tolerance method if we have a tree with expanding/collapsing functionality
|
54
|
+
if (this.options.isTree && this.options.expandOnHover) {
|
55
|
+
this.options.tolerance = 'intersect';
|
56
|
+
}
|
57
|
+
|
58
|
+
$.ui.sortable.prototype._create.apply(this, arguments);
|
59
|
+
|
60
|
+
// mjs - prepare the tree by applying the right classes (the CSS is responsible for actual hide/show functionality)
|
61
|
+
if (this.options.isTree) {
|
62
|
+
var self = this;
|
63
|
+
$(this.items).each(function() {
|
64
|
+
var $li = this.item;
|
65
|
+
if ($li.children(self.options.listType).length) {
|
66
|
+
$li.addClass(self.options.branchClass);
|
67
|
+
// expand/collapse class only if they have children
|
68
|
+
if (self.options.startCollapsed) $li.addClass(self.options.collapsedClass);
|
69
|
+
else $li.addClass(self.options.expandedClass);
|
70
|
+
} else {
|
71
|
+
$li.addClass(self.options.leafClass);
|
72
|
+
}
|
73
|
+
})
|
74
|
+
}
|
75
|
+
},
|
76
|
+
|
77
|
+
_destroy: function() {
|
78
|
+
this.element
|
79
|
+
.removeData("mjs-nestedSortable")
|
80
|
+
.removeData("ui-sortable");
|
81
|
+
return $.ui.sortable.prototype._destroy.apply(this, arguments);
|
82
|
+
},
|
83
|
+
|
84
|
+
_mouseDrag: function(event) {
|
85
|
+
var i, item, itemElement, intersection,
|
86
|
+
o = this.options,
|
87
|
+
scrolled = false;
|
88
|
+
|
89
|
+
//Compute the helpers position
|
90
|
+
this.position = this._generatePosition(event);
|
91
|
+
this.positionAbs = this._convertPositionTo("absolute");
|
92
|
+
|
93
|
+
if (!this.lastPositionAbs) {
|
94
|
+
this.lastPositionAbs = this.positionAbs;
|
95
|
+
}
|
96
|
+
|
97
|
+
//Do scrolling
|
98
|
+
if(this.options.scroll) {
|
99
|
+
if(this.scrollParent[0] != document && this.scrollParent[0].tagName != 'HTML') {
|
100
|
+
|
101
|
+
if( o.scrollY ) {
|
102
|
+
if((this.overflowOffset.top + this.scrollParent[0].offsetHeight) - event.pageY < o.scrollSensitivity) {
|
103
|
+
this.scrollParent[0].scrollTop = scrolled = this.scrollParent[0].scrollTop + o.scrollSpeed;
|
104
|
+
} else if(event.pageY - this.overflowOffset.top < o.scrollSensitivity) {
|
105
|
+
this.scrollParent[0].scrollTop = scrolled = this.scrollParent[0].scrollTop - o.scrollSpeed;
|
106
|
+
}
|
107
|
+
}
|
108
|
+
if( o.scrollX ) {
|
109
|
+
if((this.overflowOffset.left + this.scrollParent[0].offsetWidth) - event.pageX < o.scrollSensitivity) {
|
110
|
+
this.scrollParent[0].scrollLeft = scrolled = this.scrollParent[0].scrollLeft + o.scrollSpeed;
|
111
|
+
} else if(event.pageX - this.overflowOffset.left < o.scrollSensitivity) {
|
112
|
+
this.scrollParent[0].scrollLeft = scrolled = this.scrollParent[0].scrollLeft - o.scrollSpeed;
|
113
|
+
}
|
114
|
+
}
|
115
|
+
} else {
|
116
|
+
if( o.scrollY ) {
|
117
|
+
if(event.pageY - $(document).scrollTop() < o.scrollSensitivity) {
|
118
|
+
scrolled = $(document).scrollTop($(document).scrollTop() - o.scrollSpeed);
|
119
|
+
} else if($(window).height() - (event.pageY - $(document).scrollTop()) < o.scrollSensitivity) {
|
120
|
+
scrolled = $(document).scrollTop($(document).scrollTop() + o.scrollSpeed);
|
121
|
+
}
|
122
|
+
}
|
123
|
+
if( o.scrollX ) {
|
124
|
+
if(event.pageX - $(document).scrollLeft() < o.scrollSensitivity) {
|
125
|
+
scrolled = $(document).scrollLeft($(document).scrollLeft() - o.scrollSpeed);
|
126
|
+
} else if($(window).width() - (event.pageX - $(document).scrollLeft()) < o.scrollSensitivity) {
|
127
|
+
scrolled = $(document).scrollLeft($(document).scrollLeft() + o.scrollSpeed);
|
128
|
+
}
|
129
|
+
}
|
130
|
+
}
|
131
|
+
|
132
|
+
if(scrolled !== false && $.ui.ddmanager && !o.dropBehaviour)
|
133
|
+
$.ui.ddmanager.prepareOffsets(this, event);
|
134
|
+
}
|
135
|
+
|
136
|
+
//Regenerate the absolute position used for position checks
|
137
|
+
this.positionAbs = this._convertPositionTo("absolute");
|
138
|
+
|
139
|
+
// mjs - find the top offset before rearrangement,
|
140
|
+
var previousTopOffset = this.placeholder.offset().top;
|
141
|
+
|
142
|
+
//Set the helper position
|
143
|
+
if(!this.options.axis || this.options.axis !== "y") {
|
144
|
+
this.helper[0].style.left = this.position.left+"px";
|
145
|
+
}
|
146
|
+
if(!this.options.axis || this.options.axis !== "x") {
|
147
|
+
this.helper[0].style.top = this.position.top+"px";
|
148
|
+
}
|
149
|
+
|
150
|
+
// mjs - check and reset hovering state at each cycle
|
151
|
+
this.hovering = this.hovering ? this.hovering : null;
|
152
|
+
this.mouseentered = this.mouseentered ? this.mouseentered : false;
|
153
|
+
|
154
|
+
// mjs - let's start caching some variables
|
155
|
+
var parentItem = (this.placeholder[0].parentNode.parentNode &&
|
156
|
+
$(this.placeholder[0].parentNode.parentNode).closest('.ui-sortable').length)
|
157
|
+
? $(this.placeholder[0].parentNode.parentNode)
|
158
|
+
: null,
|
159
|
+
level = this._getLevel(this.placeholder),
|
160
|
+
childLevels = this._getChildLevels(this.helper);
|
161
|
+
|
162
|
+
var newList = document.createElement(o.listType);
|
163
|
+
|
164
|
+
//Rearrange
|
165
|
+
for (i = this.items.length - 1; i >= 0; i--) {
|
166
|
+
|
167
|
+
//Cache variables and intersection, continue if no intersection
|
168
|
+
item = this.items[i];
|
169
|
+
itemElement = item.item[0];
|
170
|
+
intersection = this._intersectsWithPointer(item);
|
171
|
+
if (!intersection) {
|
172
|
+
continue;
|
173
|
+
}
|
174
|
+
|
175
|
+
// Only put the placeholder inside the current Container, skip all
|
176
|
+
// items form other containers. This works because when moving
|
177
|
+
// an item from one container to another the
|
178
|
+
// currentContainer is switched before the placeholder is moved.
|
179
|
+
//
|
180
|
+
// Without this moving items in "sub-sortables" can cause the placeholder to jitter
|
181
|
+
// beetween the outer and inner container.
|
182
|
+
if (item.instance !== this.currentContainer) {
|
183
|
+
continue;
|
184
|
+
}
|
185
|
+
|
186
|
+
// No action if intersected item is disabled
|
187
|
+
// and the element above or below in the direction we're going is also disabled
|
188
|
+
if (itemElement.className.indexOf(o.disabledClass) !== -1) {
|
189
|
+
// Note: intersection hardcoded direction values from jquery.ui.sortable.js:_intersectsWithPointer
|
190
|
+
if (intersection === 2) {
|
191
|
+
// Going down
|
192
|
+
var itemAfter = this.items[i + 1];
|
193
|
+
if (itemAfter && itemAfter.item[0].className.indexOf(o.disabledClass) !== -1){
|
194
|
+
continue;
|
195
|
+
}
|
196
|
+
|
197
|
+
}
|
198
|
+
else if (intersection === 1) {
|
199
|
+
// Going up
|
200
|
+
var itemBefore = this.items[i - 1];
|
201
|
+
if (itemBefore && itemBefore.item[0].className.indexOf(o.disabledClass) !== -1){
|
202
|
+
continue;
|
203
|
+
}
|
204
|
+
}
|
205
|
+
}
|
206
|
+
|
207
|
+
// cannot intersect with itself
|
208
|
+
// no useless actions that have been done before
|
209
|
+
// no action if the item moved is the parent of the item checked
|
210
|
+
if (itemElement !== this.currentItem[0] &&
|
211
|
+
this.placeholder[intersection === 1 ? "next" : "prev"]()[0] !== itemElement &&
|
212
|
+
!$.contains(this.placeholder[0], itemElement) &&
|
213
|
+
(this.options.type === "semi-dynamic" ? !$.contains(this.element[0], itemElement) : true)
|
214
|
+
) {
|
215
|
+
|
216
|
+
// mjs - we are intersecting an element: trigger the mouseenter event and store this state
|
217
|
+
if (!this.mouseentered) {
|
218
|
+
$(itemElement).mouseenter();
|
219
|
+
this.mouseentered = true;
|
220
|
+
}
|
221
|
+
|
222
|
+
// mjs - if the element has children and they are hidden, show them after a delay (CSS responsible)
|
223
|
+
if (o.isTree && $(itemElement).hasClass(o.collapsedClass) && o.expandOnHover) {
|
224
|
+
if (!this.hovering) {
|
225
|
+
$(itemElement).addClass(o.hoveringClass);
|
226
|
+
var self = this;
|
227
|
+
this.hovering = window.setTimeout(function() {
|
228
|
+
$(itemElement).removeClass(o.collapsedClass).addClass(o.expandedClass);
|
229
|
+
self.refreshPositions();
|
230
|
+
self._trigger("expand", event, self._uiHash());
|
231
|
+
}, o.expandOnHover);
|
232
|
+
}
|
233
|
+
}
|
234
|
+
|
235
|
+
this.direction = intersection == 1 ? "down" : "up";
|
236
|
+
|
237
|
+
// mjs - rearrange the elements and reset timeouts and hovering state
|
238
|
+
if (this.options.tolerance == "pointer" || this._intersectsWithSides(item)) {
|
239
|
+
$(itemElement).mouseleave();
|
240
|
+
this.mouseentered = false;
|
241
|
+
$(itemElement).removeClass(o.hoveringClass);
|
242
|
+
this.hovering && window.clearTimeout(this.hovering);
|
243
|
+
this.hovering = null;
|
244
|
+
|
245
|
+
// mjs - do not switch container if it's a root item and 'protectRoot' is true
|
246
|
+
// or if it's not a root item but we are trying to make it root
|
247
|
+
if (o.protectRoot
|
248
|
+
&& ! (this.currentItem[0].parentNode == this.element[0] // it's a root item
|
249
|
+
&& itemElement.parentNode != this.element[0]) // it's intersecting a non-root item
|
250
|
+
) {
|
251
|
+
if (this.currentItem[0].parentNode != this.element[0]
|
252
|
+
&& itemElement.parentNode == this.element[0]
|
253
|
+
) {
|
254
|
+
|
255
|
+
if ( ! $(itemElement).children(o.listType).length) {
|
256
|
+
itemElement.appendChild(newList);
|
257
|
+
o.isTree && $(itemElement).removeClass(o.leafClass).addClass(o.branchClass + ' ' + o.expandedClass);
|
258
|
+
}
|
259
|
+
|
260
|
+
var a = this.direction === "down" ? $(itemElement).prev().children(o.listType) : $(itemElement).children(o.listType);
|
261
|
+
if (a[0] !== undefined) {
|
262
|
+
this._rearrange(event, null, a);
|
263
|
+
}
|
264
|
+
|
265
|
+
} else {
|
266
|
+
this._rearrange(event, item);
|
267
|
+
}
|
268
|
+
} else if ( ! o.protectRoot) {
|
269
|
+
this._rearrange(event, item);
|
270
|
+
}
|
271
|
+
} else {
|
272
|
+
break;
|
273
|
+
}
|
274
|
+
|
275
|
+
// Clear emtpy ul's/ol's
|
276
|
+
this._clearEmpty(itemElement);
|
277
|
+
|
278
|
+
this._trigger("change", event, this._uiHash());
|
279
|
+
break;
|
280
|
+
}
|
281
|
+
}
|
282
|
+
|
283
|
+
// mjs - to find the previous sibling in the list, keep backtracking until we hit a valid list item.
|
284
|
+
var previousItem = this.placeholder[0].previousSibling ? $(this.placeholder[0].previousSibling) : null;
|
285
|
+
if (previousItem != null) {
|
286
|
+
while (previousItem[0].nodeName.toLowerCase() != 'li' || previousItem[0].className.indexOf(o.disabledClass) !== -1 || previousItem[0] == this.currentItem[0] || previousItem[0] == this.helper[0]) {
|
287
|
+
if (previousItem[0].previousSibling) {
|
288
|
+
previousItem = $(previousItem[0].previousSibling);
|
289
|
+
} else {
|
290
|
+
previousItem = null;
|
291
|
+
break;
|
292
|
+
}
|
293
|
+
}
|
294
|
+
}
|
295
|
+
|
296
|
+
// mjs - to find the next sibling in the list, keep stepping forward until we hit a valid list item.
|
297
|
+
var nextItem = this.placeholder[0].nextSibling ? $(this.placeholder[0].nextSibling) : null;
|
298
|
+
if (nextItem != null) {
|
299
|
+
while (nextItem[0].nodeName.toLowerCase() != 'li' || nextItem[0].className.indexOf(o.disabledClass) !== -1 || nextItem[0] == this.currentItem[0] || nextItem[0] == this.helper[0]) {
|
300
|
+
if (nextItem[0].nextSibling) {
|
301
|
+
nextItem = $(nextItem[0].nextSibling);
|
302
|
+
} else {
|
303
|
+
nextItem = null;
|
304
|
+
break;
|
305
|
+
}
|
306
|
+
}
|
307
|
+
}
|
308
|
+
|
309
|
+
this.beyondMaxLevels = 0;
|
310
|
+
|
311
|
+
// mjs - if the item is moved to the left, send it one level up but only if it's at the bottom of the list
|
312
|
+
if (parentItem != null
|
313
|
+
&& nextItem == null
|
314
|
+
&& ! (o.protectRoot && parentItem[0].parentNode == this.element[0])
|
315
|
+
&&
|
316
|
+
(o.rtl && (this.positionAbs.left + this.helper.outerWidth() > parentItem.offset().left + parentItem.outerWidth())
|
317
|
+
|| ! o.rtl && (this.positionAbs.left < parentItem.offset().left))
|
318
|
+
) {
|
319
|
+
|
320
|
+
parentItem.after(this.placeholder[0]);
|
321
|
+
if (o.isTree && parentItem.children(o.listItem).children('li:visible:not(.ui-sortable-helper)').length < 1) {
|
322
|
+
parentItem.removeClass(this.options.branchClass + ' ' + this.options.expandedClass)
|
323
|
+
.addClass(this.options.leafClass);
|
324
|
+
}
|
325
|
+
this._clearEmpty(parentItem[0]);
|
326
|
+
this._trigger("change", event, this._uiHash());
|
327
|
+
}
|
328
|
+
// mjs - if the item is below a sibling and is moved to the right, make it a child of that sibling
|
329
|
+
else if (previousItem != null
|
330
|
+
&& ! previousItem.hasClass(o.disableNestingClass)
|
331
|
+
&&
|
332
|
+
(previousItem.children(o.listType).length && previousItem.children(o.listType).is(':visible')
|
333
|
+
|| ! previousItem.children(o.listType).length)
|
334
|
+
&& ! (o.protectRoot && this.currentItem[0].parentNode == this.element[0])
|
335
|
+
&&
|
336
|
+
(o.rtl && (this.positionAbs.left + this.helper.outerWidth() < previousItem.offset().left + previousItem.outerWidth() - o.tabSize)
|
337
|
+
|| ! o.rtl && (this.positionAbs.left > previousItem.offset().left + o.tabSize))
|
338
|
+
) {
|
339
|
+
|
340
|
+
this._isAllowed(previousItem, level, level+childLevels+1);
|
341
|
+
|
342
|
+
if (!previousItem.children(o.listType).length) {
|
343
|
+
previousItem[0].appendChild(newList);
|
344
|
+
o.isTree && previousItem.removeClass(o.leafClass).addClass(o.branchClass + ' ' + o.expandedClass);
|
345
|
+
}
|
346
|
+
|
347
|
+
// mjs - if this item is being moved from the top, add it to the top of the list.
|
348
|
+
if (previousTopOffset && (previousTopOffset <= previousItem.offset().top)) {
|
349
|
+
previousItem.children(o.listType).prepend(this.placeholder);
|
350
|
+
}
|
351
|
+
// mjs - otherwise, add it to the bottom of the list.
|
352
|
+
else {
|
353
|
+
previousItem.children(o.listType)[0].appendChild(this.placeholder[0]);
|
354
|
+
}
|
355
|
+
|
356
|
+
this._trigger("change", event, this._uiHash());
|
357
|
+
}
|
358
|
+
else {
|
359
|
+
this._isAllowed(parentItem, level, level+childLevels);
|
360
|
+
}
|
361
|
+
|
362
|
+
//Post events to containers
|
363
|
+
this._contactContainers(event);
|
364
|
+
|
365
|
+
//Interconnect with droppables
|
366
|
+
if($.ui.ddmanager) {
|
367
|
+
$.ui.ddmanager.drag(this, event);
|
368
|
+
}
|
369
|
+
|
370
|
+
//Call callbacks
|
371
|
+
this._trigger('sort', event, this._uiHash());
|
372
|
+
|
373
|
+
this.lastPositionAbs = this.positionAbs;
|
374
|
+
return false;
|
375
|
+
|
376
|
+
},
|
377
|
+
|
378
|
+
_mouseStop: function(event, noPropagation) {
|
379
|
+
|
380
|
+
// mjs - if the item is in a position not allowed, send it back
|
381
|
+
if (this.beyondMaxLevels) {
|
382
|
+
|
383
|
+
this.placeholder.removeClass(this.options.errorClass);
|
384
|
+
|
385
|
+
if (this.domPosition.prev) {
|
386
|
+
$(this.domPosition.prev).after(this.placeholder);
|
387
|
+
} else {
|
388
|
+
$(this.domPosition.parent).prepend(this.placeholder);
|
389
|
+
}
|
390
|
+
|
391
|
+
this._trigger("revert", event, this._uiHash());
|
392
|
+
|
393
|
+
}
|
394
|
+
|
395
|
+
|
396
|
+
// mjs - clear the hovering timeout, just to be sure
|
397
|
+
$('.'+this.options.hoveringClass).mouseleave().removeClass(this.options.hoveringClass);
|
398
|
+
this.mouseentered = false;
|
399
|
+
this.hovering && window.clearTimeout(this.hovering);
|
400
|
+
this.hovering = null;
|
401
|
+
|
402
|
+
$.ui.sortable.prototype._mouseStop.apply(this, arguments);
|
403
|
+
|
404
|
+
},
|
405
|
+
|
406
|
+
// mjs - this function is slightly modified to make it easier to hover over a collapsed element and have it expand
|
407
|
+
_intersectsWithSides: function(item) {
|
408
|
+
|
409
|
+
var half = this.options.isTree ? .8 : .5;
|
410
|
+
|
411
|
+
var isOverBottomHalf = isOverAxis(this.positionAbs.top + this.offset.click.top, item.top + (item.height*half), item.height),
|
412
|
+
isOverTopHalf = isOverAxis(this.positionAbs.top + this.offset.click.top, item.top - (item.height*half), item.height),
|
413
|
+
isOverRightHalf = isOverAxis(this.positionAbs.left + this.offset.click.left, item.left + (item.width/2), item.width),
|
414
|
+
verticalDirection = this._getDragVerticalDirection(),
|
415
|
+
horizontalDirection = this._getDragHorizontalDirection();
|
416
|
+
|
417
|
+
if (this.floating && horizontalDirection) {
|
418
|
+
return ((horizontalDirection == "right" && isOverRightHalf) || (horizontalDirection == "left" && !isOverRightHalf));
|
419
|
+
} else {
|
420
|
+
return verticalDirection && ((verticalDirection == "down" && isOverBottomHalf) || (verticalDirection == "up" && isOverTopHalf));
|
421
|
+
}
|
422
|
+
|
423
|
+
},
|
424
|
+
|
425
|
+
_contactContainers: function(event) {
|
426
|
+
|
427
|
+
if (this.options.protectRoot && this.currentItem[0].parentNode == this.element[0] ) {
|
428
|
+
return;
|
429
|
+
}
|
430
|
+
|
431
|
+
$.ui.sortable.prototype._contactContainers.apply(this, arguments);
|
432
|
+
|
433
|
+
},
|
434
|
+
|
435
|
+
_clear: function(event, noPropagation) {
|
436
|
+
|
437
|
+
$.ui.sortable.prototype._clear.apply(this, arguments);
|
438
|
+
|
439
|
+
// mjs - clean last empty ul/ol
|
440
|
+
for (var i = this.items.length - 1; i >= 0; i--) {
|
441
|
+
var item = this.items[i].item[0];
|
442
|
+
this._clearEmpty(item);
|
443
|
+
}
|
444
|
+
|
445
|
+
},
|
446
|
+
|
447
|
+
serialize: function(options) {
|
448
|
+
|
449
|
+
var o = $.extend({}, this.options, options),
|
450
|
+
items = this._getItemsAsjQuery(o && o.connected),
|
451
|
+
str = [];
|
452
|
+
|
453
|
+
$(items).each(function() {
|
454
|
+
var res = ($(o.item || this).attr(o.attribute || 'id') || '')
|
455
|
+
.match(o.expression || (/(.+)[-=_](.+)/)),
|
456
|
+
pid = ($(o.item || this).parent(o.listType)
|
457
|
+
.parent(o.items)
|
458
|
+
.attr(o.attribute || 'id') || '')
|
459
|
+
.match(o.expression || (/(.+)[-=_](.+)/));
|
460
|
+
|
461
|
+
if (res) {
|
462
|
+
str.push(((o.key || res[1]) + '[' + (o.key && o.expression ? res[1] : res[2]) + ']')
|
463
|
+
+ '='
|
464
|
+
+ (pid ? (o.key && o.expression ? pid[1] : pid[2]) : o.rootID));
|
465
|
+
}
|
466
|
+
});
|
467
|
+
|
468
|
+
if(!str.length && o.key) {
|
469
|
+
str.push(o.key + '=');
|
470
|
+
}
|
471
|
+
|
472
|
+
return str.join('&');
|
473
|
+
|
474
|
+
},
|
475
|
+
|
476
|
+
toHierarchy: function(options) {
|
477
|
+
|
478
|
+
var o = $.extend({}, this.options, options),
|
479
|
+
sDepth = o.startDepthCount || 0,
|
480
|
+
ret = [];
|
481
|
+
|
482
|
+
$(this.element).children(o.items).each(function () {
|
483
|
+
var level = _recursiveItems(this);
|
484
|
+
ret.push(level);
|
485
|
+
});
|
486
|
+
|
487
|
+
return ret;
|
488
|
+
|
489
|
+
function _recursiveItems(item) {
|
490
|
+
var id = ($(item).attr(o.attribute || 'id') || '').match(o.expression || (/(.+)[-=_](.+)/));
|
491
|
+
if (id) {
|
492
|
+
var currentItem = {"id" : id[2]};
|
493
|
+
if ($(item).children(o.listType).children(o.items).length > 0) {
|
494
|
+
currentItem.children = [];
|
495
|
+
$(item).children(o.listType).children(o.items).each(function() {
|
496
|
+
var level = _recursiveItems(this);
|
497
|
+
currentItem.children.push(level);
|
498
|
+
});
|
499
|
+
}
|
500
|
+
return currentItem;
|
501
|
+
}
|
502
|
+
}
|
503
|
+
},
|
504
|
+
|
505
|
+
toArray: function(options) {
|
506
|
+
|
507
|
+
var o = $.extend({}, this.options, options),
|
508
|
+
sDepth = o.startDepthCount || 0,
|
509
|
+
ret = [],
|
510
|
+
left = 1;
|
511
|
+
|
512
|
+
if (!o.excludeRoot) {
|
513
|
+
ret.push({
|
514
|
+
"item_id": o.rootID,
|
515
|
+
"parent_id": null,
|
516
|
+
"depth": sDepth,
|
517
|
+
"left": left,
|
518
|
+
"right": ($(o.items, this.element).length + 1) * 2
|
519
|
+
});
|
520
|
+
left++
|
521
|
+
}
|
522
|
+
|
523
|
+
$(this.element).children(o.items).each(function () {
|
524
|
+
left = _recursiveArray(this, sDepth + 1, left);
|
525
|
+
});
|
526
|
+
|
527
|
+
ret = ret.sort(function(a,b){ return (a.left - b.left); });
|
528
|
+
|
529
|
+
return ret;
|
530
|
+
|
531
|
+
function _recursiveArray(item, depth, left) {
|
532
|
+
|
533
|
+
var right = left + 1,
|
534
|
+
id,
|
535
|
+
pid;
|
536
|
+
|
537
|
+
if ($(item).children(o.listType).children(o.items).length > 0) {
|
538
|
+
depth ++;
|
539
|
+
$(item).children(o.listType).children(o.items).each(function () {
|
540
|
+
right = _recursiveArray($(this), depth, right);
|
541
|
+
});
|
542
|
+
depth --;
|
543
|
+
}
|
544
|
+
|
545
|
+
id = ($(item).attr(o.attribute || 'id')).match(o.expression || (/(.+)[-=_](.+)/));
|
546
|
+
|
547
|
+
if (depth === sDepth + 1) {
|
548
|
+
pid = o.rootID;
|
549
|
+
} else {
|
550
|
+
var parentItem = ($(item).parent(o.listType)
|
551
|
+
.parent(o.items)
|
552
|
+
.attr(o.attribute || 'id'))
|
553
|
+
.match(o.expression || (/(.+)[-=_](.+)/));
|
554
|
+
pid = parentItem[2];
|
555
|
+
}
|
556
|
+
|
557
|
+
if (id) {
|
558
|
+
ret.push({"item_id": id[2], "parent_id": pid, "depth": depth, "left": left, "right": right});
|
559
|
+
}
|
560
|
+
|
561
|
+
left = right + 1;
|
562
|
+
return left;
|
563
|
+
}
|
564
|
+
|
565
|
+
},
|
566
|
+
|
567
|
+
_clearEmpty: function(item) {
|
568
|
+
var o = this.options;
|
569
|
+
|
570
|
+
var emptyList = $(item).children(o.listType);
|
571
|
+
|
572
|
+
if (emptyList.length && !emptyList.children().length && !o.doNotClear) {
|
573
|
+
o.isTree && $(item).removeClass(o.branchClass + ' ' + o.expandedClass).addClass(o.leafClass);
|
574
|
+
emptyList.remove();
|
575
|
+
} else if (o.isTree && emptyList.length && emptyList.children().length && emptyList.is(':visible')) {
|
576
|
+
$(item).removeClass(o.leafClass).addClass(o.branchClass + ' ' + o.expandedClass);
|
577
|
+
} else if (o.isTree && emptyList.length && emptyList.children().length && !emptyList.is(':visible')) {
|
578
|
+
$(item).removeClass(o.leafClass).addClass(o.branchClass + ' ' + o.collapsedClass);
|
579
|
+
}
|
580
|
+
|
581
|
+
},
|
582
|
+
|
583
|
+
_getLevel: function(item) {
|
584
|
+
|
585
|
+
var level = 1;
|
586
|
+
|
587
|
+
if (this.options.listType) {
|
588
|
+
var list = item.closest(this.options.listType);
|
589
|
+
while (list && list.length > 0 &&
|
590
|
+
!list.is('.ui-sortable')) {
|
591
|
+
level++;
|
592
|
+
list = list.parent().closest(this.options.listType);
|
593
|
+
}
|
594
|
+
}
|
595
|
+
|
596
|
+
return level;
|
597
|
+
},
|
598
|
+
|
599
|
+
_getChildLevels: function(parent, depth) {
|
600
|
+
var self = this,
|
601
|
+
o = this.options,
|
602
|
+
result = 0;
|
603
|
+
depth = depth || 0;
|
604
|
+
|
605
|
+
$(parent).children(o.listType).children(o.items).each(function (index, child) {
|
606
|
+
result = Math.max(self._getChildLevels(child, depth + 1), result);
|
607
|
+
});
|
608
|
+
|
609
|
+
return depth ? result + 1 : result;
|
610
|
+
},
|
611
|
+
|
612
|
+
_isAllowed: function(parentItem, level, levels) {
|
613
|
+
var o = this.options,
|
614
|
+
maxLevels = this.placeholder.closest('.ui-sortable').nestedSortable('option', 'maxLevels'); // this takes into account the maxLevels set to the recipient list
|
615
|
+
|
616
|
+
// mjs - is the root protected?
|
617
|
+
// mjs - are we nesting too deep?
|
618
|
+
if ( ! o.isAllowed(this.placeholder, parentItem, this.currentItem)) {
|
619
|
+
this.placeholder.addClass(o.errorClass);
|
620
|
+
if (maxLevels < levels && maxLevels != 0) {
|
621
|
+
this.beyondMaxLevels = levels - maxLevels;
|
622
|
+
} else {
|
623
|
+
this.beyondMaxLevels = 1;
|
624
|
+
}
|
625
|
+
} else {
|
626
|
+
if (maxLevels < levels && maxLevels != 0) {
|
627
|
+
this.placeholder.addClass(o.errorClass);
|
628
|
+
this.beyondMaxLevels = levels - maxLevels;
|
629
|
+
} else {
|
630
|
+
this.placeholder.removeClass(o.errorClass);
|
631
|
+
this.beyondMaxLevels = 0;
|
632
|
+
}
|
633
|
+
}
|
634
|
+
}
|
635
|
+
|
636
|
+
}));
|
637
|
+
|
638
|
+
$.mjs.nestedSortable.prototype.options = $.extend({}, $.ui.sortable.prototype.options, $.mjs.nestedSortable.prototype.options);
|
639
|
+
})(jQuery);
|