sortable_nested_set 0.1.23
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/README +3 -0
- data/app/helpers/sortable_nested_set_helper.rb +34 -0
- data/app/views/sortable_nested_set/_category.html.erb +27 -0
- data/app/views/sortable_nested_set/_item.html.erb +12 -0
- data/app/views/sortable_nested_set/_toggle.html.erb +5 -0
- data/app/views/sortable_nested_set/_tree.html.erb +44 -0
- data/lib/public/images/drag.png +0 -0
- data/lib/public/images/folder_closed.png +0 -0
- data/lib/public/images/folder_open.png +0 -0
- data/lib/public/javascripts/controls.js +963 -0
- data/lib/public/javascripts/dragdrop.js +973 -0
- data/lib/public/javascripts/effects.js +1128 -0
- data/lib/public/javascripts/jquery.hoverIntent.minified.js +10 -0
- data/lib/public/javascripts/jquery.js +1 -0
- data/lib/public/javascripts/mega_menu.js +44 -0
- data/lib/public/javascripts/prototype.js +4320 -0
- data/lib/public/stylesheets/mega_menu.css +34 -0
- data/lib/public/stylesheets/menu_tree.css +31 -0
- data/lib/sortable_nested_set/acts_as_sortable_nested_set.rb +68 -0
- data/lib/sortable_nested_set/engine.rb +10 -0
- data/lib/sortable_nested_set/handles_sortable_nested_set.rb +155 -0
- data/lib/sortable_nested_set/routes.rb +18 -0
- data/lib/sortable_nested_set.rb +4 -0
- data/lib/tasks/sortable_nested_set_tasks.rake +6 -0
- metadata +112 -0
@@ -0,0 +1,34 @@
|
|
1
|
+
ul.menu_links li.category span.mega {
|
2
|
+
display: inline;
|
3
|
+
color: blue;
|
4
|
+
cursor: pointer;
|
5
|
+
}
|
6
|
+
|
7
|
+
ul.menu_links li.category {
|
8
|
+
position: relative;
|
9
|
+
}
|
10
|
+
|
11
|
+
ul.menu_links li.category div.links {
|
12
|
+
display: none;
|
13
|
+
z-index: 1;
|
14
|
+
border: 1px solid #eea;
|
15
|
+
position: absolute;
|
16
|
+
top: 1.6em;
|
17
|
+
left: 0em;
|
18
|
+
padding: 1.3em;
|
19
|
+
background: #ffc;
|
20
|
+
}
|
21
|
+
|
22
|
+
ul.menu_links li.category div.items {
|
23
|
+
min-height: 5px;
|
24
|
+
padding-left: 20px;
|
25
|
+
}
|
26
|
+
|
27
|
+
ul.menu_links li.category div.link_hover {
|
28
|
+
display: block;
|
29
|
+
}
|
30
|
+
|
31
|
+
ul.menu_links li.category div.pop_up_hover {
|
32
|
+
display: block;
|
33
|
+
border: 1px solid #cca;
|
34
|
+
}
|
@@ -0,0 +1,31 @@
|
|
1
|
+
ul.menu_links, ul.sub_menu_links {
|
2
|
+
list-style: none;
|
3
|
+
margin-left: 0px;
|
4
|
+
padding-left: 0px;
|
5
|
+
}
|
6
|
+
|
7
|
+
li > ul.sub_menu_links {
|
8
|
+
padding-left: 1em;
|
9
|
+
}
|
10
|
+
|
11
|
+
span.toggle a:hover {
|
12
|
+
background-color: white;
|
13
|
+
}
|
14
|
+
|
15
|
+
li.placeholder {
|
16
|
+
height: 3px;
|
17
|
+
}
|
18
|
+
|
19
|
+
li.hovered {
|
20
|
+
background: url(/images/drag.png) no-repeat bottom left;
|
21
|
+
}
|
22
|
+
|
23
|
+
li.category span.handle {
|
24
|
+
cursor: move;
|
25
|
+
font-variant: small-caps;
|
26
|
+
}
|
27
|
+
|
28
|
+
li.category div.item .handle {
|
29
|
+
cursor: move;
|
30
|
+
font-variant: normal;
|
31
|
+
}
|
@@ -0,0 +1,68 @@
|
|
1
|
+
require "awesome_nested_set"
|
2
|
+
include CollectiveIdea::Acts::NestedSet
|
3
|
+
|
4
|
+
module SortableNestedSet
|
5
|
+
def self.included(base)
|
6
|
+
base.extend(ClassMethods)
|
7
|
+
end
|
8
|
+
|
9
|
+
module ClassMethods
|
10
|
+
def acts_as_sortable_nested_set_of(items_type)
|
11
|
+
send(:include, InstanceMethods)
|
12
|
+
|
13
|
+
@subcategories_type = "sub#{name.tableize}".to_sym
|
14
|
+
@items_type = items_type
|
15
|
+
|
16
|
+
instance_eval do
|
17
|
+
def sns_subcategories_type ; @subcategories_type end
|
18
|
+
def sns_items_type ; @items_type end
|
19
|
+
end
|
20
|
+
|
21
|
+
class_eval do
|
22
|
+
define_method(:sns_subcategories) { send(self.class.sns_subcategories_type) }
|
23
|
+
define_method(:sns_items) { send(self.class.sns_items_type) }
|
24
|
+
end
|
25
|
+
|
26
|
+
acts_as_nested_set :parent_column => name.foreign_key
|
27
|
+
|
28
|
+
belongs_to name.underscore
|
29
|
+
|
30
|
+
has_many @subcategories_type, :class_name => name, :dependent => :destroy
|
31
|
+
has_many @items_type, :order => :position, :dependent => :destroy
|
32
|
+
|
33
|
+
default_scope :order => :lft
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
module InstanceMethods
|
38
|
+
def self_and_descendants_with_items
|
39
|
+
nested_set_scope.scoped :include => self.class.sns_items_type, :conditions => [
|
40
|
+
"#{self.class.quoted_table_name}.#{quoted_left_column_name} >= ? AND
|
41
|
+
#{self.class.quoted_table_name}.#{quoted_right_column_name} <= ?", left, right
|
42
|
+
]
|
43
|
+
end
|
44
|
+
|
45
|
+
def lineage(options=Hash.new)
|
46
|
+
conditions = [ 'lft <= ? AND rgt >= ?', lft, rgt ]
|
47
|
+
unless options[:include_self] == true
|
48
|
+
conditions[0] << ' AND id != ?'
|
49
|
+
conditions << id
|
50
|
+
end
|
51
|
+
|
52
|
+
self.class.find(:all, :select => 'id, name', :conditions => conditions, :order => 'lft ASC')
|
53
|
+
end
|
54
|
+
|
55
|
+
# descendents() does "SELECT *" but only id field required
|
56
|
+
def descendant_subcategory_ids(options=Hash.new)
|
57
|
+
conditions = [ 'lft >= ? AND rgt <= ?', lft, rgt ]
|
58
|
+
unless options[:include_self] == true
|
59
|
+
conditions[0] << ' AND id != ?'
|
60
|
+
conditions << id
|
61
|
+
end
|
62
|
+
|
63
|
+
self.class.find(:all, :select => :id, :conditions => conditions).collect(&:id)
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
ActiveRecord::Base.send(:include, SortableNestedSet)
|
@@ -0,0 +1,155 @@
|
|
1
|
+
require "sortable_element_for_nested_set"
|
2
|
+
include SortableElementForNestedSet
|
3
|
+
|
4
|
+
module SortableNestedSet
|
5
|
+
def self.included(base)
|
6
|
+
base.extend(ClassMethods)
|
7
|
+
end
|
8
|
+
|
9
|
+
module ClassMethods
|
10
|
+
def handles_sortable_nested_set
|
11
|
+
handles_sorting_of_nested_set
|
12
|
+
|
13
|
+
class_eval do
|
14
|
+
define_method(:sns_category_controller) { controller_name }
|
15
|
+
define_method(:sns_category_id_field) { "#{controller_name.singularize}_id" }
|
16
|
+
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 }
|
19
|
+
end
|
20
|
+
|
21
|
+
include(SortableNestedSet::InstanceMethods)
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
module InstanceMethods
|
26
|
+
def sns_add_subcategory
|
27
|
+
redirect_to(:controller => sns_category_controller, :action => :new, sns_category_id_field => params[:id])
|
28
|
+
end
|
29
|
+
|
30
|
+
def sns_add_item
|
31
|
+
redirect_to(:controller => sns_items_type, :action => :new, sns_category_id_field => params[:id])
|
32
|
+
end
|
33
|
+
|
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]
|
59
|
+
|
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
|
69
|
+
|
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)')
|
89
|
+
|
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')
|
94
|
+
|
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
|
106
|
+
end
|
107
|
+
end
|
108
|
+
|
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')"
|
120
|
+
end
|
121
|
+
end
|
122
|
+
|
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
|
133
|
+
end
|
134
|
+
|
135
|
+
def sns_destroy_category
|
136
|
+
@category = sns_category_class.find(params[:id])
|
137
|
+
@category.destroy
|
138
|
+
|
139
|
+
render :update do |page|
|
140
|
+
page["category_#{@category.id}"].remove
|
141
|
+
end
|
142
|
+
end
|
143
|
+
|
144
|
+
def sns_destroy_item
|
145
|
+
@item = sns_item_class.find(params[:id])
|
146
|
+
@item.destroy
|
147
|
+
|
148
|
+
render :update do |page|
|
149
|
+
page["item_#{@item.id}"].remove
|
150
|
+
end
|
151
|
+
end
|
152
|
+
end
|
153
|
+
end
|
154
|
+
|
155
|
+
ActionController::Base.send(:include, SortableNestedSet)
|
@@ -0,0 +1,18 @@
|
|
1
|
+
module ActionDispatch::Routing
|
2
|
+
class Mapper
|
3
|
+
def sortable_nested_set_for(category_type)
|
4
|
+
resources category_type do
|
5
|
+
get :sns_toggle, :on => :member
|
6
|
+
get :sns_add_subcategory, :on => :member
|
7
|
+
get :sns_add_item, :on => :member
|
8
|
+
put :sns_sort_categories, :on => :collection
|
9
|
+
put :sns_sort_items, :on => :collection
|
10
|
+
delete :sns_destroy_category, :on => :member
|
11
|
+
end
|
12
|
+
|
13
|
+
resources category_type.to_s.classify.constantize.sns_items_type do
|
14
|
+
delete :sns_destroy_item, :on => :member, :controller => category_type
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
metadata
ADDED
@@ -0,0 +1,112 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: sortable_nested_set
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
prerelease: false
|
5
|
+
segments:
|
6
|
+
- 0
|
7
|
+
- 1
|
8
|
+
- 23
|
9
|
+
version: 0.1.23
|
10
|
+
platform: ruby
|
11
|
+
authors: []
|
12
|
+
|
13
|
+
autorequire:
|
14
|
+
bindir: bin
|
15
|
+
cert_chain: []
|
16
|
+
|
17
|
+
date: 2010-06-29 00:00:00 -04:00
|
18
|
+
default_executable:
|
19
|
+
dependencies:
|
20
|
+
- !ruby/object:Gem::Dependency
|
21
|
+
name: awesome_nested_set
|
22
|
+
prerelease: false
|
23
|
+
requirement: &id001 !ruby/object:Gem::Requirement
|
24
|
+
none: false
|
25
|
+
requirements:
|
26
|
+
- - ">="
|
27
|
+
- !ruby/object:Gem::Version
|
28
|
+
segments:
|
29
|
+
- 0
|
30
|
+
version: "0"
|
31
|
+
type: :runtime
|
32
|
+
version_requirements: *id001
|
33
|
+
- !ruby/object:Gem::Dependency
|
34
|
+
name: sortable_element_for_nested_set
|
35
|
+
prerelease: false
|
36
|
+
requirement: &id002 !ruby/object:Gem::Requirement
|
37
|
+
none: false
|
38
|
+
requirements:
|
39
|
+
- - ">="
|
40
|
+
- !ruby/object:Gem::Version
|
41
|
+
segments:
|
42
|
+
- 0
|
43
|
+
version: "0"
|
44
|
+
type: :runtime
|
45
|
+
version_requirements: *id002
|
46
|
+
description:
|
47
|
+
email:
|
48
|
+
executables: []
|
49
|
+
|
50
|
+
extensions: []
|
51
|
+
|
52
|
+
extra_rdoc_files:
|
53
|
+
- README
|
54
|
+
files:
|
55
|
+
- app/helpers/sortable_nested_set_helper.rb
|
56
|
+
- app/views/sortable_nested_set/_category.html.erb
|
57
|
+
- app/views/sortable_nested_set/_item.html.erb
|
58
|
+
- app/views/sortable_nested_set/_toggle.html.erb
|
59
|
+
- app/views/sortable_nested_set/_tree.html.erb
|
60
|
+
- lib/public/images/drag.png
|
61
|
+
- lib/public/images/folder_closed.png
|
62
|
+
- lib/public/images/folder_open.png
|
63
|
+
- lib/public/javascripts/controls.js
|
64
|
+
- lib/public/javascripts/dragdrop.js
|
65
|
+
- lib/public/javascripts/effects.js
|
66
|
+
- lib/public/javascripts/jquery.hoverIntent.minified.js
|
67
|
+
- lib/public/javascripts/jquery.js
|
68
|
+
- lib/public/javascripts/mega_menu.js
|
69
|
+
- lib/public/javascripts/prototype.js
|
70
|
+
- lib/public/stylesheets/mega_menu.css
|
71
|
+
- lib/public/stylesheets/menu_tree.css
|
72
|
+
- lib/sortable_nested_set.rb
|
73
|
+
- lib/sortable_nested_set/acts_as_sortable_nested_set.rb
|
74
|
+
- lib/sortable_nested_set/engine.rb
|
75
|
+
- lib/sortable_nested_set/handles_sortable_nested_set.rb
|
76
|
+
- lib/sortable_nested_set/routes.rb
|
77
|
+
- lib/tasks/sortable_nested_set_tasks.rake
|
78
|
+
- README
|
79
|
+
has_rdoc: true
|
80
|
+
homepage:
|
81
|
+
licenses: []
|
82
|
+
|
83
|
+
post_install_message:
|
84
|
+
rdoc_options:
|
85
|
+
- --charset=UTF-8
|
86
|
+
require_paths:
|
87
|
+
- lib
|
88
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
89
|
+
none: false
|
90
|
+
requirements:
|
91
|
+
- - ">="
|
92
|
+
- !ruby/object:Gem::Version
|
93
|
+
segments:
|
94
|
+
- 0
|
95
|
+
version: "0"
|
96
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
97
|
+
none: false
|
98
|
+
requirements:
|
99
|
+
- - ">="
|
100
|
+
- !ruby/object:Gem::Version
|
101
|
+
segments:
|
102
|
+
- 0
|
103
|
+
version: "0"
|
104
|
+
requirements: []
|
105
|
+
|
106
|
+
rubyforge_project:
|
107
|
+
rubygems_version: 1.3.7
|
108
|
+
signing_key:
|
109
|
+
specification_version: 3
|
110
|
+
summary: Sortable Nested Set
|
111
|
+
test_files: []
|
112
|
+
|