zena 1.2.2 → 1.2.3
Sign up to get free protection for your applications and to get access to all the features.
- 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) {
|