zena 1.2.2 → 1.2.3
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/History.txt +25 -0
- data/app/controllers/documents_controller.rb +3 -25
- data/app/controllers/nodes_controller.rb +34 -24
- data/app/controllers/user_sessions_controller.rb +5 -4
- data/app/controllers/versions_controller.rb +44 -17
- data/app/models/acl.rb +2 -7
- data/app/models/group.rb +6 -2
- data/app/models/link.rb +14 -0
- data/app/models/node.rb +2 -2
- data/app/models/site.rb +13 -4
- data/app/models/text_document.rb +1 -1
- data/app/models/user.rb +11 -2
- data/app/models/virtual_class.rb +1 -1
- data/app/views/groups/_form.rhtml +6 -6
- data/app/views/nodes/render_error.rhtml +15 -0
- data/app/views/templates/document_create_tabs/_file.rhtml +1 -1
- data/app/views/templates/document_create_tabs/_import.rhtml +1 -1
- data/app/views/templates/document_create_tabs/_template.rhtml +1 -1
- data/app/views/templates/document_create_tabs/_text_document.rhtml +1 -1
- data/app/views/templates/edit_tabs/_title.rhtml +1 -1
- data/app/views/zafu/default/Node-admin.zafu +1 -1
- data/bricks/acls/zena/test/integration/acl_integration_test.rb +2 -2
- data/bricks/acls/zena/test/unit/acl_test.rb +2 -1
- data/bricks/fs_skin/zena/migrate/20110702010330_add_fs_skin_to_idx_templates.rb +1 -0
- data/bricks/fs_skin/zena/skins/blog/img/style.css +4 -4
- data/bricks/grid/lib/bricks/grid.rb +9 -3
- data/bricks/passenger/zena/deploy.rb +2 -1
- data/bricks/pdf/lib/bricks/pdf.rb +1 -1
- data/bricks/tags/zena/test/zafu/tags.yml +5 -1
- data/bricks/zena/zena/migrate/20120904071601_change_link_status_to_float.rb +13 -0
- data/config/bricks.yml +10 -10
- data/config/deploy.rb +1 -5
- data/config/gems.yml +2 -2
- data/db/init/base/skins/default/Node.zafu +7 -3
- data/db/init/base/skins/default/notes.zafu +3 -1
- data/lib/zafu/all.rb +0 -9
- data/lib/zafu/compiler.rb +0 -4
- data/lib/zafu/controller_methods.rb +0 -2
- data/lib/zafu/handler.rb +0 -5
- data/lib/zafu/markup.rb +4 -6
- data/lib/zafu/ordered_hash.rb +3 -2
- data/lib/zafu/parsing_rules.rb +1 -3
- data/lib/zafu/process/ajax.rb +4 -2
- data/lib/zafu/process/context.rb +34 -4
- data/lib/zafu/process/forms.rb +2 -2
- data/lib/zafu/process/ruby_less_processing.rb +5 -10
- data/lib/zafu/template.rb +0 -2
- data/lib/zafu/test_helper.rb +0 -2
- data/lib/zafu/view_methods.rb +0 -1
- data/lib/zafu.rb +1 -1
- data/lib/zena/acts/secure_node.rb +5 -4
- data/lib/zena/console.rb +19 -17
- data/lib/zena/core_ext/string.rb +3 -2
- data/lib/zena/deploy/app_init.rhtml +6 -1
- data/lib/zena/deploy/httpd.rhtml +16 -13
- data/lib/zena/deploy/stats.vhost.rhtml +1 -1
- data/lib/zena/deploy/vhost.rhtml +31 -11
- data/lib/zena/deploy/vhost_ssl_redir.rhtml +12 -0
- data/lib/zena/deploy/vhost_www.rhtml +1 -1
- data/lib/zena/deploy.rb +55 -11
- data/lib/zena/info.rb +1 -1
- data/lib/zena/parser/zazen_rules.rb +18 -9
- data/lib/zena/routes.rb +1 -3
- data/lib/zena/site_worker.rb +8 -1
- data/lib/zena/use/ajax.rb +29 -3
- data/lib/zena/use/ancestry.rb +2 -1
- data/lib/zena/use/authlogic.rb +12 -18
- data/lib/zena/use/context.rb +1 -1
- data/lib/zena/use/dates.rb +28 -18
- data/lib/zena/use/display.rb +49 -7
- data/lib/zena/use/forms.rb +51 -18
- data/lib/zena/use/html_tags.rb +6 -6
- data/lib/zena/use/i18n.rb +13 -4
- data/lib/zena/use/image_builder.rb +2 -0
- data/lib/zena/use/query_builder.rb +39 -14
- data/lib/zena/use/query_link.rb +57 -0
- data/lib/zena/use/query_node.rb +68 -32
- data/lib/zena/use/relations.rb +25 -15
- data/lib/zena/use/rendering.rb +66 -15
- data/lib/zena/use/upload.rb +34 -5
- data/lib/zena/use/urls.rb +28 -25
- data/lib/zena/use/version_hash.rb +14 -2
- data/lib/zena/use/zafu_safe_definitions.rb +72 -3
- data/lib/zena/use/zazen.rb +16 -4
- data/lib/zena.rb +1 -0
- data/public/javascripts/grid.js +213 -64
- data/public/javascripts/raphael.js +10 -0
- data/public/javascripts/zena.js +146 -22
- data/public/stylesheets/reset.css +12 -12
- data/public/stylesheets/zena.css +1 -1
- data/test/custom_queries/complex.host.yml +19 -0
- data/test/fixtures/files/TestNode.zafu +40 -4
- data/test/functional/nodes_controller_test.rb +84 -39
- data/test/functional/versions_controller_test.rb +2 -2
- data/test/integration/navigation_test.rb +61 -35
- data/test/integration/query_node/basic.yml +7 -7
- data/test/integration/query_node/comments.yml +1 -1
- data/test/integration/query_node/complex.yml +3 -3
- data/test/integration/query_node/filters.yml +32 -8
- data/test/integration/query_node/idx_key_value.yml +10 -10
- data/test/integration/query_node/idx_scope.yml +7 -7
- data/test/integration/query_node/relations.yml +4 -4
- data/test/integration/zafu_compiler/ajax.yml +19 -11
- data/test/integration/zafu_compiler/apphelper.yml +1 -1
- data/test/integration/zafu_compiler/asset.yml +2 -2
- data/test/integration/zafu_compiler/comments.yml +1 -1
- data/test/integration/zafu_compiler/dates.yml +1 -1
- data/test/integration/zafu_compiler/display.yml +49 -21
- data/test/integration/zafu_compiler/eval.yml +4 -4
- data/test/integration/zafu_compiler/forms.yml +25 -11
- data/test/integration/zafu_compiler/i18n.yml +5 -0
- data/test/integration/zafu_compiler/meta.yml +3 -3
- data/test/integration/zafu_compiler/query.yml +27 -9
- data/test/integration/zafu_compiler/relations.yml +9 -9
- data/test/integration/zafu_compiler/roles.yml +6 -6
- data/test/integration/zafu_compiler/rubyless.yml +7 -2
- data/test/integration/zafu_compiler/safe_definitions.yml +33 -4
- data/test/integration/zafu_compiler/security.yml +46 -1
- data/test/integration/zafu_compiler/urls.yml +28 -13
- data/test/integration/zafu_compiler/user.yml +12 -7
- data/test/integration/zafu_compiler/zafu_attributes.yml +1 -1
- data/test/integration/zafu_compiler/zazen.yml +5 -5
- data/test/integration/zafu_compiler_test.rb +18 -0
- data/test/selenium/Filter/filter3.rsel +20 -0
- data/test/selenium/Filter/filter4.rsel +20 -0
- data/test/sites/zena/versions.yml +2 -0
- data/test/unit/exif_data_test.rb +6 -1
- data/test/unit/group_test.rb +18 -3
- data/test/unit/node_test.rb +0 -7
- data/test/unit/project_test.rb +4 -0
- data/test/unit/relation_proxy_test.rb +2 -2
- data/test/unit/remote_test.rb +0 -9
- data/test/unit/role_test.rb +1 -1
- data/test/unit/string_hash_test.rb +1 -1
- data/test/unit/text_document_test.rb +13 -13
- data/test/unit/zena/use/html_tags_test.rb +6 -6
- data/test/unit/zena/use/rendering_test.rb +20 -10
- data/test/unit/zena/use/urls_test.rb +21 -18
- data/test/unit/zena/use/zafu_template_test.rb +0 -5
- data/test/unit/zena/use/zazen_test.rb +25 -25
- data/zena.gemspec +63 -57
- metadata +136 -130
- data/test/functional/nodes_controller_commit_test.rb +0 -67
|
@@ -1,10 +1,66 @@
|
|
|
1
1
|
module Zena
|
|
2
2
|
module Use
|
|
3
3
|
module ZafuSafeDefinitions
|
|
4
|
+
# This is a dummy class to declare safe parameters on params.
|
|
4
5
|
class ParamsDictionary
|
|
5
6
|
include RubyLess
|
|
6
7
|
safe_method ['[]', Symbol] => {:class => String, :nil => true}
|
|
7
8
|
end
|
|
9
|
+
|
|
10
|
+
# This class is used to return Array params ['foo', 'bar'] as ['foo', 'bar'], not 'foobar'.
|
|
11
|
+
# It also splits String params 'foo, bar' as ['foo', 'bar'].
|
|
12
|
+
class AParamsDictionary
|
|
13
|
+
def initialize(params)
|
|
14
|
+
@aparams = {}
|
|
15
|
+
params.each do |k, v|
|
|
16
|
+
@aparams[k.to_sym] = transform(v)
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def [](key)
|
|
20
|
+
@aparams[key.to_sym] || []
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
include RubyLess
|
|
25
|
+
safe_method ['[]', Symbol] => {:class => [String], :nil => false}
|
|
26
|
+
|
|
27
|
+
private
|
|
28
|
+
def transform(v)
|
|
29
|
+
if v.kind_of?(Array)
|
|
30
|
+
v.map {|k| k.to_s}.select{|e| !e.blank?}
|
|
31
|
+
elsif v.kind_of?(String)
|
|
32
|
+
v.split(',').map(&:strip).select{|e| !e.blank?}
|
|
33
|
+
else
|
|
34
|
+
[]
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
# This class is used to access nested Hash params {:key => 'foo'}.
|
|
40
|
+
class HParamsDictionary
|
|
41
|
+
def initialize(params)
|
|
42
|
+
@params = {}
|
|
43
|
+
params.each do |k, v|
|
|
44
|
+
@params[k.to_sym] = transform(v)
|
|
45
|
+
end
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
def [](key)
|
|
49
|
+
@params[key.to_sym] || {}
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
include RubyLess
|
|
53
|
+
safe_method ['[]', Symbol] => {:class => ParamsDictionary, :nil => false}
|
|
54
|
+
|
|
55
|
+
private
|
|
56
|
+
def transform(v)
|
|
57
|
+
if v.kind_of?(Hash)
|
|
58
|
+
v
|
|
59
|
+
else
|
|
60
|
+
{}
|
|
61
|
+
end
|
|
62
|
+
end
|
|
63
|
+
end
|
|
8
64
|
|
|
9
65
|
module ViewMethods
|
|
10
66
|
include RubyLess
|
|
@@ -96,10 +152,13 @@ module Zena
|
|
|
96
152
|
end
|
|
97
153
|
|
|
98
154
|
safe_method :params => ParamsDictionary
|
|
155
|
+
safe_method :aparams => AParamsDictionary
|
|
156
|
+
safe_method :hparams => HParamsDictionary
|
|
157
|
+
|
|
99
158
|
safe_method :now => {:method => 'Time.now', :class => Time}
|
|
100
159
|
safe_method :string_hash => {:method => 'StringHash.new', :class => StringHash}
|
|
101
160
|
safe_method [:string_hash, Hash] => {:method => 'StringHash.from_hash', :class => StringHash}
|
|
102
|
-
safe_method [:h, String] => {:class => String, :
|
|
161
|
+
safe_method [:h, String] => {:class => String, :accept_nil => true}
|
|
103
162
|
safe_method_for String, [:gsub, Regexp, String] => {:class => String, :pre_processor => true}
|
|
104
163
|
safe_method_for String, :upcase => {:class => String, :pre_processor => true}
|
|
105
164
|
safe_method_for String, :strip => {:class => String, :pre_processor => true}
|
|
@@ -107,8 +166,8 @@ module Zena
|
|
|
107
166
|
safe_method_for String, :url_name => {:class => String, :pre_processor => true, :method => :url_name}
|
|
108
167
|
safe_method_for String, :to_i => {:class => Number, :pre_processor => true}
|
|
109
168
|
safe_method_for String, :to_s => {:class => String, :pre_processor => true}
|
|
110
|
-
safe_method_for String, [:limit, Number] => {:class => String, :pre_processor => true}
|
|
111
|
-
safe_method_for String, [:limit, Number, String] => {:class => String, :pre_processor => true}
|
|
169
|
+
safe_method_for String, [:limit, Number] => {:class => String, :pre_processor => true, :html_safe => true}
|
|
170
|
+
safe_method_for String, [:limit, Number, String] => {:class => String, :pre_processor => true, :html_safe => true}
|
|
112
171
|
safe_method_for String, :to_f => {:class => Number, :pre_processor => true}
|
|
113
172
|
safe_method_for String, :to_json => {:class => String, :pre_processor => true}
|
|
114
173
|
safe_method_for String, [:split, String] => {:class => [String], :pre_processor => true}
|
|
@@ -120,6 +179,8 @@ module Zena
|
|
|
120
179
|
safe_method_for Number, :fmt => {:class => String, :pre_processor => true}
|
|
121
180
|
safe_method_for Number, [:fmt, Number] => {:class => String, :pre_processor => true}
|
|
122
181
|
|
|
182
|
+
safe_method_for Range, :to_a => {:class => [Number], :pre_processor => true}
|
|
183
|
+
|
|
123
184
|
safe_method_for NilClass, :to_f => {:class => Number, :pre_processor => true}
|
|
124
185
|
safe_method_for NilClass, :to_i => {:class => Number, :pre_processor => true}
|
|
125
186
|
safe_method_for NilClass, :to_json => {:class => String, :pre_processor => true}
|
|
@@ -168,6 +229,14 @@ module Zena
|
|
|
168
229
|
def zafu_max(a, b)
|
|
169
230
|
[a, b].max
|
|
170
231
|
end
|
|
232
|
+
|
|
233
|
+
def aparams
|
|
234
|
+
@aparams ||= AParamsDictionary.new(params)
|
|
235
|
+
end
|
|
236
|
+
|
|
237
|
+
def hparams
|
|
238
|
+
@hparams ||= HParamsDictionary.new(params)
|
|
239
|
+
end
|
|
171
240
|
end # ViewMethods
|
|
172
241
|
|
|
173
242
|
|
data/lib/zena/use/zazen.rb
CHANGED
|
@@ -65,7 +65,9 @@ module Zena
|
|
|
65
65
|
if text[0..0] == ' '
|
|
66
66
|
text = "\n\n#{text}"
|
|
67
67
|
end
|
|
68
|
-
|
|
68
|
+
@zazen = ZazenParser.new(text, :helper=>self)
|
|
69
|
+
res = @zazen.render(opt)
|
|
70
|
+
@zazen = nil
|
|
69
71
|
if no_p && !text.include?("\n")
|
|
70
72
|
res.gsub(%r{\A<p>|</p>\Z},'')
|
|
71
73
|
else
|
|
@@ -75,6 +77,14 @@ module Zena
|
|
|
75
77
|
rescue Timeout::Error
|
|
76
78
|
return %Q{<span class='parser_error'>#{_('Could not render text (Timeout error)')}</span>}
|
|
77
79
|
end
|
|
80
|
+
|
|
81
|
+
def raw_content(txt)
|
|
82
|
+
if @zazen
|
|
83
|
+
@zazen.raw_content(txt)
|
|
84
|
+
else
|
|
85
|
+
txt
|
|
86
|
+
end
|
|
87
|
+
end
|
|
78
88
|
|
|
79
89
|
# TODO: test
|
|
80
90
|
def zazen_diff(text1, text2, opt={})
|
|
@@ -172,7 +182,7 @@ module Zena
|
|
|
172
182
|
def make_image(opts)
|
|
173
183
|
id, style, link, mode, title = opts[:id], opts[:style], opts[:link], opts[:mode], opts[:title]
|
|
174
184
|
mode ||= 'std' # default mode
|
|
175
|
-
img = opts[:node] || secure(
|
|
185
|
+
img = opts[:node] || secure(Node) { Node.find_by_zip(id) }
|
|
176
186
|
|
|
177
187
|
return "<span class='unknownLink'>#{_('unknown document')}</span>" unless img
|
|
178
188
|
|
|
@@ -182,11 +192,13 @@ module Zena
|
|
|
182
192
|
title = img.summary if title == ""
|
|
183
193
|
|
|
184
194
|
image = img_tag(img, :mode=>mode, :host=>opts[:host])
|
|
195
|
+
|
|
196
|
+
return "<span class='unknownLink'>#{_('invalid document')}</span>" unless image
|
|
185
197
|
|
|
186
198
|
unless link
|
|
187
|
-
if id[0..0] == "0" || !img.kind_of?(Image)
|
|
199
|
+
if id[0..0] == "0" || (!img.kind_of?(Image) && !(image =~ /ZAZENBLOCKCODE/))
|
|
188
200
|
# if the id starts with '0' or it is not an Image, link to data
|
|
189
|
-
link = zen_path(img, :format => img.ext)
|
|
201
|
+
link = zen_path(img, :format => img.prop['ext'])
|
|
190
202
|
link_tag = "<a class='popup' href='#{link}' target='_blank'>"
|
|
191
203
|
end
|
|
192
204
|
end
|
data/lib/zena.rb
CHANGED
|
@@ -315,6 +315,7 @@ EXT_TYPE = [
|
|
|
315
315
|
[ "jpe" , "image/jpeg" ],
|
|
316
316
|
[ "jpeg" , "image/jpeg" ],
|
|
317
317
|
[ "js" , "application/x-javascript" ],
|
|
318
|
+
[ "js" , "text/javascript" ],
|
|
318
319
|
[ "kar" , "audio/midi" ],
|
|
319
320
|
[ "latex" , "application/x-latex" ],
|
|
320
321
|
[ "lha" , "application/octet-stream" ],
|
data/public/javascripts/grid.js
CHANGED
|
@@ -18,27 +18,10 @@ Grid.log = function(what, msg) {
|
|
|
18
18
|
}
|
|
19
19
|
|
|
20
20
|
Grid.changed = function(cell, val, prev, skip_html) {
|
|
21
|
-
if (!skip_html) {
|
|
22
|
-
if (val.value == val.show) {
|
|
23
|
-
cell.innerHTML = val.value
|
|
24
|
-
} else {
|
|
25
|
-
cell.innerHTML = val.show
|
|
26
|
-
cell.setAttribute('data-v', val.value)
|
|
27
|
-
}
|
|
28
|
-
}
|
|
29
21
|
var row = cell.up('tr')
|
|
30
22
|
var table = row.up('table')
|
|
31
23
|
var grid = table.grid
|
|
32
|
-
|
|
33
|
-
if (cell.orig_value == val.value) {
|
|
34
|
-
cell.removeClassName('changed')
|
|
35
|
-
if (row.select('.changed').length == 0) {
|
|
36
|
-
row.removeClassName('changed')
|
|
37
|
-
}
|
|
38
|
-
} else {
|
|
39
|
-
cell.addClassName('changed')
|
|
40
|
-
row.addClassName('changed')
|
|
41
|
-
}
|
|
24
|
+
|
|
42
25
|
var pos = Grid.pos(cell)
|
|
43
26
|
|
|
44
27
|
var attr, id
|
|
@@ -53,6 +36,34 @@ Grid.changed = function(cell, val, prev, skip_html) {
|
|
|
53
36
|
id = Grid.buildObj(grid, row)
|
|
54
37
|
}
|
|
55
38
|
}
|
|
39
|
+
|
|
40
|
+
if (!skip_html) {
|
|
41
|
+
var show_h = grid.show[attr] || {}
|
|
42
|
+
if (cell.hasAttribute('data-v')) cell.setAttribute('data-v', val.value)
|
|
43
|
+
cell.innerHTML = show_h[val.value] || val.show
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
if (grid.onChange) {
|
|
47
|
+
// onChange can be used to recompute total (so cell value should be set first)
|
|
48
|
+
var v = val.value
|
|
49
|
+
val = grid.onChange(cell, val, attr)
|
|
50
|
+
if (!val) return
|
|
51
|
+
if (v != val.value) {
|
|
52
|
+
cell.innerHTML = val.show
|
|
53
|
+
if (cell.hasAttribute('data-v')) cell.setAttribute('data-v', val.value)
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
if (prev.value == val.value) return;
|
|
58
|
+
if (cell.orig_value == val.value) {
|
|
59
|
+
cell.removeClassName('changed')
|
|
60
|
+
if (row.select('.changed').length == 0) {
|
|
61
|
+
row.removeClassName('changed')
|
|
62
|
+
}
|
|
63
|
+
} else {
|
|
64
|
+
cell.addClassName('changed')
|
|
65
|
+
row.addClassName('changed')
|
|
66
|
+
}
|
|
56
67
|
|
|
57
68
|
var change = {
|
|
58
69
|
id: id,
|
|
@@ -102,27 +113,28 @@ Grid.buildObj = function(grid, row) {
|
|
|
102
113
|
}
|
|
103
114
|
|
|
104
115
|
Grid.closeCell = function(e) {
|
|
105
|
-
if (Grid.
|
|
116
|
+
if (Grid.no_blur) return
|
|
106
117
|
var cell = e.tagName ? e : e.element().up()
|
|
107
118
|
var table = cell.up('table')
|
|
108
119
|
var prev = cell.prev_value
|
|
109
120
|
var val = Grid.getValue(cell)
|
|
110
121
|
|
|
111
|
-
if (table.grid.
|
|
122
|
+
if (table.grid.is_list) {
|
|
112
123
|
if (val.value == 'on') {
|
|
113
124
|
cell.addClassName('on')
|
|
114
125
|
} else {
|
|
115
126
|
cell.removeClassName('on')
|
|
116
127
|
}
|
|
117
|
-
} else {
|
|
118
|
-
cell.removeClassName('input')
|
|
119
128
|
}
|
|
129
|
+
cell.removeClassName('input')
|
|
120
130
|
|
|
121
131
|
Grid.changed(cell, val, prev)
|
|
122
132
|
|
|
123
133
|
if (table.grid.input) {
|
|
124
134
|
// single attribute table, serialize in input field
|
|
125
135
|
table.grid.input.value = Grid.serialize(table)
|
|
136
|
+
} else if (table.grid.autoSave) {
|
|
137
|
+
Grid.save(table.id)
|
|
126
138
|
}
|
|
127
139
|
}
|
|
128
140
|
|
|
@@ -150,9 +162,9 @@ Grid.paste = function(event) {
|
|
|
150
162
|
bottom: "<textarea style='position:fixed; top:0; left:10100px;' id='grid_p_" + table.grid.id + "'></textarea>"
|
|
151
163
|
});
|
|
152
164
|
paster = $("grid_p_" + table.grid.id);
|
|
153
|
-
Grid.
|
|
165
|
+
Grid.no_blur = true // prevent original input blur
|
|
154
166
|
paster.focus();
|
|
155
|
-
Grid.
|
|
167
|
+
Grid.no_blur = false
|
|
156
168
|
}
|
|
157
169
|
setTimeout(function() {
|
|
158
170
|
var text
|
|
@@ -264,6 +276,11 @@ Grid.keydown = function(event) {
|
|
|
264
276
|
return false
|
|
265
277
|
} else if (cell.childElements().first().tagName == 'SELECT' && event.shiftKey) {
|
|
266
278
|
return
|
|
279
|
+
} else if (grid.autoSave) {
|
|
280
|
+
// this will close cell
|
|
281
|
+
var i = cell.childElements().first()
|
|
282
|
+
if (i) i.blur()
|
|
283
|
+
return
|
|
267
284
|
}
|
|
268
285
|
var pos = Grid.pos(cell);
|
|
269
286
|
// go to next row
|
|
@@ -306,18 +323,39 @@ Grid.keydown = function(event) {
|
|
|
306
323
|
}
|
|
307
324
|
|
|
308
325
|
Grid.isReadOnly = function(cell) {
|
|
309
|
-
return cell.select('a').length > 0 || cell.getAttribute('data-m') == 'r'
|
|
326
|
+
return cell.select('a').length > 0 || cell.getAttribute('data-m') == 'r' || cell.hasClassName('action')
|
|
310
327
|
}
|
|
311
328
|
|
|
312
|
-
Grid.
|
|
313
|
-
if (
|
|
329
|
+
Grid.closeCheckbox = function() {
|
|
330
|
+
if (Grid.need_close) {
|
|
331
|
+
Grid.closeCell(Grid.need_close)
|
|
332
|
+
Grid.need_close = false
|
|
333
|
+
}
|
|
334
|
+
}
|
|
335
|
+
Grid.openCell = function(cell, get_next) {
|
|
336
|
+
var get_next = get_next == undefined ? true : get_next
|
|
337
|
+
Grid.closeCheckbox()
|
|
338
|
+
|
|
339
|
+
if (cell.hasClassName('input')) return;
|
|
340
|
+
if (Grid.isReadOnly(cell)) {
|
|
341
|
+
if (get_next) {
|
|
342
|
+
var n = Element.next(cell) || cell.up().nextSiblings().first().childElements()[0]
|
|
343
|
+
if (n) {
|
|
344
|
+
return Grid.openCell(n)
|
|
345
|
+
} else {
|
|
346
|
+
return false
|
|
347
|
+
}
|
|
348
|
+
} else {
|
|
349
|
+
return false
|
|
350
|
+
}
|
|
351
|
+
}
|
|
314
352
|
var val = Grid.getValue(cell)
|
|
315
353
|
cell.prev_value = val;
|
|
316
354
|
|
|
317
355
|
var value = val.value
|
|
318
356
|
var table = cell.up('table')
|
|
319
357
|
|
|
320
|
-
if (table.grid.
|
|
358
|
+
if (table.grid.is_list) {
|
|
321
359
|
if (value == 'on') {
|
|
322
360
|
cell.setAttribute('data-v', 'off')
|
|
323
361
|
} else {
|
|
@@ -353,16 +391,23 @@ Grid.openCell = function(cell) {
|
|
|
353
391
|
input = cell.childElements()[0]
|
|
354
392
|
input.value = value
|
|
355
393
|
}
|
|
394
|
+
|
|
356
395
|
input.setStyle({
|
|
357
396
|
width: w + 'px',
|
|
358
397
|
height: h + 'px'
|
|
359
398
|
})
|
|
360
|
-
|
|
399
|
+
|
|
400
|
+
if (input.type == 'checkbox') {
|
|
401
|
+
Grid.need_close = cell
|
|
402
|
+
} else {
|
|
403
|
+
input.observe('blur', Grid.closeCell)
|
|
404
|
+
}
|
|
361
405
|
input.observe('keydown', Grid.keydown)
|
|
362
406
|
input.observe('paste', Grid.paste)
|
|
363
407
|
input.focus()
|
|
364
408
|
input.select()
|
|
365
409
|
}
|
|
410
|
+
return true
|
|
366
411
|
}
|
|
367
412
|
|
|
368
413
|
Grid.click = function(event) {
|
|
@@ -371,13 +416,19 @@ Grid.click = function(event) {
|
|
|
371
416
|
var table = cell.up('table')
|
|
372
417
|
if (row.hasClassName('action')) {
|
|
373
418
|
Grid.action(event, cell, row, true)
|
|
419
|
+
Event.stop(event)
|
|
374
420
|
} else if (cell.hasClassName('action')) {
|
|
375
421
|
Grid.action(event, cell, row, false)
|
|
422
|
+
Event.stop(event)
|
|
376
423
|
} else if (cell.tagName == 'TH' && !table.grid.attr_name) {
|
|
377
424
|
// sort
|
|
378
425
|
Grid.sort(cell)
|
|
426
|
+
Event.stop(event)
|
|
379
427
|
} else {
|
|
380
|
-
|
|
428
|
+
if (event.element().tagName == 'INPUT') return;
|
|
429
|
+
if (Grid.openCell(cell, false)) {
|
|
430
|
+
Event.stop(event)
|
|
431
|
+
}
|
|
381
432
|
}
|
|
382
433
|
}
|
|
383
434
|
|
|
@@ -438,10 +489,15 @@ Grid.copy = function(cell) {
|
|
|
438
489
|
|
|
439
490
|
Grid.addRow = function(table, row) {
|
|
440
491
|
// insert row below
|
|
441
|
-
var row_str = '<tr>'
|
|
442
|
-
var
|
|
443
|
-
|
|
444
|
-
row_str = row_str +
|
|
492
|
+
var row_str = '<tr>'
|
|
493
|
+
var grid = table.grid
|
|
494
|
+
if (grid.newRow) {
|
|
495
|
+
row_str = row_str + $(grid.newRow).innerHTML
|
|
496
|
+
} else {
|
|
497
|
+
var cells = row.childElements()
|
|
498
|
+
for (var i = 0; i < cells.length -1; i++) {
|
|
499
|
+
row_str = row_str + '<td></td>'
|
|
500
|
+
}
|
|
445
501
|
}
|
|
446
502
|
row_str = row_str + Grid.Buttons(table.grid) + '</tr>';
|
|
447
503
|
row.insert({
|
|
@@ -723,26 +779,37 @@ Grid.make = function(table, opts) {
|
|
|
723
779
|
if (table.grid) return;
|
|
724
780
|
Grid.grid_c++;
|
|
725
781
|
Grid.grids[Grid.grid_c] = table;
|
|
726
|
-
|
|
782
|
+
var grid = {
|
|
727
783
|
changes: [],
|
|
728
784
|
id: Grid.grid_c,
|
|
729
|
-
helper_id: table.getAttribute('data-helper'),
|
|
730
|
-
fdate: table.getAttribute('data-fdate'),
|
|
785
|
+
helper_id: opts.helper || table.getAttribute('data-helper'),
|
|
786
|
+
fdate: opts.fdate || table.getAttribute('data-fdate'),
|
|
787
|
+
newRow: opts.newRow,
|
|
731
788
|
counter: 0, // Used to create dom_ids for new objects
|
|
732
789
|
onSuccess: opts.onSuccess,
|
|
733
790
|
onFailure: opts.onFailure || Grid.onFailure,
|
|
734
791
|
onStart: opts.onStart || Grid.onStart,
|
|
792
|
+
onChange: opts.onChange,
|
|
793
|
+
// Save on cell close
|
|
794
|
+
autoSave: opts.autoSave || false,
|
|
795
|
+
sort: opts.sort == undefined ? true : opts.sort,
|
|
735
796
|
add: opts.add || opts.add == undefined,
|
|
736
797
|
remove: opts.remove || opts.remove == undefined,
|
|
737
798
|
keydown: opts.keydown,
|
|
799
|
+
show: opts.show || {},
|
|
738
800
|
};
|
|
801
|
+
table.grid = grid
|
|
802
|
+
grid.table = table
|
|
739
803
|
|
|
740
804
|
// Detect type.
|
|
741
|
-
|
|
742
|
-
|
|
805
|
+
grid.attr_name = table.getAttribute('data-a')
|
|
806
|
+
grid.list_name = table.getAttribute('data-l')
|
|
807
|
+
// Reverse list name (update columns instead of rows)
|
|
808
|
+
grid.rlist_name = table.getAttribute('data-r')
|
|
809
|
+
grid.is_list = grid.list_name || grid.rlist_name
|
|
743
810
|
|
|
744
811
|
var empty = false;
|
|
745
|
-
if (
|
|
812
|
+
if (grid.attr_name && table.select('th').length == 0) {
|
|
746
813
|
empty = true;
|
|
747
814
|
var msg = table.getAttribute('data-msg') || "type to edit";
|
|
748
815
|
table.innerHTML = "<tr><th>" + msg + "</th></tr><tr><td></td></tr>";
|
|
@@ -752,27 +819,34 @@ Grid.make = function(table, opts) {
|
|
|
752
819
|
Grid.addButtons(table)
|
|
753
820
|
|
|
754
821
|
|
|
755
|
-
if (
|
|
822
|
+
if (grid.attr_name) {
|
|
823
|
+
table.insert({
|
|
824
|
+
after: "<p class='grid_btn'><a class='undo' href='javascript:' onclick='Grid.undo(" + Grid.grid_c + ")'>undo</a></p>"
|
|
825
|
+
});
|
|
756
826
|
// If we have an attr_name, rows and columns are
|
|
757
827
|
// serialized as json in a single field.
|
|
758
828
|
table.insert({
|
|
759
|
-
after: "<input type='hidden' id='grid_a_" + Grid.grid_c + "' name='" +
|
|
829
|
+
after: "<input type='hidden' id='grid_a_" + Grid.grid_c + "' name='" + grid.attr_name + "'/>"
|
|
760
830
|
});
|
|
761
|
-
|
|
762
|
-
if (!empty)
|
|
831
|
+
grid.input = $("grid_a_" + Grid.grid_c);
|
|
832
|
+
if (!empty) grid.input.value = Grid.serialize(table);
|
|
763
833
|
} else {
|
|
834
|
+
// Otherwise each row is a new object and each column
|
|
835
|
+
// corresponds to a different attribute (defined in the
|
|
836
|
+
// 'th' of the table).
|
|
764
837
|
var rows = table.select('tr')
|
|
765
838
|
for (var i = 1; i < rows.length; i++) {
|
|
766
|
-
if (
|
|
839
|
+
if (rows[i].id || rows[i].getAttribute('data-m') == 'r') {
|
|
840
|
+
// do not create object
|
|
841
|
+
} else {
|
|
767
842
|
Grid.buildObj(table.grid, rows[i])
|
|
768
843
|
}
|
|
769
844
|
}
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
});
|
|
845
|
+
if (!grid.autoSave) {
|
|
846
|
+
table.insert({
|
|
847
|
+
after: "<p class='grid_btn'><a class='save' href='javascript:' onclick='Grid.save(" + Grid.grid_c + ")'>save</a> <a class='undo' href='javascript:' onclick='Grid.undo(" + Grid.grid_c + ")'>undo</a></p>"
|
|
848
|
+
});
|
|
849
|
+
}
|
|
776
850
|
}
|
|
777
851
|
|
|
778
852
|
table.observe('click', Grid.click);
|
|
@@ -795,22 +869,22 @@ Grid.clearChanges = function(list, id) {
|
|
|
795
869
|
}
|
|
796
870
|
|
|
797
871
|
Grid.isChanged = function(elem) {
|
|
798
|
-
|
|
799
|
-
var grid = table.grid
|
|
800
|
-
// buildObj adds a row per new object on load
|
|
801
|
-
return grid.changes.length > (table.attr_name ? 0 : table.select('tr').length - 1)
|
|
872
|
+
return $$('#'+$(elem).id+' .changed').length > 0
|
|
802
873
|
}
|
|
803
874
|
|
|
804
|
-
|
|
805
875
|
Grid.save = function(grid_id) {
|
|
806
876
|
// do not run on GUI thread
|
|
807
877
|
setTimeout(function() {
|
|
808
|
-
|
|
878
|
+
Grid.closeCheckbox()
|
|
879
|
+
var table = Grid.grids[grid_id] || $(grid_id)
|
|
809
880
|
var grid = table.grid
|
|
810
881
|
var data = Grid.compact(grid.changes)
|
|
811
882
|
if (grid.list_name) {
|
|
812
883
|
data = Grid.dataForList(grid, data)
|
|
884
|
+
} else if (grid.rlist_name) {
|
|
885
|
+
data = Grid.dataForReverseList(grid, data)
|
|
813
886
|
}
|
|
887
|
+
|
|
814
888
|
var todo_count = data.keys().length
|
|
815
889
|
var done_count = 0
|
|
816
890
|
if (grid.onStart) {
|
|
@@ -822,8 +896,9 @@ Grid.save = function(grid_id) {
|
|
|
822
896
|
operations.put = (operations.put || 0) + 1
|
|
823
897
|
}
|
|
824
898
|
})
|
|
825
|
-
if (!grid.onStart(operations)) return
|
|
899
|
+
if (!grid.onStart(operations, data)) return
|
|
826
900
|
}
|
|
901
|
+
|
|
827
902
|
data.each(function(pair) {
|
|
828
903
|
var id = pair.key
|
|
829
904
|
var changes = pair.value
|
|
@@ -833,6 +908,7 @@ Grid.save = function(grid_id) {
|
|
|
833
908
|
attrs['node['+pair.key+']'] = pair.value
|
|
834
909
|
}
|
|
835
910
|
})
|
|
911
|
+
|
|
836
912
|
if (changes._new) {
|
|
837
913
|
new Ajax.Request('/nodes', {
|
|
838
914
|
parameters: attrs,
|
|
@@ -864,7 +940,7 @@ Grid.save = function(grid_id) {
|
|
|
864
940
|
method: 'post'
|
|
865
941
|
});
|
|
866
942
|
} else {
|
|
867
|
-
new Ajax.Request('/nodes/' + id.replace('
|
|
943
|
+
new Ajax.Request('/nodes/' + id.replace(/^[^0-9]+/,''), {
|
|
868
944
|
parameters: attrs,
|
|
869
945
|
onSuccess: function(transport) {
|
|
870
946
|
done_count++
|
|
@@ -884,7 +960,9 @@ Grid.save = function(grid_id) {
|
|
|
884
960
|
}
|
|
885
961
|
|
|
886
962
|
Grid.undo = function(grid_id, skip_undone) {
|
|
887
|
-
|
|
963
|
+
Grid.closeCheckbox()
|
|
964
|
+
|
|
965
|
+
var table = Grid.grids[grid_id] || $(grid_id)
|
|
888
966
|
var grid = table.grid
|
|
889
967
|
var changes = grid.changes
|
|
890
968
|
var last = changes.last()
|
|
@@ -911,7 +989,7 @@ Grid.undo = function(grid_id, skip_undone) {
|
|
|
911
989
|
[cell, cell.up()].invoke('addClassName', 'changed')
|
|
912
990
|
}
|
|
913
991
|
cell.addClassName('undone')
|
|
914
|
-
if (grid.
|
|
992
|
+
if (grid.is_list) {
|
|
915
993
|
cell.setAttribute('data-v', value)
|
|
916
994
|
if (value == 'on') {
|
|
917
995
|
cell.addClassName('on')
|
|
@@ -927,6 +1005,11 @@ Grid.undo = function(grid_id, skip_undone) {
|
|
|
927
1005
|
changes.pop()
|
|
928
1006
|
}
|
|
929
1007
|
|
|
1008
|
+
if (grid.input) {
|
|
1009
|
+
// single attribute table, serialize in input field
|
|
1010
|
+
grid.input.value = Grid.serialize(table)
|
|
1011
|
+
}
|
|
1012
|
+
|
|
930
1013
|
if (!skip_undone) {
|
|
931
1014
|
setTimeout(function() {
|
|
932
1015
|
table.select('.undone').invoke('removeClassName', 'undone')
|
|
@@ -965,6 +1048,9 @@ Grid.test = function() {
|
|
|
965
1048
|
// detects which rows have changes and builds the full list of
|
|
966
1049
|
// ids in the format some_relation_ids:"123,345,888,432". Other fields
|
|
967
1050
|
// are kept as is.
|
|
1051
|
+
// If update_relation_for is 'column', the list of ids is reversed (they
|
|
1052
|
+
// contain the row ids) and we build operation for setting the relation
|
|
1053
|
+
// with the column id).
|
|
968
1054
|
Grid.dataForList = function(grid, data) {
|
|
969
1055
|
var res = {}
|
|
970
1056
|
var list_name = grid.list_name
|
|
@@ -999,6 +1085,43 @@ Grid.dataForList = function(grid, data) {
|
|
|
999
1085
|
return $H(res)
|
|
1000
1086
|
}
|
|
1001
1087
|
|
|
1088
|
+
Grid.dataForReverseList = function(grid, data) {
|
|
1089
|
+
var res = {}
|
|
1090
|
+
var rlist_name = grid.rlist_name
|
|
1091
|
+
// {"id_184": {"147": "on"}, "id_193": {"147": "on", "146": "off"}}
|
|
1092
|
+
data.each(function(pair) {
|
|
1093
|
+
var def = pair.value
|
|
1094
|
+
for (var key in def) {
|
|
1095
|
+
if (parseInt(key) + '' == key) {
|
|
1096
|
+
// number key = there is a change in the list
|
|
1097
|
+
var col = res[key]
|
|
1098
|
+
if (!col) {
|
|
1099
|
+
// build full column update operation
|
|
1100
|
+
col = {id:key}
|
|
1101
|
+
res[key] = col
|
|
1102
|
+
var list = []
|
|
1103
|
+
var pos = grid.pos[key]
|
|
1104
|
+
var rows = grid.table.childElements()[0].select('tr')
|
|
1105
|
+
for(var i = rows.length - 1; i >= 0; i--) {
|
|
1106
|
+
var row = rows[i]
|
|
1107
|
+
if (row.getAttribute('data-m') != 'r') {
|
|
1108
|
+
var cell = rows[i].childElements()[pos]
|
|
1109
|
+
if (cell && cell.hasClassName('on')) {
|
|
1110
|
+
list.push(row.id.sub(/^[^\d]+/,''))
|
|
1111
|
+
}
|
|
1112
|
+
}
|
|
1113
|
+
}
|
|
1114
|
+
col[rlist_name] = list.join(',')
|
|
1115
|
+
}
|
|
1116
|
+
} else {
|
|
1117
|
+
// ignore other attributes
|
|
1118
|
+
}
|
|
1119
|
+
}
|
|
1120
|
+
})
|
|
1121
|
+
|
|
1122
|
+
return $H(res)
|
|
1123
|
+
}
|
|
1124
|
+
|
|
1002
1125
|
Grid.notify = function(table, changes) {
|
|
1003
1126
|
var rows = table.childElements()[0].select('tr')
|
|
1004
1127
|
var grid = table.grid
|
|
@@ -1011,10 +1134,11 @@ Grid.notify = function(table, changes) {
|
|
|
1011
1134
|
} else {
|
|
1012
1135
|
row = $(obj_id)
|
|
1013
1136
|
}
|
|
1014
|
-
|
|
1137
|
+
|
|
1015
1138
|
var change = changes[obj_id]
|
|
1016
1139
|
for (var attr in change) {
|
|
1017
1140
|
if (attr == 'id') continue
|
|
1141
|
+
|
|
1018
1142
|
if (attr == grid.list_name) {
|
|
1019
1143
|
var list_on = change[attr].split(/,/)
|
|
1020
1144
|
var cells = row.childElements()
|
|
@@ -1031,11 +1155,36 @@ Grid.notify = function(table, changes) {
|
|
|
1031
1155
|
}
|
|
1032
1156
|
cell.prev_value = undefined
|
|
1033
1157
|
cell.removeClassName('error')
|
|
1158
|
+
if (cell.hasClassName('changed')) cell.addClassName('saved')
|
|
1034
1159
|
cell.removeClassName('changed')
|
|
1035
|
-
|
|
1160
|
+
}
|
|
1161
|
+
}
|
|
1162
|
+
} else if (attr == grid.rlist_name) {
|
|
1163
|
+
var list_on = change[attr].split(/,/)
|
|
1164
|
+
var pos = grid.pos[obj_id.sub(/^[^\d]+/,'')]
|
|
1165
|
+
var rows = grid.table.childElements()[0].select('tr')
|
|
1166
|
+
for(var i = rows.length - 1; i >= 0; i--) {
|
|
1167
|
+
var row = rows[i]
|
|
1168
|
+
if (row.getAttribute('data-m') != 'r') {
|
|
1169
|
+
var cell = row.childElements()[pos]
|
|
1170
|
+
if (list_on.indexOf(row.id.sub(/^[^\d]+/,'')) >= 0) {
|
|
1171
|
+
cell.orig_value = 'on'
|
|
1172
|
+
cell.setAttribute('data-v', 'on')
|
|
1173
|
+
} else {
|
|
1174
|
+
cell.orig_value = 'off'
|
|
1175
|
+
cell.setAttribute('data-v', 'off')
|
|
1176
|
+
}
|
|
1177
|
+
cell.prev_value = undefined
|
|
1178
|
+
cell.removeClassName('error')
|
|
1179
|
+
if (cell.hasClassName('changed')) cell.addClassName('saved')
|
|
1180
|
+
cell.removeClassName('changed')
|
|
1181
|
+
if (row.select('.changed').length == 0) {
|
|
1182
|
+
row.removeClassName('changed')
|
|
1183
|
+
}
|
|
1036
1184
|
}
|
|
1037
1185
|
}
|
|
1038
1186
|
} else {
|
|
1187
|
+
var cells = row.childElements()
|
|
1039
1188
|
var cell
|
|
1040
1189
|
var i = pos[attr]
|
|
1041
1190
|
if (i == undefined) continue
|
|
@@ -1059,7 +1208,7 @@ Grid.notify = function(table, changes) {
|
|
|
1059
1208
|
// later
|
|
1060
1209
|
setTimeout(function() {
|
|
1061
1210
|
table.select('.saved').invoke('removeClassName', 'saved')
|
|
1062
|
-
},
|
|
1211
|
+
}, 600)
|
|
1063
1212
|
}
|
|
1064
1213
|
|
|
1065
1214
|
Grid.simulateClick = function(l) {
|