the_sortable_tree 1.9.4 → 2.0.0
Sign up to get free protection for your applications and to get access to all the features.
- data/MIT-LICENSE +4 -2
- data/README.md +237 -438
- data/app/assets/images/iconza/{gray → icons}/add.png +0 -0
- data/app/assets/images/iconza/{gray → icons}/delete.png +0 -0
- data/app/assets/images/iconza/{gray → icons}/edit.png +0 -0
- data/app/assets/images/iconza/icons/move.png +0 -0
- data/app/assets/images/iconza/{red/add.png → icons/red_add.png} +0 -0
- data/app/assets/images/iconza/{red/delete.png → icons/red_delete.png} +0 -0
- data/app/assets/images/iconza/{red/edit.png → icons/red_edit.png} +0 -0
- data/app/assets/images/iconza/icons/red_move.png +0 -0
- data/app/assets/javascripts/render_tree_helper.js.coffee +82 -0
- data/app/assets/javascripts/sortable_tree/initializer.js.coffee +54 -0
- data/app/assets/stylesheets/sortable_tree.css.scss +106 -0
- data/app/assets/stylesheets/tree.css.scss +26 -29
- data/app/controllers/the_sortable_tree_controller.rb +3 -2
- data/app/helpers/render_sortable_tree_helper.rb +62 -0
- data/app/helpers/render_tree_helper.rb +45 -0
- data/app/helpers/the_sortable_tree_helper.rb +67 -52
- data/app/views/sortable/client/_tree.html.erb +9 -0
- data/app/views/tree/client/_tree.html.erb +23 -0
- data/lib/fake.example +44 -0
- data/lib/generators/the_sortable_tree/views_generator.rb +29 -18
- data/lib/the_sortable_tree.rb +4 -8
- data/lib/the_sortable_tree/engine.rb +1 -1
- data/lib/the_sortable_tree/version.rb +1 -1
- metadata +31 -60
- data/app/assets/images/iconza/blue/add.png +0 -0
- data/app/assets/images/iconza/blue/delete.png +0 -0
- data/app/assets/images/iconza/blue/down.png +0 -0
- data/app/assets/images/iconza/blue/downloads_folder.png +0 -0
- data/app/assets/images/iconza/blue/edit.png +0 -0
- data/app/assets/images/iconza/blue/move.png +0 -0
- data/app/assets/images/iconza/blue/up.png +0 -0
- data/app/assets/images/iconza/gray/down.png +0 -0
- data/app/assets/images/iconza/gray/lock.png +0 -0
- data/app/assets/images/iconza/gray/mail.png +0 -0
- data/app/assets/images/iconza/gray/push_pin.png +0 -0
- data/app/assets/images/iconza/gray/up.png +0 -0
- data/app/assets/images/iconza/red/down.png +0 -0
- data/app/assets/images/iconza/red/newspaper.png +0 -0
- data/app/assets/images/iconza/red/trash.png +0 -0
- data/app/assets/images/iconza/red/up.png +0 -0
- data/app/assets/images/iconza/red/zoom.png +0 -0
- data/app/assets/javascripts/comments/base.js.coffee +0 -46
- data/app/assets/javascripts/sortable/base.js.coffee +0 -47
- data/app/assets/stylesheets/comments_tree.css.scss +0 -84
- data/app/assets/stylesheets/sortable.css.scss +0 -106
- data/app/views/comments/base/_children.html.haml +0 -1
- data/app/views/comments/base/_comment.html.haml +0 -8
- data/app/views/comments/base/_new_comment_form.html.haml +0 -18
- data/app/views/comments/base/_node.html.haml +0 -3
- data/app/views/comments/base/_tree.html.haml +0 -5
- data/app/views/sortable/base/_children.html.haml +0 -1
- data/app/views/sortable/base/_controls.html.haml +0 -16
- data/app/views/sortable/base/_link.html.haml +0 -7
- data/app/views/sortable/base/_new.html.haml +0 -3
- data/app/views/sortable/base/_node.html.haml +0 -3
- data/app/views/sortable/base/_tree.html.haml +0 -12
- data/app/views/tree/base/_children.html.haml +0 -1
- data/app/views/tree/base/_link.html.haml +0 -1
- data/app/views/tree/base/_node.html.haml +0 -3
- data/app/views/tree/base/_tree.html.haml +0 -3
- data/config/locales/en.yml +0 -31
- data/config/locales/ru.yml +0 -31
- data/lib/tasks/the_sortable_tree.rake +0 -4
File without changes
|
File without changes
|
File without changes
|
Binary file
|
File without changes
|
File without changes
|
File without changes
|
Binary file
|
@@ -0,0 +1,82 @@
|
|
1
|
+
@_escape = (str) ->
|
2
|
+
str
|
3
|
+
.replace(/&/g, '&')
|
4
|
+
.replace(/>/g, '>')
|
5
|
+
.replace(/</g, '<')
|
6
|
+
.replace(/"/g, '"')
|
7
|
+
|
8
|
+
@_unescape = (str) ->
|
9
|
+
str
|
10
|
+
.replace(/&/g, '&')
|
11
|
+
.replace(/>/g, '>')
|
12
|
+
.replace(/</g, '<')
|
13
|
+
.replace(/"/g, '"')
|
14
|
+
|
15
|
+
@render_tree = (tree, options = {}) ->
|
16
|
+
html = ''
|
17
|
+
|
18
|
+
opts =
|
19
|
+
id: 'id'
|
20
|
+
node: null
|
21
|
+
root: false
|
22
|
+
level: 0
|
23
|
+
boost: []
|
24
|
+
|
25
|
+
# JQuery hash merge
|
26
|
+
$.extend opts, options
|
27
|
+
|
28
|
+
# Define Boost Only Once
|
29
|
+
unless opts['boost'].length is 0
|
30
|
+
boost = []
|
31
|
+
for node in tree
|
32
|
+
num = node.parent_id || 0
|
33
|
+
item = boost[num]
|
34
|
+
boost[num] = [] unless item instanceof Array
|
35
|
+
boost[num].push node
|
36
|
+
|
37
|
+
opts['boost'] = boost
|
38
|
+
|
39
|
+
unless opts.node
|
40
|
+
# render root nodes
|
41
|
+
roots = opts['boost'][0]
|
42
|
+
|
43
|
+
# select roots
|
44
|
+
# for node in tree
|
45
|
+
# roots.push node if node.parent_id is null
|
46
|
+
|
47
|
+
# roots is empty, but tree is not empty
|
48
|
+
# I should select nodes with minimal parent_id
|
49
|
+
# they will be roots
|
50
|
+
# order by lft, should be made at server side
|
51
|
+
min_elem = tree[0]
|
52
|
+
if roots.length is 0 and tree.length isnt 0
|
53
|
+
for elem in tree
|
54
|
+
min_elem = elem if elem.parent_id < min_elem.parent_id
|
55
|
+
# select roots witn min parent_id
|
56
|
+
for elem in tree
|
57
|
+
if elem.parent_id is min_elem.parent_id
|
58
|
+
roots.push elem
|
59
|
+
|
60
|
+
# render tree
|
61
|
+
for node in roots
|
62
|
+
$.extend opts, { node: node, root: false, level: opts.level + 1 }
|
63
|
+
children_html = render_tree tree, opts
|
64
|
+
html += opts.render_node(node, children_html, opts)
|
65
|
+
else
|
66
|
+
# render children nodes
|
67
|
+
children = []
|
68
|
+
children_html = ''
|
69
|
+
children = boost[opts.node.id]
|
70
|
+
|
71
|
+
# select children
|
72
|
+
# for elem in tree
|
73
|
+
# children.push elem if elem.parent_id is opts.node.id
|
74
|
+
|
75
|
+
# render children nodes
|
76
|
+
for node in children
|
77
|
+
$.extend opts, { node: node, root: false, level: opts.level + 1 }
|
78
|
+
children_html = render_tree tree, opts
|
79
|
+
html += opts.render_node(node, children_html, opts)
|
80
|
+
|
81
|
+
# result html
|
82
|
+
html
|
@@ -0,0 +1,54 @@
|
|
1
|
+
@rebuild_sortable_tree = (rebuild_url, item_id, parent_id, prev_id, next_id) ->
|
2
|
+
$.ajax
|
3
|
+
type: 'POST'
|
4
|
+
dataType: 'script'
|
5
|
+
url: rebuild_url
|
6
|
+
data:
|
7
|
+
id: item_id
|
8
|
+
parent_id: parent_id
|
9
|
+
prev_id: prev_id
|
10
|
+
next_id: next_id
|
11
|
+
|
12
|
+
beforeSend: (xhr) ->
|
13
|
+
$('.sortable_tree i.handle').hide()
|
14
|
+
|
15
|
+
success: (data, status, xhr) ->
|
16
|
+
$('.sortable_tree i.handle').show()
|
17
|
+
|
18
|
+
error: (xhr, status, error) ->
|
19
|
+
console.log error
|
20
|
+
|
21
|
+
$ ->
|
22
|
+
for sortable_tree in $('ol.sortable_tree')
|
23
|
+
sortable_tree = $ sortable_tree
|
24
|
+
rebuild_url = sortable_tree.data('rebuild_url')
|
25
|
+
max_levels = sortable_tree.data('max_levels')
|
26
|
+
|
27
|
+
############################################
|
28
|
+
# Initialize Sortable Tree
|
29
|
+
############################################
|
30
|
+
sortable_tree.nestedSortable
|
31
|
+
items: 'li'
|
32
|
+
helper: 'clone'
|
33
|
+
handle: 'i.handle'
|
34
|
+
tolerance: 'pointer'
|
35
|
+
maxLevels: max_levels
|
36
|
+
revert: 250
|
37
|
+
tabSize: 25
|
38
|
+
opacity: 0.6
|
39
|
+
placeholder: 'placeholder'
|
40
|
+
disableNesting: 'no-nest'
|
41
|
+
toleranceElement: '> div'
|
42
|
+
forcePlaceholderSize: true
|
43
|
+
|
44
|
+
############################################
|
45
|
+
# Sortable Update Event
|
46
|
+
############################################
|
47
|
+
sortable_tree.on "sortupdate", (event, ui) =>
|
48
|
+
item = ui.item
|
49
|
+
item_id = item.attr('id')
|
50
|
+
prev_id = item.prev().attr('id')
|
51
|
+
next_id = item.next().attr('id')
|
52
|
+
parent_id = item.parent().parent().attr('id')
|
53
|
+
|
54
|
+
rebuild_sortable_tree(rebuild_url, item_id, parent_id, prev_id, next_id)
|
@@ -0,0 +1,106 @@
|
|
1
|
+
.sortable_tree{
|
2
|
+
margin: 0; padding: 0;
|
3
|
+
*{ margin: 0; padding: 0; }
|
4
|
+
|
5
|
+
ol{
|
6
|
+
margin: 0 0 0 13px;
|
7
|
+
padding: 0 0 0 15px;
|
8
|
+
border-left: 1px dashed gray;
|
9
|
+
list-style: none outside none;
|
10
|
+
}
|
11
|
+
|
12
|
+
li{
|
13
|
+
margin-bottom:5px;
|
14
|
+
position: relative;
|
15
|
+
list-style: none outside none;
|
16
|
+
}
|
17
|
+
|
18
|
+
a{
|
19
|
+
color:#000;
|
20
|
+
font-size:10pt;
|
21
|
+
font-weight:normal;
|
22
|
+
text-decoration:none;
|
23
|
+
line-height: 150%;
|
24
|
+
margin-left:30px;
|
25
|
+
margin-right:120px;
|
26
|
+
display:block;
|
27
|
+
&:hover{ text-decoration: underline; }
|
28
|
+
}
|
29
|
+
|
30
|
+
h4{
|
31
|
+
margin: 0;
|
32
|
+
a{
|
33
|
+
color: #333;
|
34
|
+
font-size: 14px;
|
35
|
+
&:hover{ background: #eee; }
|
36
|
+
}
|
37
|
+
}
|
38
|
+
|
39
|
+
p{
|
40
|
+
margin-right: 75px;
|
41
|
+
padding: 5px;
|
42
|
+
}
|
43
|
+
|
44
|
+
.handle{
|
45
|
+
background: transparent url(/assets/iconza/icons/move.png);
|
46
|
+
background-position: center center;
|
47
|
+
|
48
|
+
width: 16px;
|
49
|
+
height: 16px;
|
50
|
+
margin: 3px 10px 0 0;
|
51
|
+
float: left;
|
52
|
+
cursor: move;
|
53
|
+
|
54
|
+
&:hover{ background: transparent url(/assets/iconza/icons/red_move.png); }
|
55
|
+
}
|
56
|
+
|
57
|
+
.item{
|
58
|
+
padding: 5px;
|
59
|
+
background: #f7f7f7;
|
60
|
+
border-radius: 3px;
|
61
|
+
margin-bottom: 5px;
|
62
|
+
&:hover{ background: #eee; }
|
63
|
+
}
|
64
|
+
|
65
|
+
.placeholder {
|
66
|
+
background-color: #EEF;
|
67
|
+
border: 1px dashed blue;
|
68
|
+
}
|
69
|
+
|
70
|
+
.ui-nestedSortable-error {
|
71
|
+
background: #FAA;
|
72
|
+
color: #8a1f11;
|
73
|
+
}
|
74
|
+
|
75
|
+
.controls{
|
76
|
+
position: absolute;
|
77
|
+
top: 1px; right: 10px;
|
78
|
+
|
79
|
+
a{
|
80
|
+
width: 20px;
|
81
|
+
height: 20px;
|
82
|
+
display: block;
|
83
|
+
|
84
|
+
float: left;
|
85
|
+
padding: 5px;
|
86
|
+
cursor: pointer;
|
87
|
+
position: relative;
|
88
|
+
|
89
|
+
margin: 0 0 5px 5px;
|
90
|
+
overflow: hidden; zoom: 1;
|
91
|
+
|
92
|
+
&.new{
|
93
|
+
background: transparent url(/assets/iconza/icons/add.png) no-repeat scroll center center;
|
94
|
+
&:hover{ background: transparent url(/assets/iconza/icons/red_add.png) no-repeat scroll center center; }
|
95
|
+
}
|
96
|
+
&.edit{
|
97
|
+
background: transparent url(/assets/iconza/icons/edit.png) no-repeat scroll center center;
|
98
|
+
&:hover{ background: transparent url(/assets/iconza/icons/red_edit.png) no-repeat scroll center center; }
|
99
|
+
}
|
100
|
+
&.delete{
|
101
|
+
background: transparent url(/assets/iconza/icons/delete.png) no-repeat scroll center center;
|
102
|
+
&:hover{ background: transparent url(/assets/iconza/icons/red_delete.png) no-repeat scroll center center; }
|
103
|
+
}
|
104
|
+
}
|
105
|
+
}
|
106
|
+
}
|
@@ -1,38 +1,35 @@
|
|
1
1
|
.tree{
|
2
|
-
|
3
|
-
|
4
|
-
list-style:none;
|
5
|
-
}
|
2
|
+
margin:0; padding:0;
|
3
|
+
*{ margin: 0; padding: 0; }
|
6
4
|
|
7
5
|
ol{
|
6
|
+
margin: 0 0 0 13px;
|
7
|
+
padding: 0 0 0 15px;
|
8
|
+
border-left: 1px dashed gray;
|
9
|
+
list-style: none outside none;
|
10
|
+
}
|
8
11
|
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
font-size: 16px;
|
15
|
-
|
16
|
-
li{
|
17
|
-
font-size: 0.9em;
|
18
|
-
line-height: 130%;
|
19
|
-
margin-bottom: 5px;
|
20
|
-
font-weight: bold;
|
21
|
-
list-style:none;
|
22
|
-
list-style-position: outside;
|
23
|
-
}
|
24
|
-
|
25
|
-
ol{
|
26
|
-
border-left: 1px dashed #AAA;
|
27
|
-
padding-left: 10px;
|
28
|
-
margin-left: 15px;
|
29
|
-
margin-top: 5px;
|
12
|
+
li{
|
13
|
+
margin-bottom:5px;
|
14
|
+
position: relative;
|
15
|
+
list-style: none outside none;
|
16
|
+
}
|
30
17
|
|
31
|
-
|
32
|
-
|
18
|
+
a{
|
19
|
+
color: black;
|
20
|
+
font-size: 14px;
|
33
21
|
|
34
|
-
|
35
|
-
|
22
|
+
&:hover {
|
23
|
+
color: black;
|
24
|
+
background: #ddd;
|
36
25
|
}
|
37
26
|
}
|
27
|
+
|
28
|
+
.item{
|
29
|
+
padding: 5px;
|
30
|
+
background: #eee;
|
31
|
+
margin-bottom: 5px;
|
32
|
+
border-radius: 3px;
|
33
|
+
&:hover{ background: #ddd; }
|
34
|
+
}
|
38
35
|
}
|
@@ -5,8 +5,9 @@ module TheSortableTreeController
|
|
5
5
|
module DefineVariablesMethod
|
6
6
|
public
|
7
7
|
def the_define_common_variables
|
8
|
-
collection = self.class.to_s.split(':').last.sub(/Controller/,'').underscore.downcase # recipes
|
9
|
-
|
8
|
+
collection = self.class.to_s.split(':').last.sub(/Controller/,'').underscore.downcase # 'recipes'
|
9
|
+
collection = self.respond_to?(:sortable_collection) ? self.sortable_collection : collection # 'recipes'
|
10
|
+
variable = collection.singularize # 'recipe'
|
10
11
|
klass = self.respond_to?(:sortable_model) ? self.sortable_model : variable.classify.constantize # Recipe
|
11
12
|
["@#{variable}", collection, klass]
|
12
13
|
end
|
@@ -0,0 +1,62 @@
|
|
1
|
+
# DOC:
|
2
|
+
# We use Helper Methods for tree building,
|
3
|
+
# because it's faster than View Templates and Partials
|
4
|
+
|
5
|
+
# SECURITY note
|
6
|
+
# Prepare your data on server side for rendering
|
7
|
+
# or use h.html_escape(node.content)
|
8
|
+
# for escape potentially dangerous content
|
9
|
+
module RenderSortableTreeHelper
|
10
|
+
module Render
|
11
|
+
class << self
|
12
|
+
attr_accessor :h, :options
|
13
|
+
|
14
|
+
def render_node(h, options)
|
15
|
+
@h, @options = h, options
|
16
|
+
|
17
|
+
node = options[:node]
|
18
|
+
|
19
|
+
"
|
20
|
+
<li id='#{ node.id }_#{ options[:klass] }'>
|
21
|
+
<div class='item'>
|
22
|
+
<i class='handle'></i>
|
23
|
+
#{ show_link }
|
24
|
+
#{ controls }
|
25
|
+
</div>
|
26
|
+
#{ children }
|
27
|
+
</li>
|
28
|
+
"
|
29
|
+
end
|
30
|
+
|
31
|
+
def show_link
|
32
|
+
node = options[:node]
|
33
|
+
ns = options[:namespace]
|
34
|
+
url = h.url_for(ns + [node])
|
35
|
+
title_field = options[:title]
|
36
|
+
|
37
|
+
"<h4>#{ h.link_to(node.send(title_field), url) }</h4>"
|
38
|
+
end
|
39
|
+
|
40
|
+
def controls
|
41
|
+
node = options[:node]
|
42
|
+
|
43
|
+
edit_path = h.url_for(:controller => options[:klass].pluralize, :action => :edit, :id => node)
|
44
|
+
show_path = h.url_for(:controller => options[:klass].pluralize, :action => :show, :id => node)
|
45
|
+
|
46
|
+
"
|
47
|
+
<div class='controls'>
|
48
|
+
#{ h.link_to '', edit_path, :class => :edit }
|
49
|
+
#{ h.link_to '', show_path, :class => :delete, :data => { :confirm => 'Are you sure?' } }
|
50
|
+
</div>
|
51
|
+
"
|
52
|
+
end
|
53
|
+
|
54
|
+
def children
|
55
|
+
unless options[:children].blank?
|
56
|
+
"<ol class='nested_set'>#{ options[:children] }</ol>"
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
@@ -0,0 +1,45 @@
|
|
1
|
+
# DOC:
|
2
|
+
# We use Helper Methods for tree building,
|
3
|
+
# because it's faster than View Templates and Partials
|
4
|
+
|
5
|
+
# SECURITY note
|
6
|
+
# Prepare your data on server side for rendering
|
7
|
+
# or use h.html_escape(node.content)
|
8
|
+
# for escape potentially dangerous content
|
9
|
+
module RenderTreeHelper
|
10
|
+
class Render
|
11
|
+
class << self
|
12
|
+
attr_accessor :h, :options
|
13
|
+
|
14
|
+
def render_node(h, options)
|
15
|
+
@h, @options = h, options
|
16
|
+
|
17
|
+
node = options[:node]
|
18
|
+
"
|
19
|
+
<li>
|
20
|
+
<div class='item'>
|
21
|
+
#{ show_link }
|
22
|
+
</div>
|
23
|
+
#{ children }
|
24
|
+
</li>
|
25
|
+
"
|
26
|
+
end
|
27
|
+
|
28
|
+
def show_link
|
29
|
+
node = options[:node]
|
30
|
+
ns = options[:namespace]
|
31
|
+
url = h.url_for(ns + [node])
|
32
|
+
title_field = options[:title]
|
33
|
+
|
34
|
+
"<h4>#{ h.link_to(node.send(title_field), url) }</h4>"
|
35
|
+
end
|
36
|
+
|
37
|
+
def children
|
38
|
+
unless options[:children].blank?
|
39
|
+
"<ol>#{ options[:children] }</ol>"
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|