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
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 5b3ae1dccef7459bc5825b9dabd5b4db64da0363
|
4
|
+
data.tar.gz: d018454a024148225f218275b15f551a5ec22a01
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 4aca549a3f05eda647491d3b56ca2547c83e52cf576a73ff09c9f80f05ef1dfe5964b243d1f978b231e50e221d2cd7ddf66ec1843f0f457cf798b8fc70602af1
|
7
|
+
data.tar.gz: 717fd9676054a86710b75e9e25402312adcd9213e384a195888b1f64842939387b40fcfbd3ec46c014fa9ae34c32af371203fb0374fb8082561201f6bcfa9e04
|
data/Gemfile
ADDED
@@ -0,0 +1,23 @@
|
|
1
|
+
source 'https://rubygems.org'
|
2
|
+
|
3
|
+
# Specify your gem's dependencies in manatee.gemspec
|
4
|
+
gemspec
|
5
|
+
|
6
|
+
gem 'i18n-js', path: '../i18n-js'
|
7
|
+
|
8
|
+
gem 'sprockets', '~> 2'
|
9
|
+
# gem 'sprockets', '~> 3'
|
10
|
+
|
11
|
+
platform :ruby do
|
12
|
+
gem 'therubyracer'
|
13
|
+
end
|
14
|
+
|
15
|
+
platform :jruby do
|
16
|
+
gem 'therubyrhyno'
|
17
|
+
end
|
18
|
+
|
19
|
+
group :development, :test do
|
20
|
+
gem 'eco'
|
21
|
+
gem 'pry'
|
22
|
+
gem 'test-unit'
|
23
|
+
end
|
data/LICENSE.txt
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
Copyright (c) 2014 Dalton
|
2
|
+
|
3
|
+
MIT License
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
6
|
+
a copy of this software and associated documentation files (the
|
7
|
+
"Software"), to deal in the Software without restriction, including
|
8
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
9
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
10
|
+
permit persons to whom the Software is furnished to do so, subject to
|
11
|
+
the following conditions:
|
12
|
+
|
13
|
+
The above copyright notice and this permission notice shall be
|
14
|
+
included in all copies or substantial portions of the Software.
|
15
|
+
|
16
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
17
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
18
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
19
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
20
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
21
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
22
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.mdown
ADDED
@@ -0,0 +1,55 @@
|
|
1
|
+
# Manatee
|
2
|
+
|
3
|
+
Javascript Template Render [for Rails]?
|
4
|
+
|
5
|
+
## Installation
|
6
|
+
|
7
|
+
Use only with TheRubyRacer or TheRubyRhyno gem.
|
8
|
+
|
9
|
+
Other ExecJS adapter like Node.js, doesn't translates Ruby lambdas to Javascript functions, and some have unicode characters issues
|
10
|
+
|
11
|
+
Add this line to your application's Gemfile:
|
12
|
+
|
13
|
+
```ruby
|
14
|
+
gem 'manatee'
|
15
|
+
```
|
16
|
+
|
17
|
+
And then execute:
|
18
|
+
|
19
|
+
$ bundle
|
20
|
+
|
21
|
+
Or install it yourself as:
|
22
|
+
|
23
|
+
$ gem install manatee
|
24
|
+
|
25
|
+
## Usage
|
26
|
+
|
27
|
+
__It's unstable, may change.__
|
28
|
+
|
29
|
+
### With Rails
|
30
|
+
|
31
|
+
Create an initializer file, for example, config/initializers/manatee.rb with:
|
32
|
+
|
33
|
+
```ruby
|
34
|
+
Manatee.subscribe_on_rails
|
35
|
+
# Manatee.config do |config|
|
36
|
+
# config.some_conf = 'value'
|
37
|
+
# config.lambda_conf{ |c| 'value' }
|
38
|
+
# end
|
39
|
+
```
|
40
|
+
|
41
|
+
If need to change an option, for now, check out at lib/manatee.rb to see all options, I dont want to document that right now.
|
42
|
+
|
43
|
+
As you see, I'm lazy enough to not explain how to use it... Figure it out by yourself.
|
44
|
+
|
45
|
+
### Without Rails
|
46
|
+
|
47
|
+
Check out test file test/test\_helper.rb, and lib/manatee.rb to see all options and understand how to use it with any app that uses sprockets.
|
48
|
+
|
49
|
+
## Contributing
|
50
|
+
|
51
|
+
1. Fork it ( https://github.com/akidog/manatee/fork )
|
52
|
+
2. Create your feature branch (`git checkout -b my-new-feature`)
|
53
|
+
3. Commit your changes (`git commit -am 'Add some feature'`)
|
54
|
+
4. Push to the branch (`git push origin my-new-feature`)
|
55
|
+
5. Create a new Pull Request
|
data/Rakefile
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
//= require ./manatee/rails_helpers
|
@@ -0,0 +1,138 @@
|
|
1
|
+
helper 'audioTag', (paths...) ->
|
2
|
+
options = paths[paths.length - 1]
|
3
|
+
if typeof options == 'object'
|
4
|
+
paths.pop()
|
5
|
+
else
|
6
|
+
options = {}
|
7
|
+
path_options = {}
|
8
|
+
path_options['format'] = options['format']
|
9
|
+
options['format'] = undefined
|
10
|
+
if paths.length == 1
|
11
|
+
options['src'] = @audioPath paths[0], path_options
|
12
|
+
@tag 'audio', options
|
13
|
+
else
|
14
|
+
content = ''
|
15
|
+
source_options
|
16
|
+
if options['source']
|
17
|
+
source_options = options['source']
|
18
|
+
options['source'] = undefined
|
19
|
+
else
|
20
|
+
source_options = {}
|
21
|
+
for i, path of paths
|
22
|
+
source_options['src'] = @audioPath path, path_options
|
23
|
+
content += @tag 'source', source_options
|
24
|
+
@contentTag 'audio', content, options
|
25
|
+
|
26
|
+
helper 'autoDiscoveryLinkTag', (type = 'rss', url = @_context.domain.app, options = {}) ->
|
27
|
+
options = @_clone options
|
28
|
+
options['href'] = @assetUrl(url || @_context.domain.app)
|
29
|
+
options['rel'] ||= 'alternate'
|
30
|
+
unless options['type']
|
31
|
+
if type == 'rss'
|
32
|
+
options['type'] = 'application/rss+xml'
|
33
|
+
options['title'] ||= 'RSS'
|
34
|
+
if type == 'atom'
|
35
|
+
options['type'] = 'application/atom+xml'
|
36
|
+
options['title'] ||= 'ATOM'
|
37
|
+
options['title'] ||= ''
|
38
|
+
@tag 'link', options
|
39
|
+
|
40
|
+
helper 'faviconLinkTag', (path = 'favicon.ico', options = {}) ->
|
41
|
+
options['rel'] ||= 'shortcut icon'
|
42
|
+
options['type'] ||= 'image/x-icon'
|
43
|
+
options['href'] = path
|
44
|
+
@tag 'link', options
|
45
|
+
|
46
|
+
helper 'imageAlt', (source) ->
|
47
|
+
basename = new String(source).substring(source.lastIndexOf('/') + 1);
|
48
|
+
basename = basename.substring(0, basename.lastIndexOf(".")) if basename.lastIndexOf(".") != -1
|
49
|
+
basename = basename[0..-34] if (/\-[0-9a-z]{32}/ig).test(basename[-33..-1])
|
50
|
+
basename = basename.replace(/[-_\s]+/g, ' ')
|
51
|
+
basename[0].toUpperCase() + basename.slice(1);
|
52
|
+
|
53
|
+
handleSizeAttribute = (options) ->
|
54
|
+
if typeof options['size'] == 'string'
|
55
|
+
size = options['size'].toLowerCase()
|
56
|
+
options['size'] = undefined
|
57
|
+
if (/\d+X\d+/i).test(size)
|
58
|
+
xIndex = size.lastIndexOf('x');
|
59
|
+
options['width'] ||= size.substring 0, xIndex
|
60
|
+
options['height'] ||= size.substring xIndex+1
|
61
|
+
if (new RegExp("\\d{" + size.length + "}")).test(size)
|
62
|
+
options['width'] ||= size
|
63
|
+
options['height'] ||= size
|
64
|
+
|
65
|
+
helper 'imageTag', (source, options = {}) ->
|
66
|
+
options = @_clone options
|
67
|
+
handleSizeAttribute options
|
68
|
+
|
69
|
+
if source == '' || source[0..4] == 'data:'
|
70
|
+
options['src'] = source
|
71
|
+
options['alt'] = undefined if options['alt'] == null
|
72
|
+
else
|
73
|
+
options['src'] = @imagePath source
|
74
|
+
if options['alt'] == null
|
75
|
+
options['alt'] = undefined
|
76
|
+
else
|
77
|
+
options['alt'] = @imageAlt(source) if options['alt'] == undefined
|
78
|
+
@tag 'img', options
|
79
|
+
|
80
|
+
helper 'javascriptIncludeTag', (sources...) ->
|
81
|
+
path_options = if typeof sources[sources.length-1] == 'object'
|
82
|
+
sources.pop()
|
83
|
+
else
|
84
|
+
{}
|
85
|
+
|
86
|
+
result = ''
|
87
|
+
for index, source of sources
|
88
|
+
options = { src: @javascriptPath(source, path_options) }
|
89
|
+
result += @contentTag 'script', '', options
|
90
|
+
result
|
91
|
+
|
92
|
+
helper 'stylesheetLinkTag', (sources...) ->
|
93
|
+
path_options = {}
|
94
|
+
options = if typeof sources[sources.length - 1] == 'object'
|
95
|
+
opts = sources.pop()
|
96
|
+
path_options['format'] = opts['format']
|
97
|
+
path_options['type'] = opts['type']
|
98
|
+
opts['format'] = undefined
|
99
|
+
opts['type'] = undefined
|
100
|
+
opts['rel'] ||= 'stylesheet'
|
101
|
+
opts['media'] ||= 'screen'
|
102
|
+
opts
|
103
|
+
else
|
104
|
+
{ rel: 'stylesheet', media: 'screen' }
|
105
|
+
|
106
|
+
result = ''
|
107
|
+
for index, source of sources
|
108
|
+
options['href'] = @stylesheetPath source, path_options
|
109
|
+
result += @tag 'link', options
|
110
|
+
result
|
111
|
+
|
112
|
+
helper 'videoTag', (paths...) ->
|
113
|
+
options = if typeof paths[paths.length - 1] == 'object'
|
114
|
+
@_clone paths.pop()
|
115
|
+
else
|
116
|
+
{}
|
117
|
+
|
118
|
+
handleSizeAttribute options
|
119
|
+
options['poster'] = @imagePath(options['poster']) if options['poster']
|
120
|
+
|
121
|
+
path_options = {}
|
122
|
+
path_options['format'] = options['format']
|
123
|
+
options['format'] = undefined
|
124
|
+
if paths.length == 1
|
125
|
+
options['src'] = @videoPath paths[0], path_options
|
126
|
+
@tag 'video', options
|
127
|
+
else
|
128
|
+
content = ''
|
129
|
+
source_options
|
130
|
+
if options['source']
|
131
|
+
source_options = options['source']
|
132
|
+
options['source'] = undefined
|
133
|
+
else
|
134
|
+
source_options = {}
|
135
|
+
for i, path of paths
|
136
|
+
source_options['src'] = @videoPath path, path_options
|
137
|
+
content += @tag 'source', source_options
|
138
|
+
@contentTag 'video', content, options
|
@@ -0,0 +1,90 @@
|
|
1
|
+
# TODO: Think about digests
|
2
|
+
helper 'computeAssetPath', (source, options = {}) ->
|
3
|
+
options['type'] ||= 'asset'
|
4
|
+
|
5
|
+
if @_context.forceAssetDomain
|
6
|
+
options['domain'] ||= @_context.domain[ options['type'] ]
|
7
|
+
|
8
|
+
format = if options['format'] == undefined
|
9
|
+
@_context.defaultFormat[ options['type'] ]
|
10
|
+
else
|
11
|
+
options['format']
|
12
|
+
|
13
|
+
if format
|
14
|
+
sharp_index = source.lastIndexOf '#'
|
15
|
+
query_index = source.lastIndexOf '?'
|
16
|
+
|
17
|
+
index = if sharp_index == -1
|
18
|
+
query_index
|
19
|
+
else
|
20
|
+
if query_index == -1
|
21
|
+
sharp_index
|
22
|
+
else
|
23
|
+
if query_index > sharp_index
|
24
|
+
sharp_index
|
25
|
+
else
|
26
|
+
query_index
|
27
|
+
|
28
|
+
if index == -1
|
29
|
+
source += '.' + format unless source[(-format.length-1)..-1] == ('.' + format)
|
30
|
+
else
|
31
|
+
prefix = source.slice 0, index
|
32
|
+
sufix = source.slice index
|
33
|
+
if prefix[(-format.length-1)..-1] == ('.' + format)
|
34
|
+
source = prefix + sufix
|
35
|
+
else
|
36
|
+
source = prefix + '.' + format + sufix
|
37
|
+
|
38
|
+
source = if source[0] == '/'
|
39
|
+
source
|
40
|
+
else
|
41
|
+
prefix_path = @_context.defaultPath[options['type']]
|
42
|
+
if prefix_path[prefix_path.length-1] == '/'
|
43
|
+
prefix_path + source
|
44
|
+
else
|
45
|
+
prefix_path + '/' + source
|
46
|
+
|
47
|
+
source = '/' + source if source[0] != '/'
|
48
|
+
|
49
|
+
if options['domain']
|
50
|
+
source = options['domain'] + source
|
51
|
+
|
52
|
+
source
|
53
|
+
|
54
|
+
helper 'assetPath', (source, options = {}) ->
|
55
|
+
source = source.toString()
|
56
|
+
options = @_clone options
|
57
|
+
|
58
|
+
fullDoaminPath = /[\w\d]+\:\/\//i
|
59
|
+
return source if fullDoaminPath.test(source) || source[0..1] == '//'
|
60
|
+
|
61
|
+
@computeAssetPath source, options
|
62
|
+
|
63
|
+
helper 'assetUrl', (source, options = {}) ->
|
64
|
+
source = source.toString()
|
65
|
+
options = @_clone options
|
66
|
+
|
67
|
+
options['domain'] ||= if options['type']
|
68
|
+
@_context.domain[options['type']] || @_context.domain.asset
|
69
|
+
else
|
70
|
+
@_context.domain.asset
|
71
|
+
|
72
|
+
@assetPath source, options
|
73
|
+
|
74
|
+
assetPathBuilder = (_type) ->
|
75
|
+
type_built = _type
|
76
|
+
(source, options = {}) ->
|
77
|
+
options = @_clone options
|
78
|
+
options['type'] = type_built if options['type'] == undefined
|
79
|
+
@assetPath source, options
|
80
|
+
|
81
|
+
assetUrlBuilder = (_type) ->
|
82
|
+
type_built = _type
|
83
|
+
(source, options = {}) ->
|
84
|
+
options = @_clone options
|
85
|
+
options['type'] = type_built if options['type'] == undefined
|
86
|
+
@assetUrl source, options
|
87
|
+
|
88
|
+
for index, type of ['audio', 'font', 'image', 'video', 'javascript', 'stylesheet']
|
89
|
+
helper (type+'Path'), assetPathBuilder.call(this,type)
|
90
|
+
helper (type+'Url'), assetUrlBuilder.call(this,type)
|
@@ -0,0 +1,54 @@
|
|
1
|
+
helper 'strftime', (date, pattern) ->
|
2
|
+
I18n.strftime date, pattern
|
3
|
+
|
4
|
+
helper 'distanceOfTimeInWords', (from_time, to_time, withSeconds = false) ->
|
5
|
+
distance_in_minutes = Math.abs((to_time - from_time) / 60000)
|
6
|
+
[key, options] = switch
|
7
|
+
when distance_in_minutes < 1
|
8
|
+
if withSeconds
|
9
|
+
distance_in_seconds = Math.abs((to_time - from_time) / 1000)
|
10
|
+
switch
|
11
|
+
when distance_in_seconds < 5 then [ 'less_than_x_seconds', { count: 5 } ]
|
12
|
+
when distance_in_seconds < 10 then [ 'less_than_x_seconds', { count: 10 } ]
|
13
|
+
when distance_in_seconds < 20 then [ 'less_than_x_seconds', { count: 20 } ]
|
14
|
+
when distance_in_seconds < 40 then [ 'half_a_minute', { } ]
|
15
|
+
when distance_in_seconds < 60 then [ 'less_than_x_minutes', { count: 1 } ]
|
16
|
+
else ['x_minutes', { count: 1 }]
|
17
|
+
else
|
18
|
+
['less_than_x_minutes', { count: 1 }]
|
19
|
+
when distance_in_minutes < 45 then [ 'x_minutes', { count: Math.floor(distance_in_minutes) } ]
|
20
|
+
when distance_in_minutes < 90 then [ 'about_x_hours', { count: 1 } ]
|
21
|
+
when distance_in_minutes < 1440 then [ 'about_x_hours', { count: Math.floor(distance_in_minutes / 60.0) } ]
|
22
|
+
when distance_in_minutes < 2520 then [ 'x_days', { count: 1 } ]
|
23
|
+
when distance_in_minutes < 43200 then [ 'x_days', { count: Math.floor(distance_in_minutes / 1440.0) } ]
|
24
|
+
when distance_in_minutes < 86400 then [ 'about_x_months', { count: Math.floor(distance_in_minutes / 43200.0) } ]
|
25
|
+
when distance_in_minutes < 525600 then [ 'x_months', { count: Math.floor(distance_in_minutes / 43200.0) } ]
|
26
|
+
else
|
27
|
+
remainder = distance_in_minutes % 525600
|
28
|
+
distance_in_years = Math.floor( distance_in_minutes / 525600 )
|
29
|
+
if remainder < 131400
|
30
|
+
['about_x_years', { count: distance_in_years } ]
|
31
|
+
else if remainder < 394200
|
32
|
+
['over_x_years', { count: distance_in_years } ]
|
33
|
+
else
|
34
|
+
['almost_x_years', { count: distance_in_years + 1 } ]
|
35
|
+
location_key = 'datetime.distance_in_words.' + key
|
36
|
+
@translate location_key, options
|
37
|
+
|
38
|
+
helper 'distanceOfTimeInWordsToNow', (from_time, withSeconds = false) ->
|
39
|
+
@distanceOfTimeInWords from_time, new Date(), withSeconds
|
40
|
+
alias 'timeAgoInWords', 'distanceOfTimeInWordsToNow'
|
41
|
+
|
42
|
+
# date_select
|
43
|
+
# datetime_select
|
44
|
+
# select_date
|
45
|
+
# select_datetime
|
46
|
+
# select_day
|
47
|
+
# select_hour
|
48
|
+
# select_minute
|
49
|
+
# select_month
|
50
|
+
# select_second
|
51
|
+
# select_time
|
52
|
+
# select_year
|
53
|
+
# time_select
|
54
|
+
# time_tag
|
@@ -0,0 +1,25 @@
|
|
1
|
+
|
2
|
+
# check_box
|
3
|
+
# color_field
|
4
|
+
# date_field
|
5
|
+
# datetime_field
|
6
|
+
# datetime_local_field
|
7
|
+
# email_field
|
8
|
+
# fields_for
|
9
|
+
# file_field
|
10
|
+
# form_for
|
11
|
+
# hidden_field
|
12
|
+
# label
|
13
|
+
# month_field
|
14
|
+
# number_field
|
15
|
+
# password_field
|
16
|
+
# phone_field
|
17
|
+
# radio_button
|
18
|
+
# range_field
|
19
|
+
# search_field
|
20
|
+
# telephone_field
|
21
|
+
# text_area
|
22
|
+
# text_field
|
23
|
+
# time_field
|
24
|
+
# url_field
|
25
|
+
# week_field
|