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.
- 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
|
+
}
|