the_sortable_tree 1.9.4 → 2.0.0
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/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
|