rails_admin_nested_set 0.0.1
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.
- data/.gitignore +19 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +22 -0
- data/README.md +65 -0
- data/Rakefile +1 -0
- data/app/assets/javascripts/jquery.mjs.nestedSortable.js +429 -0
- data/app/assets/javascripts/rails_admin_nested_set.js.coffee +45 -0
- data/app/assets/stylesheets/rails_admin_nested_set.css.scss +305 -0
- data/app/views/rails_admin/main/nested_set.html.haml +3 -0
- data/config/locales/en.nested_set.yml +6 -0
- data/config/locales/ru.nested_set.yml +6 -0
- data/lib/rails_admin_nested_set/configuration.rb +20 -0
- data/lib/rails_admin_nested_set/engine.rb +7 -0
- data/lib/rails_admin_nested_set/helper.rb +45 -0
- data/lib/rails_admin_nested_set/model.rb +9 -0
- data/lib/rails_admin_nested_set/nested_set.rb +76 -0
- data/lib/rails_admin_nested_set/version.rb +3 -0
- data/lib/rails_admin_nested_set.rb +14 -0
- data/rails_admin_nested_set.gemspec +23 -0
- metadata +112 -0
data/.gitignore
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
|
+
# NestedSetUi
|
2
|
+
|
3
|
+
TODO: Write a gem description
|
4
|
+
|
5
|
+
## Installation
|
6
|
+
|
7
|
+
Add this line to your application's Gemfile:
|
8
|
+
|
9
|
+
gem 'rails_admin_nested_set'
|
10
|
+
|
11
|
+
And then execute:
|
12
|
+
|
13
|
+
$ bundle
|
14
|
+
|
15
|
+
Or install it yourself as:
|
16
|
+
|
17
|
+
$ gem install rails_admin_nested_set
|
18
|
+
|
19
|
+
## Usage with rails_admin
|
20
|
+
|
21
|
+
Add to /app/assets/javascripts/rails_admin/custom/ui.coffee
|
22
|
+
|
23
|
+
#= require rails_admin_nested_set
|
24
|
+
|
25
|
+
Add to /app/assets/stylesheets/rails_admin/custom/theming.sass
|
26
|
+
|
27
|
+
@import rails_admin_nested_set.css.scss
|
28
|
+
|
29
|
+
Add the nested_set action for each model or only for models you need
|
30
|
+
|
31
|
+
RailsAdmin.config do |config|
|
32
|
+
config.actions do
|
33
|
+
......
|
34
|
+
nested_set do
|
35
|
+
visible do
|
36
|
+
%w(Page).include? bindings[:abstract_model].model_name
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
In model config:
|
43
|
+
|
44
|
+
rails_admin do
|
45
|
+
...
|
46
|
+
|
47
|
+
nested_set({
|
48
|
+
max_depth: 1
|
49
|
+
})
|
50
|
+
end
|
51
|
+
|
52
|
+
## Contributing
|
53
|
+
|
54
|
+
1. Fork it
|
55
|
+
2. Create your feature branch (`git checkout -b my-new-feature`)
|
56
|
+
3. Commit your changes (`git commit -am 'Add some feature'`)
|
57
|
+
4. Push to the branch (`git push origin my-new-feature`)
|
58
|
+
5. Create new Pull Request
|
59
|
+
|
60
|
+
## Credits
|
61
|
+
|
62
|
+
Some ideas and code for this gem are taken from:
|
63
|
+
|
64
|
+
https://github.com/dalpo/rails_admin_nestable (MIT license)
|
65
|
+
https://github.com/the-teacher/the_sortable_tree (MIT license)
|
data/Rakefile
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
require "bundler/gem_tasks"
|
@@ -0,0 +1,429 @@
|
|
1
|
+
/*
|
2
|
+
* jQuery UI Nested Sortable
|
3
|
+
* v 1.3.5 / 21 jun 2012
|
4
|
+
* http://mjsarfatti.com/code/nestedSortable
|
5
|
+
*
|
6
|
+
* Depends on:
|
7
|
+
* jquery.ui.sortable.js 1.8+
|
8
|
+
*
|
9
|
+
* Copyright (c) 2010-2012 Manuele J Sarfatti
|
10
|
+
* Licensed under the MIT License
|
11
|
+
* http://www.opensource.org/licenses/mit-license.php
|
12
|
+
*/
|
13
|
+
|
14
|
+
(function($) {
|
15
|
+
|
16
|
+
$.widget("mjs.nestedSortable", $.extend({}, $.ui.sortable.prototype, {
|
17
|
+
|
18
|
+
options: {
|
19
|
+
tabSize: 20,
|
20
|
+
disableNesting: 'mjs-nestedSortable-no-nesting',
|
21
|
+
errorClass: 'mjs-nestedSortable-error',
|
22
|
+
doNotClear: false,
|
23
|
+
listType: 'ol',
|
24
|
+
maxLevels: 0,
|
25
|
+
protectRoot: false,
|
26
|
+
rootID: null,
|
27
|
+
rtl: false,
|
28
|
+
isAllowed: function(item, parent) { return true; }
|
29
|
+
},
|
30
|
+
|
31
|
+
_create: function() {
|
32
|
+
this.element.data('sortable', this.element.data('nestedSortable'));
|
33
|
+
|
34
|
+
if (!this.element.is(this.options.listType))
|
35
|
+
throw new Error('nestedSortable: Please check the listType option is set to your actual list type');
|
36
|
+
|
37
|
+
return $.ui.sortable.prototype._create.apply(this, arguments);
|
38
|
+
},
|
39
|
+
|
40
|
+
destroy: function() {
|
41
|
+
this.element
|
42
|
+
.removeData("nestedSortable")
|
43
|
+
.unbind(".nestedSortable");
|
44
|
+
return $.ui.sortable.prototype.destroy.apply(this, arguments);
|
45
|
+
},
|
46
|
+
|
47
|
+
_mouseDrag: function(event) {
|
48
|
+
|
49
|
+
//Compute the helpers position
|
50
|
+
this.position = this._generatePosition(event);
|
51
|
+
this.positionAbs = this._convertPositionTo("absolute");
|
52
|
+
|
53
|
+
if (!this.lastPositionAbs) {
|
54
|
+
this.lastPositionAbs = this.positionAbs;
|
55
|
+
}
|
56
|
+
|
57
|
+
var o = this.options;
|
58
|
+
|
59
|
+
//Do scrolling
|
60
|
+
if(this.options.scroll) {
|
61
|
+
var scrolled = false;
|
62
|
+
if(this.scrollParent[0] != document && this.scrollParent[0].tagName != 'HTML') {
|
63
|
+
|
64
|
+
if((this.overflowOffset.top + this.scrollParent[0].offsetHeight) - event.pageY < o.scrollSensitivity)
|
65
|
+
this.scrollParent[0].scrollTop = scrolled = this.scrollParent[0].scrollTop + o.scrollSpeed;
|
66
|
+
else if(event.pageY - this.overflowOffset.top < o.scrollSensitivity)
|
67
|
+
this.scrollParent[0].scrollTop = scrolled = this.scrollParent[0].scrollTop - o.scrollSpeed;
|
68
|
+
|
69
|
+
if((this.overflowOffset.left + this.scrollParent[0].offsetWidth) - event.pageX < o.scrollSensitivity)
|
70
|
+
this.scrollParent[0].scrollLeft = scrolled = this.scrollParent[0].scrollLeft + o.scrollSpeed;
|
71
|
+
else if(event.pageX - this.overflowOffset.left < o.scrollSensitivity)
|
72
|
+
this.scrollParent[0].scrollLeft = scrolled = this.scrollParent[0].scrollLeft - o.scrollSpeed;
|
73
|
+
|
74
|
+
} else {
|
75
|
+
|
76
|
+
if(event.pageY - $(document).scrollTop() < o.scrollSensitivity)
|
77
|
+
scrolled = $(document).scrollTop($(document).scrollTop() - o.scrollSpeed);
|
78
|
+
else if($(window).height() - (event.pageY - $(document).scrollTop()) < o.scrollSensitivity)
|
79
|
+
scrolled = $(document).scrollTop($(document).scrollTop() + o.scrollSpeed);
|
80
|
+
|
81
|
+
if(event.pageX - $(document).scrollLeft() < o.scrollSensitivity)
|
82
|
+
scrolled = $(document).scrollLeft($(document).scrollLeft() - o.scrollSpeed);
|
83
|
+
else if($(window).width() - (event.pageX - $(document).scrollLeft()) < o.scrollSensitivity)
|
84
|
+
scrolled = $(document).scrollLeft($(document).scrollLeft() + o.scrollSpeed);
|
85
|
+
|
86
|
+
}
|
87
|
+
|
88
|
+
if(scrolled !== false && $.ui.ddmanager && !o.dropBehaviour)
|
89
|
+
$.ui.ddmanager.prepareOffsets(this, event);
|
90
|
+
}
|
91
|
+
|
92
|
+
//Regenerate the absolute position used for position checks
|
93
|
+
this.positionAbs = this._convertPositionTo("absolute");
|
94
|
+
|
95
|
+
// Find the top offset before rearrangement,
|
96
|
+
var previousTopOffset = this.placeholder.offset().top;
|
97
|
+
|
98
|
+
//Set the helper position
|
99
|
+
if(!this.options.axis || this.options.axis != "y") this.helper[0].style.left = this.position.left+'px';
|
100
|
+
if(!this.options.axis || this.options.axis != "x") this.helper[0].style.top = this.position.top+'px';
|
101
|
+
|
102
|
+
//Rearrange
|
103
|
+
for (var i = this.items.length - 1; i >= 0; i--) {
|
104
|
+
|
105
|
+
//Cache variables and intersection, continue if no intersection
|
106
|
+
var item = this.items[i], itemElement = item.item[0], intersection = this._intersectsWithPointer(item);
|
107
|
+
if (!intersection) continue;
|
108
|
+
|
109
|
+
if(itemElement != this.currentItem[0] //cannot intersect with itself
|
110
|
+
&& this.placeholder[intersection == 1 ? "next" : "prev"]()[0] != itemElement //no useless actions that have been done before
|
111
|
+
&& !$.contains(this.placeholder[0], itemElement) //no action if the item moved is the parent of the item checked
|
112
|
+
&& (this.options.type == 'semi-dynamic' ? !$.contains(this.element[0], itemElement) : true)
|
113
|
+
//&& itemElement.parentNode == this.placeholder[0].parentNode // only rearrange items within the same container
|
114
|
+
) {
|
115
|
+
|
116
|
+
$(itemElement).mouseenter();
|
117
|
+
|
118
|
+
this.direction = intersection == 1 ? "down" : "up";
|
119
|
+
|
120
|
+
if (this.options.tolerance == "pointer" || this._intersectsWithSides(item)) {
|
121
|
+
$(itemElement).mouseleave();
|
122
|
+
this._rearrange(event, item);
|
123
|
+
} else {
|
124
|
+
break;
|
125
|
+
}
|
126
|
+
|
127
|
+
// Clear emtpy ul's/ol's
|
128
|
+
this._clearEmpty(itemElement);
|
129
|
+
|
130
|
+
this._trigger("change", event, this._uiHash());
|
131
|
+
break;
|
132
|
+
}
|
133
|
+
}
|
134
|
+
|
135
|
+
var parentItem = (this.placeholder[0].parentNode.parentNode &&
|
136
|
+
$(this.placeholder[0].parentNode.parentNode).closest('.ui-sortable').length)
|
137
|
+
? $(this.placeholder[0].parentNode.parentNode)
|
138
|
+
: null,
|
139
|
+
level = this._getLevel(this.placeholder),
|
140
|
+
childLevels = this._getChildLevels(this.helper);
|
141
|
+
|
142
|
+
// To find the previous sibling in the list, keep backtracking until we hit a valid list item.
|
143
|
+
var previousItem = this.placeholder[0].previousSibling ? $(this.placeholder[0].previousSibling) : null;
|
144
|
+
if (previousItem != null) {
|
145
|
+
while (previousItem[0].nodeName.toLowerCase() != 'li' || previousItem[0] == this.currentItem[0] || previousItem[0] == this.helper[0]) {
|
146
|
+
if (previousItem[0].previousSibling) {
|
147
|
+
previousItem = $(previousItem[0].previousSibling);
|
148
|
+
} else {
|
149
|
+
previousItem = null;
|
150
|
+
break;
|
151
|
+
}
|
152
|
+
}
|
153
|
+
}
|
154
|
+
|
155
|
+
// To find the next sibling in the list, keep stepping forward until we hit a valid list item.
|
156
|
+
var nextItem = this.placeholder[0].nextSibling ? $(this.placeholder[0].nextSibling) : null;
|
157
|
+
if (nextItem != null) {
|
158
|
+
while (nextItem[0].nodeName.toLowerCase() != 'li' || nextItem[0] == this.currentItem[0] || nextItem[0] == this.helper[0]) {
|
159
|
+
if (nextItem[0].nextSibling) {
|
160
|
+
nextItem = $(nextItem[0].nextSibling);
|
161
|
+
} else {
|
162
|
+
nextItem = null;
|
163
|
+
break;
|
164
|
+
}
|
165
|
+
}
|
166
|
+
}
|
167
|
+
|
168
|
+
var newList = document.createElement(o.listType);
|
169
|
+
|
170
|
+
this.beyondMaxLevels = 0;
|
171
|
+
|
172
|
+
// If the item is moved to the left, send it to its parent's level unless there are siblings below it.
|
173
|
+
if (parentItem != null && nextItem == null &&
|
174
|
+
(o.rtl && (this.positionAbs.left + this.helper.outerWidth() > parentItem.offset().left + parentItem.outerWidth()) ||
|
175
|
+
!o.rtl && (this.positionAbs.left < parentItem.offset().left))) {
|
176
|
+
parentItem.after(this.placeholder[0]);
|
177
|
+
this._clearEmpty(parentItem[0]);
|
178
|
+
this._trigger("change", event, this._uiHash());
|
179
|
+
}
|
180
|
+
// If the item is below a sibling and is moved to the right, make it a child of that sibling.
|
181
|
+
else if (previousItem != null &&
|
182
|
+
(o.rtl && (this.positionAbs.left + this.helper.outerWidth() < previousItem.offset().left + previousItem.outerWidth() - o.tabSize) ||
|
183
|
+
!o.rtl && (this.positionAbs.left > previousItem.offset().left + o.tabSize))) {
|
184
|
+
this._isAllowed(previousItem, level, level+childLevels+1);
|
185
|
+
if (!previousItem.children(o.listType).length) {
|
186
|
+
previousItem[0].appendChild(newList);
|
187
|
+
}
|
188
|
+
// If this item is being moved from the top, add it to the top of the list.
|
189
|
+
if (previousTopOffset && (previousTopOffset <= previousItem.offset().top)) {
|
190
|
+
previousItem.children(o.listType).prepend(this.placeholder);
|
191
|
+
}
|
192
|
+
// Otherwise, add it to the bottom of the list.
|
193
|
+
else {
|
194
|
+
previousItem.children(o.listType)[0].appendChild(this.placeholder[0]);
|
195
|
+
}
|
196
|
+
this._trigger("change", event, this._uiHash());
|
197
|
+
}
|
198
|
+
else {
|
199
|
+
this._isAllowed(parentItem, level, level+childLevels);
|
200
|
+
}
|
201
|
+
|
202
|
+
//Post events to containers
|
203
|
+
this._contactContainers(event);
|
204
|
+
|
205
|
+
//Interconnect with droppables
|
206
|
+
if($.ui.ddmanager) $.ui.ddmanager.drag(this, event);
|
207
|
+
|
208
|
+
//Call callbacks
|
209
|
+
this._trigger('sort', event, this._uiHash());
|
210
|
+
|
211
|
+
this.lastPositionAbs = this.positionAbs;
|
212
|
+
return false;
|
213
|
+
|
214
|
+
},
|
215
|
+
|
216
|
+
_mouseStop: function(event, noPropagation) {
|
217
|
+
|
218
|
+
// If the item is in a position not allowed, send it back
|
219
|
+
if (this.beyondMaxLevels) {
|
220
|
+
|
221
|
+
this.placeholder.removeClass(this.options.errorClass);
|
222
|
+
|
223
|
+
if (this.domPosition.prev) {
|
224
|
+
$(this.domPosition.prev).after(this.placeholder);
|
225
|
+
} else {
|
226
|
+
$(this.domPosition.parent).prepend(this.placeholder);
|
227
|
+
}
|
228
|
+
|
229
|
+
this._trigger("revert", event, this._uiHash());
|
230
|
+
|
231
|
+
}
|
232
|
+
|
233
|
+
// Clean last empty ul/ol
|
234
|
+
for (var i = this.items.length - 1; i >= 0; i--) {
|
235
|
+
var item = this.items[i].item[0];
|
236
|
+
this._clearEmpty(item);
|
237
|
+
}
|
238
|
+
|
239
|
+
$.ui.sortable.prototype._mouseStop.apply(this, arguments);
|
240
|
+
|
241
|
+
},
|
242
|
+
|
243
|
+
serialize: function(options) {
|
244
|
+
|
245
|
+
var o = $.extend({}, this.options, options),
|
246
|
+
items = this._getItemsAsjQuery(o && o.connected),
|
247
|
+
str = [];
|
248
|
+
|
249
|
+
$(items).each(function() {
|
250
|
+
var res = ($(o.item || this).attr(o.attribute || 'id') || '')
|
251
|
+
.match(o.expression || (/(.+)[-=_](.+)/)),
|
252
|
+
pid = ($(o.item || this).parent(o.listType)
|
253
|
+
.parent(o.items)
|
254
|
+
.attr(o.attribute || 'id') || '')
|
255
|
+
.match(o.expression || (/(.+)[-=_](.+)/));
|
256
|
+
|
257
|
+
if (res) {
|
258
|
+
str.push(((o.key || res[1]) + '[' + (o.key && o.expression ? res[1] : res[2]) + ']')
|
259
|
+
+ '='
|
260
|
+
+ (pid ? (o.key && o.expression ? pid[1] : pid[2]) : o.rootID));
|
261
|
+
}
|
262
|
+
});
|
263
|
+
|
264
|
+
if(!str.length && o.key) {
|
265
|
+
str.push(o.key + '=');
|
266
|
+
}
|
267
|
+
|
268
|
+
return str.join('&');
|
269
|
+
|
270
|
+
},
|
271
|
+
|
272
|
+
toHierarchy: function(options) {
|
273
|
+
|
274
|
+
var o = $.extend({}, this.options, options),
|
275
|
+
sDepth = o.startDepthCount || 0,
|
276
|
+
ret = [];
|
277
|
+
|
278
|
+
$(this.element).children(o.items).each(function () {
|
279
|
+
var level = _recursiveItems(this);
|
280
|
+
ret.push(level);
|
281
|
+
});
|
282
|
+
|
283
|
+
return ret;
|
284
|
+
|
285
|
+
function _recursiveItems(item) {
|
286
|
+
var id = ($(item).attr(o.attribute || 'id') || '').match(o.expression || (/(.+)[-=_](.+)/));
|
287
|
+
if (id) {
|
288
|
+
var currentItem = {"id" : id[2]};
|
289
|
+
if ($(item).children(o.listType).children(o.items).length > 0) {
|
290
|
+
currentItem.children = [];
|
291
|
+
$(item).children(o.listType).children(o.items).each(function() {
|
292
|
+
var level = _recursiveItems(this);
|
293
|
+
currentItem.children.push(level);
|
294
|
+
});
|
295
|
+
}
|
296
|
+
return currentItem;
|
297
|
+
}
|
298
|
+
}
|
299
|
+
},
|
300
|
+
|
301
|
+
toArray: function(options) {
|
302
|
+
|
303
|
+
var o = $.extend({}, this.options, options),
|
304
|
+
sDepth = o.startDepthCount || 0,
|
305
|
+
ret = [],
|
306
|
+
left = 2;
|
307
|
+
|
308
|
+
ret.push({
|
309
|
+
"item_id": o.rootID,
|
310
|
+
"parent_id": 'none',
|
311
|
+
"depth": sDepth,
|
312
|
+
"left": '1',
|
313
|
+
"right": ($(o.items, this.element).length + 1) * 2
|
314
|
+
});
|
315
|
+
|
316
|
+
$(this.element).children(o.items).each(function () {
|
317
|
+
left = _recursiveArray(this, sDepth + 1, left);
|
318
|
+
});
|
319
|
+
|
320
|
+
ret = ret.sort(function(a,b){ return (a.left - b.left); });
|
321
|
+
|
322
|
+
return ret;
|
323
|
+
|
324
|
+
function _recursiveArray(item, depth, left) {
|
325
|
+
|
326
|
+
var right = left + 1,
|
327
|
+
id,
|
328
|
+
pid;
|
329
|
+
|
330
|
+
if ($(item).children(o.listType).children(o.items).length > 0) {
|
331
|
+
depth ++;
|
332
|
+
$(item).children(o.listType).children(o.items).each(function () {
|
333
|
+
right = _recursiveArray($(this), depth, right);
|
334
|
+
});
|
335
|
+
depth --;
|
336
|
+
}
|
337
|
+
|
338
|
+
id = ($(item).attr(o.attribute || 'id')).match(o.expression || (/(.+)[-=_](.+)/));
|
339
|
+
|
340
|
+
if (depth === sDepth + 1) {
|
341
|
+
pid = o.rootID;
|
342
|
+
} else {
|
343
|
+
var parentItem = ($(item).parent(o.listType)
|
344
|
+
.parent(o.items)
|
345
|
+
.attr(o.attribute || 'id'))
|
346
|
+
.match(o.expression || (/(.+)[-=_](.+)/));
|
347
|
+
pid = parentItem[2];
|
348
|
+
}
|
349
|
+
|
350
|
+
if (id) {
|
351
|
+
ret.push({"item_id": id[2], "parent_id": pid, "depth": depth, "left": left, "right": right});
|
352
|
+
}
|
353
|
+
|
354
|
+
left = right + 1;
|
355
|
+
return left;
|
356
|
+
}
|
357
|
+
|
358
|
+
},
|
359
|
+
|
360
|
+
_clearEmpty: function(item) {
|
361
|
+
|
362
|
+
var emptyList = $(item).children(this.options.listType);
|
363
|
+
if (emptyList.length && !emptyList.children().length && !this.options.doNotClear) {
|
364
|
+
emptyList.remove();
|
365
|
+
}
|
366
|
+
|
367
|
+
},
|
368
|
+
|
369
|
+
_getLevel: function(item) {
|
370
|
+
|
371
|
+
var level = 1;
|
372
|
+
|
373
|
+
if (this.options.listType) {
|
374
|
+
var list = item.closest(this.options.listType);
|
375
|
+
while (list && list.length > 0 &&
|
376
|
+
!list.is('.ui-sortable')) {
|
377
|
+
level++;
|
378
|
+
list = list.parent().closest(this.options.listType);
|
379
|
+
}
|
380
|
+
}
|
381
|
+
|
382
|
+
return level;
|
383
|
+
},
|
384
|
+
|
385
|
+
_getChildLevels: function(parent, depth) {
|
386
|
+
var self = this,
|
387
|
+
o = this.options,
|
388
|
+
result = 0;
|
389
|
+
depth = depth || 0;
|
390
|
+
|
391
|
+
$(parent).children(o.listType).children(o.items).each(function (index, child) {
|
392
|
+
result = Math.max(self._getChildLevels(child, depth + 1), result);
|
393
|
+
});
|
394
|
+
|
395
|
+
return depth ? result + 1 : result;
|
396
|
+
},
|
397
|
+
|
398
|
+
_isAllowed: function(parentItem, level, levels) {
|
399
|
+
var o = this.options,
|
400
|
+
isRoot = $(this.domPosition.parent).hasClass('ui-sortable') ? true : false,
|
401
|
+
maxLevels = this.placeholder.closest('.ui-sortable').nestedSortable('option', 'maxLevels'); // this takes into account the maxLevels set to the recipient list
|
402
|
+
|
403
|
+
// Is the root protected?
|
404
|
+
// Are we trying to nest under a no-nest?
|
405
|
+
// Are we nesting too deep?
|
406
|
+
if (!o.isAllowed(this.currentItem, parentItem) ||
|
407
|
+
parentItem && parentItem.hasClass(o.disableNesting) ||
|
408
|
+
o.protectRoot && (parentItem == null && !isRoot || isRoot && level > 1)) {
|
409
|
+
this.placeholder.addClass(o.errorClass);
|
410
|
+
if (maxLevels < levels && maxLevels != 0) {
|
411
|
+
this.beyondMaxLevels = levels - maxLevels;
|
412
|
+
} else {
|
413
|
+
this.beyondMaxLevels = 1;
|
414
|
+
}
|
415
|
+
} else {
|
416
|
+
if (maxLevels < levels && maxLevels != 0) {
|
417
|
+
this.placeholder.addClass(o.errorClass);
|
418
|
+
this.beyondMaxLevels = levels - maxLevels;
|
419
|
+
} else {
|
420
|
+
this.placeholder.removeClass(o.errorClass);
|
421
|
+
this.beyondMaxLevels = 0;
|
422
|
+
}
|
423
|
+
}
|
424
|
+
}
|
425
|
+
|
426
|
+
}));
|
427
|
+
|
428
|
+
$.mjs.nestedSortable.prototype.options = $.extend({}, $.ui.sortable.prototype.options, $.mjs.nestedSortable.prototype.options);
|
429
|
+
})(jQuery);
|
@@ -0,0 +1,45 @@
|
|
1
|
+
#= require jquery
|
2
|
+
#= require jquery_ujs
|
3
|
+
#= require jquery.ui.sortable
|
4
|
+
#= require jquery.mjs.nestedSortable
|
5
|
+
|
6
|
+
window.rails_admin_nested_set = (tree_config) ->
|
7
|
+
show_flash = (data)->
|
8
|
+
$flash = $('<div>')
|
9
|
+
.addClass('nestable-flash alert')
|
10
|
+
.append($('<button>').addClass('close').data('dismiss', 'alert').html('×'))
|
11
|
+
.append($('<span>').addClass('body').html(data))
|
12
|
+
$('#rails_admin_nestable').append($flash)
|
13
|
+
$flash.fadeIn(200).delay(2000).fadeOut 200, -> $(this).remove()
|
14
|
+
|
15
|
+
$ ->
|
16
|
+
$("#" + tree_config["id"]).nestedSortable
|
17
|
+
forcePlaceholderSize: true
|
18
|
+
# handle: 'i.dd-handle',
|
19
|
+
helper: "clone"
|
20
|
+
items: "li"
|
21
|
+
maxLevels: tree_config["max_depth"]
|
22
|
+
opacity: .6
|
23
|
+
placeholder: "dd-placeholder"
|
24
|
+
tabSize: 25
|
25
|
+
# tolerance: 'pointer',
|
26
|
+
# toleranceElement: '.dd3-content',
|
27
|
+
update: (event, ui) ->
|
28
|
+
$.ajax
|
29
|
+
type: "POST"
|
30
|
+
dataType: "html"
|
31
|
+
url: tree_config["update_url"]
|
32
|
+
data:
|
33
|
+
id: ui.item.data("id")
|
34
|
+
parent_id: ui.item.parent().parent().data("id")
|
35
|
+
prev_id: ui.item.prev().data("id")
|
36
|
+
next_id: ui.item.next().data("id")
|
37
|
+
|
38
|
+
error: (xhr, status, error) ->
|
39
|
+
# alert error
|
40
|
+
# window.location.reload()
|
41
|
+
show_flash('Nested Set: fatal error')
|
42
|
+
|
43
|
+
success: (data) ->
|
44
|
+
console.log(data)
|
45
|
+
show_flash(data)
|
@@ -0,0 +1,305 @@
|
|
1
|
+
/**
|
2
|
+
* jQuery Nestable
|
3
|
+
*/
|
4
|
+
|
5
|
+
.dd {
|
6
|
+
position: relative;
|
7
|
+
display: block;
|
8
|
+
margin: 0;
|
9
|
+
padding: 0;
|
10
|
+
/* max-width: 600px; */
|
11
|
+
list-style: none;
|
12
|
+
font-size: 13px;
|
13
|
+
line-height: 20px;
|
14
|
+
}
|
15
|
+
|
16
|
+
.dd-list, .dd-list ol {
|
17
|
+
display: block;
|
18
|
+
position: relative;
|
19
|
+
margin: 0;
|
20
|
+
padding: 0;
|
21
|
+
list-style: none;
|
22
|
+
}
|
23
|
+
|
24
|
+
.dd-list ol {
|
25
|
+
padding-left: 30px;
|
26
|
+
}
|
27
|
+
|
28
|
+
.dd-collapsed ol {
|
29
|
+
display: none;
|
30
|
+
}
|
31
|
+
|
32
|
+
.dd-item,
|
33
|
+
.dd-empty,
|
34
|
+
.dd-placeholder {
|
35
|
+
display: block;
|
36
|
+
position: relative;
|
37
|
+
margin: 0;
|
38
|
+
padding: 0;
|
39
|
+
min-height: 20px;
|
40
|
+
font-size: 13px;
|
41
|
+
line-height: 20px;
|
42
|
+
}
|
43
|
+
|
44
|
+
.dd-handle {
|
45
|
+
display: block;
|
46
|
+
height: 30px;
|
47
|
+
margin: 5px 0;
|
48
|
+
padding: 5px 10px;
|
49
|
+
color: #333;
|
50
|
+
text-decoration: none;
|
51
|
+
font-weight: bold;
|
52
|
+
border: 1px solid #ccc;
|
53
|
+
background: #fafafa;
|
54
|
+
background: -webkit-linear-gradient(top, #fafafa 0%, #eee 100%);
|
55
|
+
background: -moz-linear-gradient(top, #fafafa 0%, #eee 100%);
|
56
|
+
background: linear-gradient(top, #fafafa 0%, #eee 100%);
|
57
|
+
-webkit-border-radius: 3px;
|
58
|
+
border-radius: 3px;
|
59
|
+
box-sizing: border-box;
|
60
|
+
-moz-box-sizing: border-box;
|
61
|
+
}
|
62
|
+
|
63
|
+
.dd-handle:hover {
|
64
|
+
color: #2ea8e5;
|
65
|
+
background: #fff;
|
66
|
+
}
|
67
|
+
|
68
|
+
.dd-item > button {
|
69
|
+
display: block;
|
70
|
+
position: relative;
|
71
|
+
cursor: pointer;
|
72
|
+
float: left;
|
73
|
+
width: 25px;
|
74
|
+
height: 20px;
|
75
|
+
margin: 5px 0;
|
76
|
+
padding: 0;
|
77
|
+
text-indent: 100%;
|
78
|
+
white-space: nowrap;
|
79
|
+
overflow: hidden;
|
80
|
+
border: 0;
|
81
|
+
background: transparent;
|
82
|
+
font-size: 12px;
|
83
|
+
line-height: 1;
|
84
|
+
text-align: center;
|
85
|
+
font-weight: bold;
|
86
|
+
}
|
87
|
+
|
88
|
+
.dd-item > button:before {
|
89
|
+
content: '+';
|
90
|
+
display: block;
|
91
|
+
position: absolute;
|
92
|
+
width: 100%;
|
93
|
+
text-align: center;
|
94
|
+
text-indent: 0;
|
95
|
+
}
|
96
|
+
|
97
|
+
.dd-item > button[data-action="collapse"]:before {
|
98
|
+
content: '-';
|
99
|
+
}
|
100
|
+
|
101
|
+
.dd-placeholder, .dd-empty {
|
102
|
+
margin: 5px 0;
|
103
|
+
padding: 0;
|
104
|
+
min-height: 30px;
|
105
|
+
background: #f2fbff;
|
106
|
+
border: 1px dashed #b6bcbf;
|
107
|
+
box-sizing: border-box;
|
108
|
+
-moz-box-sizing: border-box;
|
109
|
+
}
|
110
|
+
|
111
|
+
.dd-empty {
|
112
|
+
border: 1px dashed #bbb;
|
113
|
+
min-height: 100px;
|
114
|
+
background-color: #e5e5e5;
|
115
|
+
background-image: -webkit-linear-gradient(45deg, #fff 25%, transparent 25%, transparent 75%, #fff 75%, #fff), -webkit-linear-gradient(45deg, #fff 25%, transparent 25%, transparent 75%, #fff 75%, #fff);
|
116
|
+
background-image: -moz-linear-gradient(45deg, #fff 25%, transparent 25%, transparent 75%, #fff 75%, #fff), -moz-linear-gradient(45deg, #fff 25%, transparent 25%, transparent 75%, #fff 75%, #fff);
|
117
|
+
background-image: linear-gradient(45deg, #fff 25%, transparent 25%, transparent 75%, #fff 75%, #fff), linear-gradient(45deg, #fff 25%, transparent 25%, transparent 75%, #fff 75%, #fff);
|
118
|
+
background-size: 60px 60px;
|
119
|
+
background-position: 0 0, 30px 30px;
|
120
|
+
}
|
121
|
+
|
122
|
+
.dd-dragel {
|
123
|
+
position: absolute;
|
124
|
+
pointer-events: none;
|
125
|
+
z-index: 9999;
|
126
|
+
}
|
127
|
+
|
128
|
+
.dd-dragel > .dd-item .dd-handle {
|
129
|
+
margin-top: 0;
|
130
|
+
}
|
131
|
+
|
132
|
+
.dd-dragel .dd-handle {
|
133
|
+
-webkit-box-shadow: 2px 4px 6px 0 rgba(0, 0, 0, .1);
|
134
|
+
box-shadow: 2px 4px 6px 0 rgba(0, 0, 0, .1);
|
135
|
+
}
|
136
|
+
|
137
|
+
/**
|
138
|
+
* Nestable Extras
|
139
|
+
*/
|
140
|
+
|
141
|
+
.nestable-lists {
|
142
|
+
display: block;
|
143
|
+
clear: both;
|
144
|
+
padding: 30px 0;
|
145
|
+
width: 100%;
|
146
|
+
border: 0;
|
147
|
+
border-top: 2px solid #ddd;
|
148
|
+
border-bottom: 2px solid #ddd;
|
149
|
+
}
|
150
|
+
|
151
|
+
#nestable-menu {
|
152
|
+
padding: 0;
|
153
|
+
margin: 20px 0;
|
154
|
+
}
|
155
|
+
|
156
|
+
#nestable-output, #nestable2-output {
|
157
|
+
width: 100%;
|
158
|
+
height: 7em;
|
159
|
+
font-size: 0.75em;
|
160
|
+
line-height: 1.333333em;
|
161
|
+
font-family: Consolas, monospace;
|
162
|
+
padding: 5px;
|
163
|
+
box-sizing: border-box;
|
164
|
+
-moz-box-sizing: border-box;
|
165
|
+
}
|
166
|
+
|
167
|
+
#nestable2 .dd-handle {
|
168
|
+
color: #fff;
|
169
|
+
border: 1px solid #999;
|
170
|
+
background: #bbb;
|
171
|
+
background: -webkit-linear-gradient(top, #bbb 0%, #999 100%);
|
172
|
+
background: -moz-linear-gradient(top, #bbb 0%, #999 100%);
|
173
|
+
background: linear-gradient(top, #bbb 0%, #999 100%);
|
174
|
+
}
|
175
|
+
|
176
|
+
#nestable2 .dd-handle:hover {
|
177
|
+
background: #bbb;
|
178
|
+
}
|
179
|
+
|
180
|
+
#nestable2 .dd-item > button:before {
|
181
|
+
color: #fff;
|
182
|
+
}
|
183
|
+
|
184
|
+
@media only screen and (min-width: 700px) {
|
185
|
+
|
186
|
+
.dd {
|
187
|
+
float: none;
|
188
|
+
width: auto;
|
189
|
+
}
|
190
|
+
.dd + .dd {
|
191
|
+
margin-left: 2%;
|
192
|
+
}
|
193
|
+
|
194
|
+
}
|
195
|
+
|
196
|
+
.dd-hover > .dd-handle {
|
197
|
+
background: #2ea8e5 !important;
|
198
|
+
}
|
199
|
+
|
200
|
+
/**
|
201
|
+
* Nestable Draggable Handles
|
202
|
+
*/
|
203
|
+
|
204
|
+
.dd3-content {
|
205
|
+
display: block;
|
206
|
+
height: 30px;
|
207
|
+
margin: 5px 0;
|
208
|
+
padding: 5px 10px 5px 40px;
|
209
|
+
color: #333;
|
210
|
+
text-decoration: none;
|
211
|
+
font-weight: bold;
|
212
|
+
border: 1px solid #ccc;
|
213
|
+
background: #fafafa;
|
214
|
+
background: -webkit-linear-gradient(top, #fafafa 0%, #eee 100%);
|
215
|
+
background: -moz-linear-gradient(top, #fafafa 0%, #eee 100%);
|
216
|
+
background: linear-gradient(top, #fafafa 0%, #eee 100%);
|
217
|
+
-webkit-border-radius: 3px;
|
218
|
+
border-radius: 3px;
|
219
|
+
box-sizing: border-box;
|
220
|
+
-moz-box-sizing: border-box;
|
221
|
+
}
|
222
|
+
|
223
|
+
.dd3-content:hover {
|
224
|
+
color: #2ea8e5;
|
225
|
+
background: #fff;
|
226
|
+
}
|
227
|
+
|
228
|
+
.dd-dragel > .dd3-item > .dd3-content {
|
229
|
+
margin: 0;
|
230
|
+
}
|
231
|
+
|
232
|
+
.dd3-item > button {
|
233
|
+
margin-left: 30px;
|
234
|
+
}
|
235
|
+
|
236
|
+
.dd3-handle {
|
237
|
+
position: absolute;
|
238
|
+
margin: 0;
|
239
|
+
left: 0;
|
240
|
+
top: 0;
|
241
|
+
cursor: pointer;
|
242
|
+
width: 30px;
|
243
|
+
text-indent: 100px;
|
244
|
+
white-space: nowrap;
|
245
|
+
overflow: hidden;
|
246
|
+
border: 1px solid #aaa;
|
247
|
+
background: #ddd;
|
248
|
+
background: -webkit-linear-gradient(top, #ddd 0%, #bbb 100%);
|
249
|
+
background: -moz-linear-gradient(top, #ddd 0%, #bbb 100%);
|
250
|
+
background: linear-gradient(top, #ddd 0%, #bbb 100%);
|
251
|
+
border-top-right-radius: 0;
|
252
|
+
border-bottom-right-radius: 0;
|
253
|
+
}
|
254
|
+
|
255
|
+
.dd3-handle:before {
|
256
|
+
content: '≡';
|
257
|
+
display: block;
|
258
|
+
position: absolute;
|
259
|
+
left: 0;
|
260
|
+
top: 3px;
|
261
|
+
width: 100%;
|
262
|
+
text-align: center;
|
263
|
+
text-indent: 0;
|
264
|
+
color: #fff;
|
265
|
+
font-size: 20px;
|
266
|
+
font-weight: normal;
|
267
|
+
}
|
268
|
+
|
269
|
+
.dd3-handle:hover {
|
270
|
+
background: #ddd;
|
271
|
+
}
|
272
|
+
|
273
|
+
.dd3-content .links {
|
274
|
+
width: auto;
|
275
|
+
}
|
276
|
+
|
277
|
+
.dd3-content ul.inline.actions {
|
278
|
+
margin: 0;
|
279
|
+
padding: 0;
|
280
|
+
list-style: none;
|
281
|
+
display: block
|
282
|
+
}
|
283
|
+
|
284
|
+
.dd3-content ul.inline.actions li a {
|
285
|
+
width: 14px;
|
286
|
+
height: 16px;
|
287
|
+
text-decoration: none;
|
288
|
+
}
|
289
|
+
|
290
|
+
.dd3-content ul.inline.actions li a:hover {
|
291
|
+
text-decoration: none;
|
292
|
+
}
|
293
|
+
|
294
|
+
#rails_admin_nestable {
|
295
|
+
position: relative;
|
296
|
+
}
|
297
|
+
|
298
|
+
.nestable-flash {
|
299
|
+
position: fixed;
|
300
|
+
top: 80px;
|
301
|
+
width: auto;
|
302
|
+
right: 20px;
|
303
|
+
display: none;
|
304
|
+
min-width: 150px;
|
305
|
+
}
|
@@ -0,0 +1,20 @@
|
|
1
|
+
module RailsAdminNestedSet
|
2
|
+
class Configuration
|
3
|
+
def initialize(abstract_model)
|
4
|
+
@abstract_model = abstract_model
|
5
|
+
end
|
6
|
+
|
7
|
+
def options
|
8
|
+
@options ||= {
|
9
|
+
max_depth: 1,
|
10
|
+
}.merge(config)
|
11
|
+
|
12
|
+
@options || {}
|
13
|
+
end
|
14
|
+
|
15
|
+
protected
|
16
|
+
def config
|
17
|
+
::RailsAdmin::Config.model(@abstract_model.model).nested_set
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,45 @@
|
|
1
|
+
module RailsAdminNestedSet
|
2
|
+
module Helper
|
3
|
+
def rails_admin_nested_set(tree, opts= {})
|
4
|
+
tree = tree.to_a.sort_by { |m| m.lft }
|
5
|
+
roots = tree.select{|elem| elem.parent_id.nil?}
|
6
|
+
id = "ns_#{rand(100_000_000..999_999_999)}"
|
7
|
+
content = content_tag(:ol, rails_admin_nested_set_builder(roots, tree), id: id, class: 'dd-list')
|
8
|
+
js = "rails_admin_nested_set({id: '#{id}', max_depth: #{max_depth}, update_url: '#{nested_set_path(model_name: @abstract_model)}'});"
|
9
|
+
content + content_tag(:script, js.html_safe, type: 'text/javascript')
|
10
|
+
end
|
11
|
+
|
12
|
+
|
13
|
+
def rails_admin_nested_set_builder(nodes, tree)
|
14
|
+
nodes.map do |node|
|
15
|
+
li_classes = 'dd-item dd3-item'
|
16
|
+
|
17
|
+
content_tag :li, class: li_classes, :'data-id' => node.id do
|
18
|
+
|
19
|
+
output = content_tag :div, 'drag', class: 'dd-handle dd3-handle'
|
20
|
+
output+= content_tag :div, class: 'dd3-content' do
|
21
|
+
content = link_to @model_config.with(object: node).object_label, edit_path(@abstract_model, node.id)
|
22
|
+
content + content_tag(:div, action_links(node), class: 'pull-right links')
|
23
|
+
end
|
24
|
+
|
25
|
+
children = tree.select{|elem| elem.parent_id == node.id}
|
26
|
+
if children.any?
|
27
|
+
output += content_tag :ol, rails_admin_nested_set_builder(children, tree), class: 'dd-list'
|
28
|
+
end
|
29
|
+
|
30
|
+
output
|
31
|
+
end
|
32
|
+
end.join.html_safe
|
33
|
+
end
|
34
|
+
|
35
|
+
def max_depth
|
36
|
+
@nested_set_conf.options[:max_depth] || '0'
|
37
|
+
end
|
38
|
+
|
39
|
+
def action_links(model)
|
40
|
+
content_tag :ul, class: 'inline actions' do
|
41
|
+
menu_for :member, @abstract_model, model, true
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
@@ -0,0 +1,76 @@
|
|
1
|
+
module RailsAdmin
|
2
|
+
module Config
|
3
|
+
module Actions
|
4
|
+
class NestedSet < Base
|
5
|
+
RailsAdmin::Config::Actions.register(self)
|
6
|
+
|
7
|
+
# Is the action acting on the root level (Example: /admin/contact)
|
8
|
+
register_instance_option :root? do
|
9
|
+
false
|
10
|
+
end
|
11
|
+
|
12
|
+
register_instance_option :collection? do
|
13
|
+
true
|
14
|
+
end
|
15
|
+
|
16
|
+
# Is the action on an object scope (Example: /admin/team/1/edit)
|
17
|
+
register_instance_option :member? do
|
18
|
+
false
|
19
|
+
end
|
20
|
+
|
21
|
+
register_instance_option :controller do
|
22
|
+
Proc.new do |klass|
|
23
|
+
@nested_set_conf = ::RailsAdminNestedSet::Configuration.new @abstract_model
|
24
|
+
|
25
|
+
if params['id'].present?
|
26
|
+
p params
|
27
|
+
begin
|
28
|
+
id = params[:id].to_s
|
29
|
+
parent_id = params[:parent_id].to_s
|
30
|
+
prev_id = params[:prev_id].to_s
|
31
|
+
next_id = params[:next_id].to_s
|
32
|
+
|
33
|
+
if id.empty?
|
34
|
+
return render text: 'Nested set UI error: node id not defined', status: 500
|
35
|
+
elsif parent_id.empty? && prev_id.empty? && next_id.empty?
|
36
|
+
return render text: 'Nested set UI error: not defined where to move node', status: 500
|
37
|
+
end
|
38
|
+
|
39
|
+
|
40
|
+
obj = @abstract_model.model.find(id)
|
41
|
+
if prev_id.empty? && next_id.empty?
|
42
|
+
p "move_to_child_of #{parent_id}"
|
43
|
+
obj.move_to_child_of @abstract_model.model.find(parent_id)
|
44
|
+
elsif !prev_id.empty?
|
45
|
+
p "move_to_right_of #{prev_id}"
|
46
|
+
obj.move_to_right_of @abstract_model.model.find(prev_id)
|
47
|
+
elsif !next_id.empty?
|
48
|
+
p "move_to_left_of #{next_id}"
|
49
|
+
obj.move_to_left_of @abstract_model.model.find(next_id)
|
50
|
+
end
|
51
|
+
|
52
|
+
message = "<strong>#{I18n.t('admin.actions.nestable.success')}!</strong>"
|
53
|
+
rescue Exception => e
|
54
|
+
@abstract_model.model.rebuild!
|
55
|
+
message = "<strong>#{I18n.t('admin.actions.nestable.error')}</strong>: #{e}"
|
56
|
+
end
|
57
|
+
|
58
|
+
render text: message
|
59
|
+
else
|
60
|
+
@nodes = list_entries(@model_config, :index, nil, nil).order('lft')
|
61
|
+
render action: @action.template_name
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
register_instance_option :link_icon do
|
67
|
+
'icon-move'
|
68
|
+
end
|
69
|
+
|
70
|
+
register_instance_option :http_methods do
|
71
|
+
[:get, :post]
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
module RailsAdminNestedSet
|
2
|
+
|
3
|
+
end
|
4
|
+
|
5
|
+
require "rails_admin_nested_set/version"
|
6
|
+
require 'rails_admin_nested_set/engine'
|
7
|
+
|
8
|
+
require 'rails_admin/config/actions'
|
9
|
+
require 'rails_admin/config/model'
|
10
|
+
|
11
|
+
require 'rails_admin_nested_set/configuration'
|
12
|
+
require 'rails_admin_nested_set/nested_set'
|
13
|
+
require 'rails_admin_nested_set/model'
|
14
|
+
require 'rails_admin_nested_set/helper'
|
@@ -0,0 +1,23 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require 'rails_admin_nested_set/version'
|
5
|
+
|
6
|
+
Gem::Specification.new do |gem|
|
7
|
+
gem.name = "rails_admin_nested_set"
|
8
|
+
gem.version = RailsAdminNestedSet::VERSION
|
9
|
+
gem.authors = ["Gleb Tv"]
|
10
|
+
gem.email = ["glebtv@gmail.com"]
|
11
|
+
gem.description = %q{Rails admin nested set}
|
12
|
+
gem.summary = %q{Interface for editing a nested set for rails_admin}
|
13
|
+
gem.homepage = "https://github.com/rs-pro/rails_admin_nested_set"
|
14
|
+
|
15
|
+
gem.add_dependency "rails", ">= 3.1"
|
16
|
+
gem.add_dependency "jquery-rails"
|
17
|
+
gem.add_dependency "jquery-ui-rails"
|
18
|
+
|
19
|
+
gem.files = `git ls-files`.split($/)
|
20
|
+
gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
|
21
|
+
gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
|
22
|
+
gem.require_paths = ["lib"]
|
23
|
+
end
|
metadata
ADDED
@@ -0,0 +1,112 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: rails_admin_nested_set
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1
|
5
|
+
prerelease:
|
6
|
+
platform: ruby
|
7
|
+
authors:
|
8
|
+
- Gleb Tv
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
date: 2013-03-14 00:00:00.000000000 Z
|
13
|
+
dependencies:
|
14
|
+
- !ruby/object:Gem::Dependency
|
15
|
+
name: rails
|
16
|
+
requirement: !ruby/object:Gem::Requirement
|
17
|
+
none: false
|
18
|
+
requirements:
|
19
|
+
- - ! '>='
|
20
|
+
- !ruby/object:Gem::Version
|
21
|
+
version: '3.1'
|
22
|
+
type: :runtime
|
23
|
+
prerelease: false
|
24
|
+
version_requirements: !ruby/object:Gem::Requirement
|
25
|
+
none: false
|
26
|
+
requirements:
|
27
|
+
- - ! '>='
|
28
|
+
- !ruby/object:Gem::Version
|
29
|
+
version: '3.1'
|
30
|
+
- !ruby/object:Gem::Dependency
|
31
|
+
name: jquery-rails
|
32
|
+
requirement: !ruby/object:Gem::Requirement
|
33
|
+
none: false
|
34
|
+
requirements:
|
35
|
+
- - ! '>='
|
36
|
+
- !ruby/object:Gem::Version
|
37
|
+
version: '0'
|
38
|
+
type: :runtime
|
39
|
+
prerelease: false
|
40
|
+
version_requirements: !ruby/object:Gem::Requirement
|
41
|
+
none: false
|
42
|
+
requirements:
|
43
|
+
- - ! '>='
|
44
|
+
- !ruby/object:Gem::Version
|
45
|
+
version: '0'
|
46
|
+
- !ruby/object:Gem::Dependency
|
47
|
+
name: jquery-ui-rails
|
48
|
+
requirement: !ruby/object:Gem::Requirement
|
49
|
+
none: false
|
50
|
+
requirements:
|
51
|
+
- - ! '>='
|
52
|
+
- !ruby/object:Gem::Version
|
53
|
+
version: '0'
|
54
|
+
type: :runtime
|
55
|
+
prerelease: false
|
56
|
+
version_requirements: !ruby/object:Gem::Requirement
|
57
|
+
none: false
|
58
|
+
requirements:
|
59
|
+
- - ! '>='
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '0'
|
62
|
+
description: Rails admin nested set
|
63
|
+
email:
|
64
|
+
- glebtv@gmail.com
|
65
|
+
executables: []
|
66
|
+
extensions: []
|
67
|
+
extra_rdoc_files: []
|
68
|
+
files:
|
69
|
+
- .gitignore
|
70
|
+
- Gemfile
|
71
|
+
- LICENSE.txt
|
72
|
+
- README.md
|
73
|
+
- Rakefile
|
74
|
+
- app/assets/javascripts/jquery.mjs.nestedSortable.js
|
75
|
+
- app/assets/javascripts/rails_admin_nested_set.js.coffee
|
76
|
+
- app/assets/stylesheets/rails_admin_nested_set.css.scss
|
77
|
+
- app/views/rails_admin/main/nested_set.html.haml
|
78
|
+
- config/locales/en.nested_set.yml
|
79
|
+
- config/locales/ru.nested_set.yml
|
80
|
+
- lib/rails_admin_nested_set.rb
|
81
|
+
- lib/rails_admin_nested_set/configuration.rb
|
82
|
+
- lib/rails_admin_nested_set/engine.rb
|
83
|
+
- lib/rails_admin_nested_set/helper.rb
|
84
|
+
- lib/rails_admin_nested_set/model.rb
|
85
|
+
- lib/rails_admin_nested_set/nested_set.rb
|
86
|
+
- lib/rails_admin_nested_set/version.rb
|
87
|
+
- rails_admin_nested_set.gemspec
|
88
|
+
homepage: https://github.com/rs-pro/rails_admin_nested_set
|
89
|
+
licenses: []
|
90
|
+
post_install_message:
|
91
|
+
rdoc_options: []
|
92
|
+
require_paths:
|
93
|
+
- lib
|
94
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
95
|
+
none: false
|
96
|
+
requirements:
|
97
|
+
- - ! '>='
|
98
|
+
- !ruby/object:Gem::Version
|
99
|
+
version: '0'
|
100
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
101
|
+
none: false
|
102
|
+
requirements:
|
103
|
+
- - ! '>='
|
104
|
+
- !ruby/object:Gem::Version
|
105
|
+
version: '0'
|
106
|
+
requirements: []
|
107
|
+
rubyforge_project:
|
108
|
+
rubygems_version: 1.8.24
|
109
|
+
signing_key:
|
110
|
+
specification_version: 3
|
111
|
+
summary: Interface for editing a nested set for rails_admin
|
112
|
+
test_files: []
|