manatee 0.0.1.pre1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/Gemfile +23 -0
- data/LICENSE.txt +22 -0
- data/README.mdown +55 -0
- data/Rakefile +7 -0
- data/app/assets/javascripts/manatee.js +1 -0
- data/app/assets/javascripts/manatee/helpers.js +4 -0
- data/app/assets/javascripts/manatee/helpers/asset_tag.jsh.coffee +138 -0
- data/app/assets/javascripts/manatee/helpers/asset_url.jsh.coffee +90 -0
- data/app/assets/javascripts/manatee/helpers/csrf.jsh.coffee +5 -0
- data/app/assets/javascripts/manatee/helpers/date.jsh.coffee +54 -0
- data/app/assets/javascripts/manatee/helpers/debug.jsh.coffee +2 -0
- data/app/assets/javascripts/manatee/helpers/form.jsh.coffee +25 -0
- data/app/assets/javascripts/manatee/helpers/form_builder.jsh.coffee +24 -0
- data/app/assets/javascripts/manatee/helpers/form_options.jsh.coffee +267 -0
- data/app/assets/javascripts/manatee/helpers/form_tag.jsh.coffee +204 -0
- data/app/assets/javascripts/manatee/helpers/javascript.jsh.coffee +15 -0
- data/app/assets/javascripts/manatee/helpers/number.jsh.coffee +58 -0
- data/app/assets/javascripts/manatee/helpers/sanitize.jsh.coffee +5 -0
- data/app/assets/javascripts/manatee/helpers/tag.jsh.coffee +58 -0
- data/app/assets/javascripts/manatee/helpers/text.jsh.coffee +12 -0
- data/app/assets/javascripts/manatee/helpers/translation.jsh.coffee +7 -0
- data/app/assets/javascripts/manatee/helpers/url.jsh.coffee +36 -0
- data/app/assets/javascripts/manatee/helpers/util.jsh.coffee +41 -0
- data/app/assets/javascripts/manatee/rails_helpers.js +2 -0
- data/app/assets/javascripts/manatee/rails_helpers/routes.js.erb +195 -0
- data/app/assets/javascripts/manatee/rails_routes.js +1 -0
- data/app/assets/javascripts/manatee/renderer.js.erb +53 -0
- data/app/assets/javascripts/manatee_railsless.js +1 -0
- data/lib/manatee.rb +87 -0
- data/lib/manatee/config.rb +51 -0
- data/lib/manatee/handler.rb +45 -0
- data/lib/manatee/rails.rb +6 -0
- data/lib/manatee/rails/extensions.rb +23 -0
- data/lib/manatee/rails/handler.rb +16 -0
- data/lib/manatee/rails/hash_visitor.rb +35 -0
- data/lib/manatee/rails/helper.rb +9 -0
- data/lib/manatee/rails/resolver.rb +70 -0
- data/lib/manatee/rails/routes_compiler.rb +34 -0
- data/lib/manatee/sprockets.rb +8 -0
- data/lib/manatee/sprockets/jsh_processor_2x.rb +32 -0
- data/lib/manatee/sprockets/jsh_processor_3x.rb +31 -0
- data/lib/manatee/version.rb +3 -0
- data/manatee.gemspec +32 -0
- data/test/example/renderer.js.erb +8 -0
- data/test/example/translations.en.yml +410 -0
- data/test/example/views/index.jst.eco +12 -0
- data/test/helpers/asset_tag_test.rb +175 -0
- data/test/helpers/asset_url_test.rb +349 -0
- data/test/helpers/form_options_test.rb +718 -0
- data/test/helpers/form_tag_test.rb +387 -0
- data/test/helpers/javascript_test.rb +39 -0
- data/test/helpers/number_test.rb +111 -0
- data/test/helpers/tag_test.rb +71 -0
- data/test/support/dom_assertion.rb +49 -0
- data/test/support/view_test.rb +91 -0
- data/test/test_helper.rb +17 -0
- metadata +213 -0
@@ -0,0 +1,24 @@
|
|
1
|
+
|
2
|
+
# _to_partial_path
|
3
|
+
# button
|
4
|
+
# check_box
|
5
|
+
# collection_check_boxes
|
6
|
+
# collection_radio_buttons
|
7
|
+
# collection_select
|
8
|
+
# date_select
|
9
|
+
# datetime_select
|
10
|
+
# emitted_hidden_id?
|
11
|
+
# fields_for
|
12
|
+
# file_field
|
13
|
+
# grouped_collection_select
|
14
|
+
# hidden_field
|
15
|
+
# label
|
16
|
+
# multipart=
|
17
|
+
# new
|
18
|
+
# radio_button
|
19
|
+
# select
|
20
|
+
# submit
|
21
|
+
# time_select
|
22
|
+
# time_zone_select
|
23
|
+
# to_model
|
24
|
+
# to_partial_path
|
@@ -0,0 +1,267 @@
|
|
1
|
+
optionsNullSelector = (value) ->
|
2
|
+
false
|
3
|
+
|
4
|
+
optionsStringSelectorBuilder = (_reference) ->
|
5
|
+
reference = _reference
|
6
|
+
(value) ->
|
7
|
+
value.toString() && reference == value.toString()
|
8
|
+
|
9
|
+
optionsArraySelectorBuilder = (_reference)->
|
10
|
+
reference = []
|
11
|
+
for index, ref_value of _reference
|
12
|
+
reference.push ref_value.toString()
|
13
|
+
(value) ->
|
14
|
+
value && reference.indexOf(value.toString()) > -1
|
15
|
+
|
16
|
+
disabledAndSelector = (options, selected_selector, disabled_selector) ->
|
17
|
+
options['selected'] = true if selected_selector(options['value'])
|
18
|
+
options['disabled'] = true if disabled_selector(options['value'])
|
19
|
+
|
20
|
+
stringOption = (value, selected_selector, disabled_selector) ->
|
21
|
+
options = { value: value }
|
22
|
+
disabledAndSelector options, selected_selector, disabled_selector
|
23
|
+
@contentTag 'option', @htmlEscape(value), @htmlEscapeAttributes(options)
|
24
|
+
|
25
|
+
selectorForOptions = (reference) ->
|
26
|
+
reference = reference.toString() if typeof reference == 'number' || reference == true || reference == false
|
27
|
+
|
28
|
+
return [ reference, optionsNullSelector ] if typeof reference == 'function'
|
29
|
+
return [ optionsNullSelector, optionsNullSelector ] if reference == null || reference == undefined
|
30
|
+
return [ optionsStringSelectorBuilder(reference), optionsNullSelector ] if typeof reference == 'string'
|
31
|
+
return [ optionsArraySelectorBuilder(reference), optionsNullSelector ] if reference instanceof Array
|
32
|
+
|
33
|
+
[ selectorForOptions( reference['selected'] )[0], selectorForOptions( reference['disabled'] )[0] ]
|
34
|
+
|
35
|
+
collectionValueForSelector = (_selector, value_method) ->
|
36
|
+
selector = _selector
|
37
|
+
(value) ->
|
38
|
+
selector value[value_method]
|
39
|
+
|
40
|
+
selectorForOptionsFromCollection = (reference, _value_method) ->
|
41
|
+
value_method = _value_method
|
42
|
+
reference = reference.toString() if typeof reference == 'number' || reference == true || reference == false
|
43
|
+
|
44
|
+
return [ reference, optionsNullSelector ] if typeof reference == 'function'
|
45
|
+
return [ optionsNullSelector, optionsNullSelector ] if reference == null || reference == undefined
|
46
|
+
return [ collectionValueForSelector(optionsStringSelectorBuilder(reference), value_method), optionsNullSelector ] if typeof reference == 'string'
|
47
|
+
return [ collectionValueForSelector(optionsArraySelectorBuilder(reference), value_method), optionsNullSelector ] if reference instanceof Array
|
48
|
+
|
49
|
+
[ selectorForOptionsFromCollection( reference['selected'], _value_method )[0], selectorForOptionsFromCollection( reference['disabled'], _value_method )[0] ]
|
50
|
+
|
51
|
+
collectionFetcherBlock = (_accessor_method) ->
|
52
|
+
accessor_method = _accessor_method
|
53
|
+
if typeof accessor_method == 'function'
|
54
|
+
accessor_method
|
55
|
+
else
|
56
|
+
(object) ->
|
57
|
+
object[accessor_method]
|
58
|
+
|
59
|
+
extractNamePrefixAndMethod = ((object, method_or_prefix_and_method) ->
|
60
|
+
[ prefix, method ] = if typeof method_or_prefix_and_method == 'string'
|
61
|
+
class_name = object['_class'] || object['_type'] || object['class'] || object['type']
|
62
|
+
if class_name
|
63
|
+
class_name = class_name() if typeof class_name == 'function'
|
64
|
+
[ @underscore(class_name), method_or_prefix_and_method ]
|
65
|
+
else
|
66
|
+
[ null, method_or_prefix_and_method ]
|
67
|
+
else
|
68
|
+
method_or_prefix_and_method
|
69
|
+
|
70
|
+
name = if prefix then prefix + '[' + method + ']' else method
|
71
|
+
[ name, prefix, method ]).bind(this)
|
72
|
+
|
73
|
+
multipleHiddenInput = ((name, options) ->
|
74
|
+
multiple_hidden_input = if options['multiple']
|
75
|
+
name = name + '[]' if name[-2..-1] != '[]'
|
76
|
+
if options['include_hidden'] == true || options['include_hidden'] == undefined
|
77
|
+
@tag 'input', type: 'hidden', name: name, value: '', disabled: options['disabled']
|
78
|
+
else
|
79
|
+
''
|
80
|
+
else
|
81
|
+
''
|
82
|
+
options['include_hidden'] = undefined
|
83
|
+
[ name, multiple_hidden_input ]).bind(this)
|
84
|
+
|
85
|
+
|
86
|
+
|
87
|
+
helper 'optionsForSelect', (container, selectors) ->
|
88
|
+
return container.toString() if typeof container == 'string' || typeof value == 'number' || value == true || value == false
|
89
|
+
return container().toString() if typeof container == 'function'
|
90
|
+
|
91
|
+
[ selected_selector, disabled_selector ] = selectorForOptions selectors
|
92
|
+
|
93
|
+
unless container instanceof Array
|
94
|
+
array_container = []
|
95
|
+
for index, value of container
|
96
|
+
array_container.push [ index, value ]
|
97
|
+
container = array_container
|
98
|
+
|
99
|
+
select_options = ''
|
100
|
+
for index, value of container
|
101
|
+
value = value.toString() if typeof value == 'number' || value == true || value == false
|
102
|
+
value = '' if value == null
|
103
|
+
|
104
|
+
if typeof value == 'string'
|
105
|
+
select_options += stringOption.call(this, value, selected_selector, disabled_selector)
|
106
|
+
if value instanceof Array
|
107
|
+
[ content, value_option, options ] = if value.length == 3
|
108
|
+
[ value[0], value[1], @_clone(value[2]) ]
|
109
|
+
else if value.length == 2
|
110
|
+
if typeof value[1] == 'object'
|
111
|
+
[ value[0], value[0], @_clone(value[1]) ]
|
112
|
+
else
|
113
|
+
[ value[0], value[1], {} ]
|
114
|
+
else
|
115
|
+
[ value[0], value[0], {} ]
|
116
|
+
|
117
|
+
content = content.toString() if typeof content == 'number' || content == true || content == false
|
118
|
+
content ||= ''
|
119
|
+
|
120
|
+
value_option = value_option.toString() if typeof value_option == 'number' || value_option == true || value_option == false
|
121
|
+
options['value'] ||= value_option || content
|
122
|
+
|
123
|
+
disabledAndSelector options, selected_selector, disabled_selector
|
124
|
+
select_options += @contentTag 'option', @htmlEscape(content), @htmlEscapeAttributes(options)
|
125
|
+
|
126
|
+
select_options
|
127
|
+
|
128
|
+
helper 'optionsFromCollectionForSelect', (collection, value_method, text_method, selectors) ->
|
129
|
+
[ selected_selector, disabled_selector ] = selectorForOptionsFromCollection selectors, value_method
|
130
|
+
selectors = { selected: [], disabled: [] }
|
131
|
+
|
132
|
+
text_fetcher = collectionFetcherBlock text_method
|
133
|
+
value_fetcher = collectionFetcherBlock value_method
|
134
|
+
|
135
|
+
container = []
|
136
|
+
for index, object of collection
|
137
|
+
options = if object instanceof Array && object.length == 3
|
138
|
+
object[2]
|
139
|
+
else
|
140
|
+
{}
|
141
|
+
value = value_fetcher object
|
142
|
+
container.push [ text_fetcher(object), value, options ]
|
143
|
+
selectors['selected'].push(value) if selected_selector(object)
|
144
|
+
selectors['disabled'].push(value) if disabled_selector(object)
|
145
|
+
|
146
|
+
@optionsForSelect container, selectors
|
147
|
+
|
148
|
+
helper 'groupedOptionsForSelect', (grouped_options, selectors, options = {}) ->
|
149
|
+
[ selected_selector, disabled_selector ] = selectorForOptions selectors
|
150
|
+
|
151
|
+
options_for_select = ''
|
152
|
+
if options['prompt']
|
153
|
+
prompt = if typeof options['prompt'] == 'string' || options['prompt'] == 'number'
|
154
|
+
options['prompt'].toString()
|
155
|
+
else
|
156
|
+
@translate 'helpers.select.prompt', default: 'Please select'
|
157
|
+
options_for_select += @contentTag( 'option', @htmlEscape(prompt), { value: '' } )
|
158
|
+
|
159
|
+
for index, options_in_group of grouped_options
|
160
|
+
[ options_in_group, optgroup_options ] = if options['divider']
|
161
|
+
if grouped_options instanceof Array
|
162
|
+
[ options_in_group, { label: options['divider'] } ]
|
163
|
+
else
|
164
|
+
if grouped_options instanceof Array
|
165
|
+
optgroup_options = options_in_group[2] || {}
|
166
|
+
optgroup_options['label'] = options_in_group[0]
|
167
|
+
[ options_in_group[1], optgroup_options ]
|
168
|
+
else
|
169
|
+
[ options_in_group, { label: index } ]
|
170
|
+
|
171
|
+
options_for_select += @contentTag('optgroup', @optionsForSelect(options_in_group, selectors), @htmlEscapeAttributes(optgroup_options) )
|
172
|
+
|
173
|
+
options_for_select
|
174
|
+
|
175
|
+
helper 'optionGroupsFromCollectionForSelect', (collection, group_method, group_label_method, option_key_method, option_value_method, selectors) ->
|
176
|
+
group_fetcher = collectionFetcherBlock group_method
|
177
|
+
group_label_fetcher = collectionFetcherBlock group_label_method
|
178
|
+
|
179
|
+
options_for_select = ''
|
180
|
+
for index, object of collection
|
181
|
+
options_for_select += @contentTag('optgroup', @optionsFromCollectionForSelect(group_fetcher(object), option_key_method, option_value_method, selectors), { label: @htmlEscape( group_label_fetcher object ) } )
|
182
|
+
|
183
|
+
options_for_select
|
184
|
+
|
185
|
+
# EXPLAIN: When object is a Ruby object, two things can happen:
|
186
|
+
# 1. If it don't responds to method [], class if infered by .class method
|
187
|
+
# 2. If it responds to method [], class is infered by object['_class'] || object['_type'] || object['class'] || object['type'] in that order
|
188
|
+
#
|
189
|
+
# IMPORTANT: Removed 'required' option, it seems too much cryptic to be used
|
190
|
+
# TODO: Add index option
|
191
|
+
helper 'selectForObject', (object, method_or_prefix_and_method, choices_or_options, options_or_choices) ->
|
192
|
+
[ name, prefix, method ] = extractNamePrefixAndMethod object, method_or_prefix_and_method
|
193
|
+
|
194
|
+
[ choices, options ] = if typeof options_or_choices == 'function'
|
195
|
+
[ options_or_choices, @_clone( choices_or_options || {} ) ]
|
196
|
+
else
|
197
|
+
[ choices_or_options, @_clone( options_or_choices || {} ) ]
|
198
|
+
|
199
|
+
[ name, multiple_hidden_input ] = multipleHiddenInput name, options
|
200
|
+
|
201
|
+
# reference = options['selected'] || object[method]
|
202
|
+
# options['selected'] = undefined
|
203
|
+
# pre_selector = selectorForOptions( reference )[0]
|
204
|
+
# selector = (value) ->
|
205
|
+
# selected = pre_selector value
|
206
|
+
# # Welcome black magic!
|
207
|
+
# options['prompt'] = undefined if selected
|
208
|
+
# selected
|
209
|
+
|
210
|
+
selector = if options['selected'] == null
|
211
|
+
undefined
|
212
|
+
else
|
213
|
+
reference = options['selected'] || object[method]
|
214
|
+
pre_selector = selectorForOptions( reference )[0]
|
215
|
+
(value) ->
|
216
|
+
selected = pre_selector value
|
217
|
+
# Welcome black magic!
|
218
|
+
options['prompt'] = undefined if selected
|
219
|
+
selected
|
220
|
+
options['selected'] = undefined
|
221
|
+
|
222
|
+
if typeof choices == 'object'
|
223
|
+
unless choices instanceof Array
|
224
|
+
array_choices = []
|
225
|
+
for index, value of choices
|
226
|
+
array_choices.push [ index, value ]
|
227
|
+
choices = array_choices
|
228
|
+
|
229
|
+
if choices[1] instanceof Array
|
230
|
+
choices_options = {}
|
231
|
+
choices_options['divider'] = options['divider'] unless options['divider'] == undefined
|
232
|
+
options['divider'] = undefined
|
233
|
+
return multiple_hidden_input + @selectTag(name, @groupedOptionsForSelect(choices, selector, choices_options), options)
|
234
|
+
|
235
|
+
multiple_hidden_input + @selectTag(name, @optionsForSelect(choices, selector), options)
|
236
|
+
|
237
|
+
helper 'collectionSelectForObject', (object, method_or_prefix_and_method, collection, value_method, text_method, options)->
|
238
|
+
[ name, prefix, method ] = extractNamePrefixAndMethod object, method_or_prefix_and_method
|
239
|
+
|
240
|
+
options = if options != undefined then @_clone(options) else {}
|
241
|
+
|
242
|
+
[ name, multiple_hidden_input ] = multipleHiddenInput name, options
|
243
|
+
|
244
|
+
selector = if options['selected'] == null
|
245
|
+
undefined
|
246
|
+
else
|
247
|
+
reference = options['selected'] || object[method]
|
248
|
+
pre_selector = selectorForOptionsFromCollection( reference, value_method )[0]
|
249
|
+
(value) ->
|
250
|
+
selected = pre_selector value
|
251
|
+
# Welcome black magic!
|
252
|
+
options['prompt'] = undefined if selected
|
253
|
+
selected
|
254
|
+
options['selected'] = undefined
|
255
|
+
|
256
|
+
choices = @optionsFromCollectionForSelect collection, value_method, text_method, selector
|
257
|
+
multiple_hidden_input + @selectTag(name, choices, options)
|
258
|
+
|
259
|
+
# TODO: Finish form_options helpers
|
260
|
+
# collection_check_boxes
|
261
|
+
# collection_radio_buttons
|
262
|
+
# collection_select
|
263
|
+
# grouped_collection_select
|
264
|
+
|
265
|
+
# TODO: Think about implementing or not time zone helpers
|
266
|
+
# time_zone_options_for_select
|
267
|
+
# time_zone_select
|
@@ -0,0 +1,204 @@
|
|
1
|
+
sanitizeToId = (name) ->
|
2
|
+
name = name.toString()
|
3
|
+
name = name[0..-3] if name[-2..-1] == '[]'
|
4
|
+
name.replace(/\]/g, '').replace /[^-a-zA-Z0-9:.]/g, '_'
|
5
|
+
|
6
|
+
humanizeToLabel = (name) ->
|
7
|
+
sanitized = name.toString().replace(/\]/g, '').replace /[^-a-zA-Z0-9:.]/g, ' '
|
8
|
+
sanitized[0].toUpperCase() + sanitized.slice(1);
|
9
|
+
|
10
|
+
fieldTagBuilder = (_type) ->
|
11
|
+
type = _type
|
12
|
+
(name, value = null, options = {}) ->
|
13
|
+
html_options = @_clone options
|
14
|
+
html_options['type'] = type
|
15
|
+
html_options['name'] = name
|
16
|
+
html_options['id'] = sanitizeToId(name) if html_options['id'] == undefined
|
17
|
+
html_options['value'] ||= value if value
|
18
|
+
@tag 'input', html_options
|
19
|
+
|
20
|
+
for i, type of ['color', 'date', 'datetime', 'email', 'file', 'hidden', 'month', 'number', 'password', 'range', 'search', 'text', 'time', 'url', 'week']
|
21
|
+
helper (type+'FieldTag'), fieldTagBuilder.call(this, type)
|
22
|
+
|
23
|
+
helper 'datetimeLocalFieldTag', fieldTagBuilder.call(this, 'datetime-local')
|
24
|
+
helper 'telephoneFieldTag', fieldTagBuilder.call(this, 'tel')
|
25
|
+
|
26
|
+
alias 'phoneFieldTag', 'telephoneFieldTag'
|
27
|
+
|
28
|
+
helper 'buttonTag', (content_or_options = null, options_or_content = {}) ->
|
29
|
+
[content, options] = @_contentOrOptions content_or_options, options_or_content
|
30
|
+
|
31
|
+
content = content.call(this) if typeof content == 'function'
|
32
|
+
options['name'] ||= 'button'
|
33
|
+
options['type'] ||= 'submit'
|
34
|
+
@contentTag 'button', (content || 'Button'), options
|
35
|
+
|
36
|
+
helper 'checkBoxTag', (name, value = 1, checked = false, options = {}) ->
|
37
|
+
html_options = @_clone options
|
38
|
+
html_options['type'] = 'checkbox'
|
39
|
+
html_options['name'] = name
|
40
|
+
html_options['value'] = value
|
41
|
+
html_options['checked'] = 'checked' if checked
|
42
|
+
html_options['id'] = sanitizeToId(name) if html_options['id'] == undefined
|
43
|
+
@tag 'input', html_options
|
44
|
+
alias 'checkboxTag', 'checkBoxTag'
|
45
|
+
|
46
|
+
helper 'fieldSetTag', (legend = null, content_or_options = '', options_or_content = {}) ->
|
47
|
+
[content, options] = if typeof legend == 'function'
|
48
|
+
content_and_options = [ legend, {} ]
|
49
|
+
legend = null
|
50
|
+
content_and_options
|
51
|
+
else
|
52
|
+
@_contentOrOptions content_or_options, options_or_content, ''
|
53
|
+
|
54
|
+
content = content.call(this) if typeof content == 'function'
|
55
|
+
content = @contentTag('legend', legend) + content if legend && legend != ''
|
56
|
+
@contentTag 'fieldset', content, options
|
57
|
+
|
58
|
+
alias 'fieldsetTag', 'fieldSetTag'
|
59
|
+
|
60
|
+
buildFormOptions = (url, options) ->
|
61
|
+
method = (options['method'] || 'post').toLowerCase()
|
62
|
+
options['action'] = url
|
63
|
+
options['method'] = if method == 'get' then 'get' else 'post'
|
64
|
+
options['accept-charset'] ||= 'UTF-8'
|
65
|
+
|
66
|
+
if options['multipart']
|
67
|
+
options['enctype'] = 'multipart/form-data'
|
68
|
+
options['multipart'] = undefined
|
69
|
+
|
70
|
+
options['data-remote'] = 'true' if options['remote']
|
71
|
+
options['remote'] = undefined
|
72
|
+
|
73
|
+
enforce_utf8 = options['enforce_utf8'] == undefined || options['enforce_utf8']
|
74
|
+
options['enforce_utf8'] = undefined
|
75
|
+
|
76
|
+
authenticity_token = if ( @_context.protectFromForgery && options['authenticity_token'] == undefined ) || options['authenticity_token']
|
77
|
+
if options['authenticity_token'] == undefined || options['authenticity_token'] == true then @csrfToken else options['authenticity_token']
|
78
|
+
else
|
79
|
+
false
|
80
|
+
options['authenticity_token'] = undefined
|
81
|
+
|
82
|
+
[ method, enforce_utf8, authenticity_token ]
|
83
|
+
|
84
|
+
buildFormContent = (content, method, enforce_utf8, authenticity_token) ->
|
85
|
+
form_content = ''
|
86
|
+
|
87
|
+
method_enforcer = method != 'get' && method != 'post'
|
88
|
+
form_content += '<div style="display:none">'
|
89
|
+
if enforce_utf8 || authenticity_token || method_enforcer
|
90
|
+
form_content += @utf8EnforcerTag() if enforce_utf8
|
91
|
+
form_content += @methodHiddenTag(method) if method_enforcer
|
92
|
+
form_content += @authenticityTokenTag(authenticity_token) if authenticity_token
|
93
|
+
form_content += '</div>'
|
94
|
+
|
95
|
+
content = content.call(this) if typeof content == 'function'
|
96
|
+
|
97
|
+
form_content + content
|
98
|
+
|
99
|
+
helper 'formTag', (url = '/', content_or_options, options_or_content) ->
|
100
|
+
[content, options] = if typeof url == 'function'
|
101
|
+
content_and_options = [url, {}]
|
102
|
+
url = '/'
|
103
|
+
content_and_options
|
104
|
+
else
|
105
|
+
@_contentOrOptions content_or_options, options_or_content, ''
|
106
|
+
|
107
|
+
[ method, enforce_utf8, authenticity_token ] = buildFormOptions.call this, url, options
|
108
|
+
|
109
|
+
@contentTag 'form', buildFormContent.call(this, content, method, enforce_utf8, authenticity_token), options
|
110
|
+
|
111
|
+
helper 'imageSubmitTag', (source, options = {}) ->
|
112
|
+
options = @_clone options
|
113
|
+
options['src'] ||= @imagePath source
|
114
|
+
options['type'] ||= 'image'
|
115
|
+
options['alt'] ||= @imageAlt source
|
116
|
+
@tag 'input', options
|
117
|
+
|
118
|
+
helper 'labelTag', (name, content_or_options = null, options_or_content = {}) ->
|
119
|
+
[content, options] = if typeof name == 'function'
|
120
|
+
content_and_options = [name, {}]
|
121
|
+
name = null
|
122
|
+
content_and_options
|
123
|
+
else
|
124
|
+
@_contentOrOptions content_or_options, options_or_content
|
125
|
+
|
126
|
+
content ||= humanizeToLabel name
|
127
|
+
options['for'] ||= sanitizeToId(name) if name
|
128
|
+
@contentTag 'label', content, options
|
129
|
+
|
130
|
+
helper 'radioButtonTag', (name, value, checked = false, options = {}) ->
|
131
|
+
options = @_clone options
|
132
|
+
options['type'] = 'radio'
|
133
|
+
options['name'] = name
|
134
|
+
options['value'] = value
|
135
|
+
options['checked'] = 'checked' if checked
|
136
|
+
options['id'] ||= sanitizeToId(name) + '_' + sanitizeToId(value)
|
137
|
+
@tag 'input', options
|
138
|
+
|
139
|
+
helper 'selectTag', (name, option_tags = '', options = {}) ->
|
140
|
+
options = @_clone options
|
141
|
+
|
142
|
+
if options['include_blank']
|
143
|
+
blank_content = if options['include_blank'] == true then '' else @htmlEscape( options['include_blank'] )
|
144
|
+
option_tags = @contentTag('option', blank_content, value: '') + option_tags
|
145
|
+
options['include_blank'] = undefined
|
146
|
+
|
147
|
+
if options['prompt']
|
148
|
+
prompt_content = if options['prompt'] == true then @translate('helpers.select.prompt', default: 'Please select') else @htmlEscape( options['prompt'] )
|
149
|
+
option_tags = @contentTag('option', prompt_content, value: '') + option_tags
|
150
|
+
options['prompt'] = undefined
|
151
|
+
|
152
|
+
html_name = if options['multiple']
|
153
|
+
unless name.toString()[-2..-1] == '[]'
|
154
|
+
name + '[]'
|
155
|
+
else
|
156
|
+
name
|
157
|
+
|
158
|
+
options['multiple'] = undefined if options['multiple'] == false
|
159
|
+
|
160
|
+
options['id'] = sanitizeToId(name) if options['id'] == undefined
|
161
|
+
options['name'] ||= name
|
162
|
+
@contentTag 'select', option_tags, options
|
163
|
+
|
164
|
+
helper 'submitTag', (value = "Save changes", options = {}) ->
|
165
|
+
options = @_clone options
|
166
|
+
options['type'] ||= 'submit'
|
167
|
+
options['name'] ||= 'commit'
|
168
|
+
options['value'] ||= value
|
169
|
+
@tag 'input', options
|
170
|
+
|
171
|
+
handleSizeAttribute = (options) ->
|
172
|
+
if typeof options['size'] == 'string'
|
173
|
+
size = options['size'].toLowerCase()
|
174
|
+
if (/\d+X\d+/i).test(size)
|
175
|
+
xIndex = size.lastIndexOf('x');
|
176
|
+
options['cols'] ||= size.substring 0, xIndex
|
177
|
+
options['rows'] ||= size.substring xIndex+1
|
178
|
+
options['size'] = undefined
|
179
|
+
|
180
|
+
helper 'textAreaTag', (name, content_or_options = '', options_or_content = {}) ->
|
181
|
+
[content, options] = @_contentOrOptions content_or_options, options_or_content
|
182
|
+
handleSizeAttribute options
|
183
|
+
|
184
|
+
content = content.call(this) if typeof content == 'function'
|
185
|
+
content ||= ''
|
186
|
+
|
187
|
+
if typeof options['escape'] == 'undefined' || options['escape']
|
188
|
+
content = @htmlEscape content
|
189
|
+
options['escape'] = undefined
|
190
|
+
|
191
|
+
options['id'] = sanitizeToId(name) if options['id'] == undefined
|
192
|
+
options['name'] ||= name
|
193
|
+
@contentTag 'textarea', "\n"+content, options
|
194
|
+
|
195
|
+
alias 'textareaTag', 'textAreaTag'
|
196
|
+
|
197
|
+
helper 'authenticityTokenTag', (authenticity_token) ->
|
198
|
+
@tag 'input', type: 'hidden', name: @_context.requestForgeryProtectionToken, value: authenticity_token
|
199
|
+
|
200
|
+
helper 'methodHiddenTag', (method) ->
|
201
|
+
@tag 'input', type: 'hidden', name: '_method', value: method
|
202
|
+
|
203
|
+
helper 'utf8EnforcerTag', () ->
|
204
|
+
@tag 'input', type: 'hidden', name: 'utf8', value: "✓"
|