sortable_nested_set 0.1.26 → 0.2.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.
@@ -1,6 +1,3 @@
1
- require 'sortable_element_for_nested_set'
2
- include SortableElementForNestedSet
3
-
4
1
  module SortableNestedSet
5
2
  def self.included(base)
6
3
  base.extend ClassMethods
@@ -8,146 +5,68 @@ module SortableNestedSet
8
5
 
9
6
  module ClassMethods
10
7
  def handles_sortable_nested_set
11
- handles_sorting_of_nested_set
12
-
13
8
  class_eval do
14
- define_method(:sns_category_controller) { controller_name }
15
9
  define_method(:sns_category_id_field) { "#{controller_name.singularize}_id" }
16
10
  define_method(:sns_category_class) { controller_name.classify.constantize }
17
- define_method(:sns_items_type) {sns_category_class.sns_items_type }
18
- define_method(:sns_item_class) {sns_items_type.to_s.classify.constantize }
11
+ define_method(:sns_items_type) { sns_category_class.sns_items_type }
12
+ define_method(:sns_item_class) { sns_items_type.to_s.classify.constantize }
19
13
  end
20
14
 
21
- include(SortableNestedSet::InstanceMethods)
15
+ include InstanceMethods
22
16
  end
23
17
  end
24
18
 
25
19
  module InstanceMethods
26
20
  def sns_add_subcategory
27
- redirect_to :controller => sns_category_controller, :action => :new, sns_category_id_field => params[:id]
21
+ redirect_to :action => :new, sns_category_id_field => params[:id]
28
22
  end
29
23
 
30
24
  def sns_add_item
31
25
  redirect_to :controller => sns_items_type, :action => :new, sns_category_id_field => params[:id]
32
26
  end
33
27
 
34
- def sns_sort_categories
35
- category = sns_category_class.find(params[:moved])
36
- new_pos = position_of(:moved).in_tree(:menu)
37
-
38
- # Ambiguous moves occur when a category is dropped outside the menu AND
39
- # a :root parameter is specified. In this case, move the element to the
40
- # :root category in the database and also put it at the end with javascript.
41
- ambiguous_move = nil
42
-
43
- begin
44
- category.transaction do
45
- if new_pos[:parent]
46
- category.move_to_child_of(new_pos[:parent])
47
- elsif params[:root].nil?
48
- category.move_to_root
49
- else
50
- root_id = params[:root].to_i
51
- ambiguous_move = params[:menu]['0']['id'] == params[:moved] ? :top : :bottom
52
-
53
- if ambiguous_move == :bottom
54
- category.move_to_child_of(params[:root])
55
- else
56
- category.move_to_left_of(sns_category_class.find(params[:root]).sns_subcategories.first)
57
- end
58
- end unless category.parent_id === new_pos[:parent]
28
+ def sns_sort
29
+ matches = params[:moved].match(/(\w+)_(\d+)$/)
30
+ moved_type = matches[1]
31
+ moved_id = matches[2].to_i
32
+ moved_parent = nil
33
+ tree = {} # category_id => [children of move_type]
59
34
 
60
- begin
61
- if new_pos[:move_to_right_of]
62
- category.move_to_right_of(new_pos[:move_to_right_of])
63
- else
64
- category.move_to_left_of(new_pos[:move_to_left_of])
65
- end
66
- rescue ActiveRecord::RecordNotFound
67
- # FIXME error raised when making first child
68
- end unless ambiguous_move
35
+ for node in params[:tree].scan(/(\w+)\[(\d+)\]=(\w+)/)
36
+ type = node[0]
37
+ next unless type == moved_type
69
38
 
70
- raise category.errors.full_messages.to_sentence unless category.valid?
71
- end
72
- rescue => err
73
- render :update do |page|
74
- page.alert err.message
75
- page.reload # TODO return to original location with javascript
76
- end
77
- else
78
- render :update do |page|
79
- page.assign :categoryElement, page.literal("$('category_#{category.id}')")
80
-
81
- if ambiguous_move
82
- page.assign :rootElement, page.literal("$('subcategories_#{root_id}')")
83
- page.remove "category_#{category.id}"
84
-
85
- # there is a placeholder at index 0
86
- page << (ambiguous_move == :top ?
87
- 'rootElement.insertBefore(categoryElement, rootElement.childElements()[1])' :
88
- 'rootElement.insert(categoryElement)')
39
+ id = node[1].to_i
40
+ parent = (node[2] == 'root') ? nil : node[2].to_i
89
41
 
90
- page.visual_effect 'highlight', "category_#{category.id}"
91
- else
92
- page.assign :elderSibling, page.literal('categoryElement.nextElementSibling')
93
- page.assign :parent, page.literal('categoryElement.parentNode')
42
+ moved_parent = parent if id == moved_id
94
43
 
95
- page << <<EOF
96
- if (elderSibling && elderSibling.hasClassName('placeholder')) {
97
- elderSibling.remove();
98
- parent.insertBefore(elderSibling, categoryElement);
99
- }
100
-
101
- /* WORKAROUND DEFECT: after dragdrop, z-index not abided by */
102
- categoryElement.style.removeProperty('z-index');
103
- EOF
104
- end
105
- end
44
+ tree[parent] ||= []
45
+ tree[parent] << id
106
46
  end
107
- end
108
47
 
109
- def sns_sort_items
110
- container = params.find { |key, val| key =~ /items_\d+/ }
111
-
112
- params[container.first].each_with_index do |id, index|
113
- category_id = container.first.match(/items_(\d+)/)[1]
114
- sns_item_class.update(id, sns_category_id_field => category_id, :position => index + 1)
115
- end if container
116
-
117
- render :update do |page|
118
- id = "item_#{params[:moved]}"
119
- page << "$('#{id}').style.removeProperty('z-index')"
48
+ if moved_type == sns_items_type.to_s.singularize
49
+ item = sns_item_class.find(moved_id)
50
+ item.remove_from_list
51
+ item.update_attribute(sns_category_id_field, moved_parent)
52
+ item.insert_at(tree[moved_parent].index(moved_id))
53
+ else
54
+ moved_type.classify.constantize.find(moved_id).move(moved_parent, tree[moved_parent])
120
55
  end
121
- end
122
56
 
123
- def sns_toggle
124
- render :update do |page|
125
- page["items_#{params[:id]}"].toggle
126
- page["subcategories_#{params[:id]}"].toggle
127
- page.replace_html "toggle_#{params[:id]}",
128
- :partial => 'sortable_nested_set/toggle',
129
- :locals => { :category_type => params[:category_type],
130
- :id => params[:id],
131
- :state => (params[:state] == :open ? :closed : :open) }
132
- end
57
+ render :nothing => true
133
58
  end
134
59
 
135
60
  def sns_destroy_category
136
61
  @category = sns_category_class.find(params[:id])
137
62
  @category.destroy
138
-
139
- render :update do |page|
140
- page["category_#{@category.id}"].remove
141
- end
63
+ render 'sortable_nested_set/sns_destroy_category'
142
64
  end
143
65
 
144
66
  def sns_destroy_item
145
67
  @item = sns_item_class.find(params[:id])
146
68
  @item.destroy
147
-
148
- render :update do |page|
149
- page["item_#{@item.id}"].remove
150
- end
69
+ render 'sortable_nested_set/sns_destroy_item'
151
70
  end
152
71
  end
153
72
  end
@@ -15,8 +15,7 @@ module ActionDispatch::Routing
15
15
  end
16
16
 
17
17
  collection do
18
- put :sns_sort_categories
19
- put :sns_sort_items
18
+ put :sns_sort
20
19
  end
21
20
  end
22
21
 
@@ -1,4 +1,4 @@
1
- require 'sortable_nested_set/engine' if defined? Rails && Rails::VERSION::MAJOR == 3
1
+ require 'sortable_nested_set/engine'
2
2
  require 'sortable_nested_set/acts_as_sortable_nested_set'
3
3
  require 'sortable_nested_set/handles_sortable_nested_set'
4
4
  require 'sortable_nested_set/routes'
Binary file
Binary file
Binary file
@@ -0,0 +1,10 @@
1
+ /**
2
+ * hoverIntent r5 // 2007.03.27 // jQuery 1.1.2
3
+ * <http://cherne.net/brian/resources/jquery.hoverIntent.html>
4
+ *
5
+ * @param f onMouseOver function || An object with configuration options
6
+ * @param g onMouseOut function || Nothing (use configuration options object)
7
+ * @return The object (aka "this") that called hoverIntent, and the event object
8
+ * @author Brian Cherne <brian@cherne.net>
9
+ */
10
+ (function($){$.fn.hoverIntent=function(f,g){var cfg={sensitivity:7,interval:100,timeout:0};cfg=$.extend(cfg,g?{over:f,out:g}:f);var cX,cY,pX,pY;var track=function(ev){cX=ev.pageX;cY=ev.pageY;};var compare=function(ev,ob){ob.hoverIntent_t=clearTimeout(ob.hoverIntent_t);if((Math.abs(pX-cX)+Math.abs(pY-cY))<cfg.sensitivity){$(ob).unbind("mousemove",track);ob.hoverIntent_s=1;return cfg.over.apply(ob,[ev]);}else{pX=cX;pY=cY;ob.hoverIntent_t=setTimeout(function(){compare(ev,ob);},cfg.interval);}};var delay=function(ev,ob){ob.hoverIntent_t=clearTimeout(ob.hoverIntent_t);ob.hoverIntent_s=0;return cfg.out.apply(ob,[ev]);};var handleHover=function(e){var p=(e.type=="mouseover"?e.fromElement:e.toElement)||e.relatedTarget;while(p&&p!=this){try{p=p.parentNode;}catch(e){p=this;}}if(p==this){return false;}var ev=jQuery.extend({},e);var ob=this;if(ob.hoverIntent_t){ob.hoverIntent_t=clearTimeout(ob.hoverIntent_t);}if(e.type=="mouseover"){pX=ev.pageX;pY=ev.pageY;$(ob).bind("mousemove",track);if(ob.hoverIntent_s!=1){ob.hoverIntent_t=setTimeout(function(){compare(ev,ob);},cfg.interval);}}else{$(ob).unbind("mousemove",track);if(ob.hoverIntent_s==1){ob.hoverIntent_t=setTimeout(function(){delay(ev,ob);},cfg.timeout);}}};return this.mouseover(handleHover).mouseout(handleHover);};})(jQuery);
@@ -0,0 +1,291 @@
1
+ /*
2
+ * jQuery UI Nested Sortable 1.2.1
3
+ *
4
+ * Copyright 2010, Manuele J Sarfatti
5
+ *
6
+ * http://mjsarfatti.com/sandbox/nestedSortable
7
+ *
8
+ * Depends:
9
+ * jquery.ui.core.js 1.8+
10
+ * jquery.ui.widget.js 1.8+
11
+ * jquery.ui.sortable.js 1.8+
12
+ */
13
+ (function($) {
14
+
15
+ $.widget("ui.nestedSortable", $.extend({}, $.ui.sortable.prototype, {
16
+
17
+ options: {
18
+ tabSize: 20,
19
+ disableNesting: 'ui-nestedSortable-no-nesting',
20
+ errorClass: 'ui-nestedSortable-error',
21
+ listType: 'ol'
22
+ },
23
+
24
+ _create: function(){
25
+ this.element.data('sortable', this.element.data('sortableTree'));
26
+ return $.ui.sortable.prototype._create.apply(this, arguments);
27
+ },
28
+
29
+ _mouseDrag: function(event) {
30
+
31
+ //Compute the helpers position
32
+ this.position = this._generatePosition(event);
33
+ this.positionAbs = this._convertPositionTo("absolute");
34
+
35
+ if (!this.lastPositionAbs) {
36
+ this.lastPositionAbs = this.positionAbs;
37
+ }
38
+
39
+ //Do scrolling
40
+ if(this.options.scroll) {
41
+ var o = this.options, scrolled = false;
42
+ if(this.scrollParent[0] != document && this.scrollParent[0].tagName != 'HTML') {
43
+
44
+ if((this.overflowOffset.top + this.scrollParent[0].offsetHeight) - event.pageY < o.scrollSensitivity)
45
+ this.scrollParent[0].scrollTop = scrolled = this.scrollParent[0].scrollTop + o.scrollSpeed;
46
+ else if(event.pageY - this.overflowOffset.top < o.scrollSensitivity)
47
+ this.scrollParent[0].scrollTop = scrolled = this.scrollParent[0].scrollTop - o.scrollSpeed;
48
+
49
+ if((this.overflowOffset.left + this.scrollParent[0].offsetWidth) - event.pageX < o.scrollSensitivity)
50
+ this.scrollParent[0].scrollLeft = scrolled = this.scrollParent[0].scrollLeft + o.scrollSpeed;
51
+ else if(event.pageX - this.overflowOffset.left < o.scrollSensitivity)
52
+ this.scrollParent[0].scrollLeft = scrolled = this.scrollParent[0].scrollLeft - o.scrollSpeed;
53
+
54
+ } else {
55
+
56
+ if(event.pageY - $(document).scrollTop() < o.scrollSensitivity)
57
+ scrolled = $(document).scrollTop($(document).scrollTop() - o.scrollSpeed);
58
+ else if($(window).height() - (event.pageY - $(document).scrollTop()) < o.scrollSensitivity)
59
+ scrolled = $(document).scrollTop($(document).scrollTop() + o.scrollSpeed);
60
+
61
+ if(event.pageX - $(document).scrollLeft() < o.scrollSensitivity)
62
+ scrolled = $(document).scrollLeft($(document).scrollLeft() - o.scrollSpeed);
63
+ else if($(window).width() - (event.pageX - $(document).scrollLeft()) < o.scrollSensitivity)
64
+ scrolled = $(document).scrollLeft($(document).scrollLeft() + o.scrollSpeed);
65
+
66
+ }
67
+
68
+ if(scrolled !== false && $.ui.ddmanager && !o.dropBehaviour)
69
+ $.ui.ddmanager.prepareOffsets(this, event);
70
+ }
71
+
72
+ //Regenerate the absolute position used for position checks
73
+ this.positionAbs = this._convertPositionTo("absolute");
74
+
75
+ //Set the helper position
76
+ if(!this.options.axis || this.options.axis != "y") this.helper[0].style.left = this.position.left+'px';
77
+ if(!this.options.axis || this.options.axis != "x") this.helper[0].style.top = this.position.top+'px';
78
+
79
+ //Rearrange
80
+ for (var i = this.items.length - 1; i >= 0; i--) {
81
+
82
+ //Cache variables and intersection, continue if no intersection
83
+ var item = this.items[i], itemElement = item.item[0], intersection = this._intersectsWithPointer(item);
84
+ if (!intersection) continue;
85
+
86
+ if(itemElement != this.currentItem[0] //cannot intersect with itself
87
+ && this.placeholder[intersection == 1 ? "next" : "prev"]()[0] != itemElement //no useless actions that have been done before
88
+ && !$.ui.contains(this.placeholder[0], itemElement) //no action if the item moved is the parent of the item checked
89
+ && (this.options.type == 'semi-dynamic' ? !$.ui.contains(this.element[0], itemElement) : true)
90
+ //&& itemElement.parentNode == this.placeholder[0].parentNode // only rearrange items within the same container
91
+ ) {
92
+
93
+ this.direction = intersection == 1 ? "down" : "up";
94
+
95
+ if (this.options.tolerance == "pointer" || this._intersectsWithSides(item)) {
96
+ this._rearrange(event, item);
97
+ } else {
98
+ break;
99
+ }
100
+
101
+ // Clear emtpy ul's/ol's
102
+ this._clearEmpty(itemElement);
103
+
104
+ this._trigger("change", event, this._uiHash());
105
+ break;
106
+ }
107
+ }
108
+
109
+ // Get the real previous item
110
+ itemBefore = this.placeholder[0].previousSibling;
111
+ while (itemBefore != null) {
112
+ if (itemBefore.nodeType == 1 && itemBefore != this.currentItem[0]) {
113
+ break;
114
+ } else {
115
+ itemBefore = itemBefore.previousSibling;
116
+ }
117
+ }
118
+
119
+ parentItem = this.placeholder[0].parentNode.parentNode;
120
+ newList = document.createElement(o.listType);
121
+
122
+ // Make/delete nested ul's/ol's
123
+ if (parentItem != null && parentItem.nodeName == 'LI' && this.positionAbs.left < $(parentItem).offset().left) {
124
+ $(parentItem).after(this.placeholder[0]);
125
+ this._clearEmpty(parentItem);
126
+ } else if (itemBefore != null && itemBefore.nodeName == 'LI' && this.positionAbs.left > $(itemBefore).offset().left + this.options.tabSize) {
127
+ if (!($(itemBefore).hasClass(this.options.disableNesting))) {
128
+ if ($(this.placeholder[0]).hasClass(this.options.errorClass)) {
129
+ $(this.placeholder[0]).css('marginLeft', 0).removeClass(this.options.errorClass);
130
+ }
131
+ if (itemBefore.children[1] == null) {
132
+ itemBefore.appendChild(newList);
133
+ }
134
+ itemBefore.children[1].appendChild(this.placeholder[0]);
135
+ } else {
136
+ $(this.placeholder[0]).addClass(this.options.errorClass).css('marginLeft', this.options.tabSize);
137
+ }
138
+ } else if (itemBefore != null) {
139
+ if ($(this.placeholder[0]).hasClass(this.options.errorClass)) {
140
+ $(this.placeholder[0]).css('marginLeft', 0).removeClass(this.options.errorClass);
141
+ }
142
+ $(itemBefore).after(this.placeholder[0]);
143
+ } else {
144
+ if ($(this.placeholder[0]).hasClass(this.options.errorClass)) {
145
+ $(this.placeholder[0]).css('marginLeft', 0).removeClass(this.options.errorClass);
146
+ }
147
+ }
148
+
149
+ //Post events to containers
150
+ this._contactContainers(event);
151
+
152
+ //Interconnect with droppables
153
+ if($.ui.ddmanager) $.ui.ddmanager.drag(this, event);
154
+
155
+ //Call callbacks
156
+ this._trigger('sort', event, this._uiHash());
157
+
158
+ this.lastPositionAbs = this.positionAbs;
159
+ return false;
160
+
161
+ },
162
+
163
+ serialize: function(o) {
164
+
165
+ var items = this._getItemsAsjQuery(o && o.connected);
166
+ var str = []; o = o || {};
167
+
168
+ $(items).each(function() {
169
+ var res = ($(o.item || this).attr(o.attribute || 'id') || '').match(o.expression || (/(.+)[-=_](.+)/));
170
+ var pid = ($(o.item || this).parent(o.listType).parent('li').attr(o.attribute || 'id') || '').match(o.expression || (/(.+)[-=_](.+)/));
171
+ if(res) str.push((o.key || res[1]+'['+(o.key && o.expression ? res[1] : res[2])+']')+'='+(pid ? (o.key && o.expression ? pid[1] : pid[2]) : 'root'));
172
+ });
173
+
174
+ if(!str.length && o.key) {
175
+ str.push(o.key + '=');
176
+ }
177
+
178
+ return str.join('&');
179
+
180
+ },
181
+
182
+ toArray: function(o) {
183
+
184
+ o = o || {};
185
+ var sDepth = o.startDepthCount || 0;
186
+ var ret = [];
187
+ var left = 2;
188
+
189
+ ret.push({"item_id": 'root', "parent_id": 'none', "depth": sDepth, "left": '1', "right": ($('li', this.element).length + 1) * 2});
190
+
191
+ $(this.element).children('li').each(function() {
192
+ left = _recursiveArray($(this), sDepth + 1, left);
193
+ });
194
+
195
+ return ret;
196
+
197
+ function _recursiveArray(item, depth, left) {
198
+
199
+ right = left + 1;
200
+
201
+ if ($(item).children(o.listType).children('li').length > 0) {
202
+ depth ++;
203
+ $(item).children(o.listType).children('li').each(function() {
204
+ right = _recursiveArray($(this), depth, right);
205
+ });
206
+ depth --;
207
+ }
208
+
209
+ id = $(item).attr('id').match(o.expression || (/(.+)[-=_](.+)/));
210
+
211
+ if (depth === sDepth + 1) pid = 'root';
212
+ else {
213
+ parentItem = $(item).parent(o.listType).parent('li').attr('id').match(o.expression || (/(.+)[-=_](.+)/));
214
+ pid = parentItem[2];
215
+ }
216
+
217
+ ret.push({"item_id": id[2], "parent_id": pid, "depth": depth, "left": left, "right": right});
218
+
219
+ return left = right + 1;
220
+ }
221
+
222
+ },
223
+
224
+ _createPlaceholder: function(that) {
225
+
226
+ var self = that || this, o = self.options;
227
+
228
+ if(!o.placeholder || o.placeholder.constructor == String) {
229
+ var className = o.placeholder;
230
+ o.placeholder = {
231
+ element: function() {
232
+
233
+ var el = $(document.createElement(self.currentItem[0].nodeName))
234
+ .addClass(className || self.currentItem[0].className+" ui-sortable-placeholder")
235
+ .removeClass("ui-sortable-helper")[0];
236
+
237
+ if(!className)
238
+ el.style.visibility = "hidden";
239
+
240
+ return el;
241
+ },
242
+ update: function(container, p) {
243
+
244
+ // 1. If a className is set as 'placeholder option, we don't force sizes - the class is responsible for that
245
+ // 2. The option 'forcePlaceholderSize can be enabled to force it even if a class name is specified
246
+ if(className && !o.forcePlaceholderSize) return;
247
+
248
+ //If the element doesn't have an actual height by itself (without styles coming from a stylesheet), it receives the inline height from the dragged item
249
+ if(!p.height() || p.css('height') == 'auto') { p.height(self.currentItem.height()); };
250
+ if(!p.width()) { p.width(self.currentItem.width()); };
251
+ }
252
+ };
253
+ }
254
+
255
+ //Create the placeholder
256
+ self.placeholder = $(o.placeholder.element.call(self.element, self.currentItem));
257
+
258
+ //Append it after the actual current item
259
+ self.currentItem.after(self.placeholder);
260
+
261
+ //Update the size of the placeholder (TODO: Logic to fuzzy, see line 316/317)
262
+ o.placeholder.update(self, self.placeholder);
263
+
264
+ },
265
+
266
+ _clear: function(event, noPropagation) {
267
+
268
+ $.ui.sortable.prototype._clear.apply(this, arguments);
269
+
270
+ // Clean last empty ul/ol
271
+ for (var i = this.items.length - 1; i >= 0; i--) {
272
+ var item = this.items[i].item[0];
273
+ this._clearEmpty(item);
274
+ }
275
+ return true;
276
+
277
+ },
278
+
279
+ _clearEmpty: function(item) {
280
+
281
+ if (item.children[1] && item.children[1].children.length == 0) {
282
+ item.removeChild(item.children[1]);
283
+ }
284
+
285
+ }
286
+
287
+ }));
288
+
289
+ $.ui.nestedSortable.prototype.options = $.extend({}, $.ui.sortable.prototype.options, $.ui.nestedSortable.prototype.options);
290
+
291
+ })(jQuery);
@@ -0,0 +1,67 @@
1
+ $(document).ready(function() {
2
+ $.getScript('/javascripts/jquery.hoverIntent.minified.js', function() {
3
+ function linkHover() {
4
+ $(this).next().addClass('link_hover');
5
+ }
6
+
7
+ function linkUnhover() {
8
+ if (!$(this).next().hasClass('pop_up_hover'))
9
+ $(this).next().removeClass('link_hover');
10
+ }
11
+
12
+ function popUpHover() {
13
+ $(this).removeClass('link_hover');
14
+ $(this).addClass('pop_up_hover');
15
+ }
16
+
17
+ function popUpUnhover() {
18
+ $(this).removeClass('pop_up_hover');
19
+ }
20
+
21
+ var linkConfig = {
22
+ interval: 200,
23
+ out: linkUnhover,
24
+ over: linkHover,
25
+ sensitivity: 4,
26
+ timeout: 250
27
+ };
28
+
29
+ var popUpConfig = {
30
+ interval: 0,
31
+ out: popUpUnhover,
32
+ over: popUpHover,
33
+ sensitivity: 4,
34
+ timeout: 250
35
+ };
36
+
37
+ $('.mega_menu').hoverIntent(linkConfig);
38
+ $('.mega_menu_links').hoverIntent(popUpConfig);
39
+ });
40
+
41
+ $.getScript('/javascripts/jquery.ui.nestedSortable.js', function() {
42
+ $('#menu').nestedSortable({
43
+ disableNesting: 'item',
44
+ forcePlaceholderSize: true,
45
+ handle: '.handle',
46
+ items: 'li',
47
+ opacity: .6,
48
+ placeholder: 'placeholder',
49
+ tabSize: 25,
50
+ tolerance: 'pointer',
51
+ toleranceElement: '> div',
52
+ update: function(event, ui) {
53
+ $.ajax({
54
+ type: 'put',
55
+ url: this.getAttribute('data-url'),
56
+ data: { moved: ui.item[0].id, tree: $('#menu').nestedSortable('serialize') }
57
+ });
58
+ }
59
+ });
60
+ });
61
+
62
+ $('#menu .toggle').click(function() {
63
+ $(this).toggleClass('open');
64
+ $(this).toggleClass('closed');
65
+ $(this).parent().next('ol').toggle();
66
+ });
67
+ });
@@ -0,0 +1,82 @@
1
+ .mega_menu {
2
+ background: url(/images/mega_menu.png) no-repeat center;
3
+ cursor: pointer;
4
+ padding-left: 30px;
5
+ }
6
+
7
+ .mega_menu_links {
8
+ background: #ffc;
9
+ border: 1px solid #eea;
10
+ display: none;
11
+ padding: 0.5em;
12
+ position: absolute;
13
+ z-index: 1;
14
+ }
15
+
16
+ .mega_menu_links a:link, a:visited {
17
+ color: blue;
18
+ text-decoration: none;
19
+ }
20
+
21
+ .mega_menu_links a:hover {
22
+ color: green;
23
+ }
24
+
25
+ .link_hover {
26
+ display: block;
27
+ }
28
+
29
+ .pop_up_hover {
30
+ display: block;
31
+ border: 1px solid #cca;
32
+ }
33
+
34
+ .sortable, .sortable ol {
35
+ list-style-type: none;
36
+ margin: 0 0 0 25px;
37
+ padding: 0;
38
+ }
39
+
40
+ .sortable {
41
+ margin: 0;
42
+ }
43
+
44
+ .sortable li {
45
+ margin: 7px 0 0 0;
46
+ padding: 0;
47
+ position: relative;
48
+ }
49
+
50
+ .sortable .handle {
51
+ cursor: move;
52
+ display: inline;
53
+ margin: 0;
54
+ }
55
+
56
+ .placeholder {
57
+ background-color: #cfcfcf;
58
+ }
59
+
60
+ .ui-nestedSortable-error {
61
+ background: #fbe3e4;
62
+ color: #8a1f11;
63
+ }
64
+
65
+ .toggle {
66
+ cursor: pointer;
67
+ padding-right: 1em;
68
+ }
69
+
70
+ #menu span.open {
71
+ background: url(/images/folder_open.png) no-repeat center;
72
+ }
73
+
74
+ #menu span.closed {
75
+ background: url(/images/folder_closed.png) no-repeat center;
76
+ }
77
+
78
+ a.lineage {
79
+ background: url(/images/folder_closed.png) no-repeat left;
80
+ padding-left: 9px;
81
+ padding-right: 5px;
82
+ }