sortable_nested_set 0.1.26 → 0.2.2
Sign up to get free protection for your applications and to get access to all the features.
- data/.document +5 -0
- data/Gemfile +14 -0
- data/Gemfile.lock +62 -0
- data/README +28 -2
- data/README.rdoc +19 -0
- data/Rakefile +53 -0
- data/VERSION +1 -0
- data/app/helpers/sortable_nested_set_helper.rb +24 -21
- data/app/views/sortable_nested_set/_category.html.erb +16 -29
- data/app/views/sortable_nested_set/_item.html.erb +15 -16
- data/app/views/sortable_nested_set/_tree.html.erb +15 -45
- data/app/views/sortable_nested_set/sns_destroy_category.js.erb +1 -0
- data/app/views/sortable_nested_set/sns_destroy_item.js.erb +1 -0
- data/lib/sortable_nested_set/acts_as_sortable_nested_set.rb +24 -29
- data/lib/sortable_nested_set/engine.rb +0 -6
- data/lib/sortable_nested_set/handles_sortable_nested_set.rb +28 -109
- data/lib/sortable_nested_set/routes.rb +1 -2
- data/lib/sortable_nested_set.rb +1 -1
- data/public/images/folder_closed.png +0 -0
- data/public/images/folder_open.png +0 -0
- data/public/images/mega_menu.png +0 -0
- data/public/javascripts/jquery.hoverIntent.minified.js +10 -0
- data/public/javascripts/jquery.ui.nestedSortable.js +291 -0
- data/public/javascripts/sortable_nested_set.js +67 -0
- data/public/stylesheets/sortable_nested_set.css +82 -0
- data/sortable_nested_set.gemspec +84 -0
- data/test/helper.rb +18 -0
- data/test/test_sortable_nested_set.rb +7 -0
- metadata +81 -18
- data/app/views/sortable_nested_set/_toggle.html.erb +0 -5
@@ -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
|
15
|
+
include InstanceMethods
|
22
16
|
end
|
23
17
|
end
|
24
18
|
|
25
19
|
module InstanceMethods
|
26
20
|
def sns_add_subcategory
|
27
|
-
redirect_to :
|
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
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
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
|
-
|
61
|
-
|
62
|
-
|
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
|
-
|
71
|
-
|
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
|
-
|
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
|
-
|
96
|
-
|
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
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
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
|
-
|
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
|
data/lib/sortable_nested_set.rb
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
require 'sortable_nested_set/engine'
|
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
|
+
}
|