ultimate-base 0.3.1.1 → 0.3.2

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.
Files changed (35) hide show
  1. data/Gemfile.lock +15 -15
  2. data/app/assets/javascripts/ultimate/backbone/lib/backbone.js +70 -56
  3. data/app/assets/javascripts/ultimate/backbone/model.js.coffee +2 -2
  4. data/app/assets/javascripts/ultimate/backbone/view.js.coffee +5 -4
  5. data/app/assets/javascripts/ultimate/backbone/views/typed-fields.js.coffee +1 -1
  6. data/app/assets/javascripts/ultimate/helpers/asset_tag.js.coffee +21 -19
  7. data/app/assets/javascripts/ultimate/helpers/form_options.js.coffee +65 -0
  8. data/app/assets/javascripts/ultimate/helpers/form_tag.js.coffee +177 -0
  9. data/app/assets/javascripts/ultimate/helpers/javascript.js.coffee +31 -0
  10. data/app/assets/javascripts/ultimate/helpers/record_tag.js.coffee +29 -15
  11. data/app/assets/javascripts/ultimate/helpers/tag.js.coffee +7 -3
  12. data/app/assets/javascripts/ultimate/helpers/url.js.coffee +62 -7
  13. data/app/assets/javascripts/ultimate/jquery-plugin-adapter.js.coffee +1 -0
  14. data/app/assets/javascripts/ultimate/jquery-plugin-class.js.coffee +35 -16
  15. data/app/assets/javascripts/ultimate/jquery.base.js.coffee +1 -1
  16. data/app/assets/javascripts/ultimate/underscore/underscore.js +292 -192
  17. data/app/assets/javascripts/ultimate/underscore/underscore.outcasts.js.coffee +27 -2
  18. data/app/assets/javascripts/ultimate/underscore/underscore.string.js +4 -4
  19. data/app/assets/stylesheets/polyfills/PIE.htc +81 -81
  20. data/app/assets/stylesheets/polyfills/boxsizing.htc +255 -54
  21. data/app/assets/stylesheets/ultimate/mixins/_vendors.scss +9 -0
  22. data/app/assets/stylesheets/ultimate/mixins/css3.scss +39 -28
  23. data/app/assets/stylesheets/ultimate/mixins/microstructures.scss +32 -6
  24. data/lib/ultimate/base/version.rb +1 -1
  25. data/test/javascripts/tests/helpers/asset_tag_test.js.coffee +1 -1
  26. data/test/javascripts/tests/helpers/form_options_test.js.coffee +96 -0
  27. data/test/javascripts/tests/helpers/form_tag_test.js.coffee +225 -0
  28. data/test/javascripts/tests/helpers/javascript_test.js.coffee +25 -0
  29. data/test/javascripts/tests/helpers/record_tag_test.js.coffee +5 -3
  30. data/test/javascripts/tests/helpers/tag_test.js.coffee +22 -17
  31. data/test/javascripts/tests/helpers/url_test.js.coffee +50 -6
  32. data/test/javascripts/tests/underscore/underscore.outcasts.test.js.coffee +9 -0
  33. metadata +8 -4
  34. data/app/assets/javascripts/ultimate/helpers/translation.js.coffee +0 -97
  35. data/test/javascripts/tests/helpers/translation_test.js.coffee +0 -140
@@ -0,0 +1,65 @@
1
+ #= require ./base
2
+ #= require ./tag
3
+
4
+ @Ultimate.Helpers.FormOptions =
5
+
6
+ options_for_select: (container, selected = null) ->
7
+ return container if _.isString(container)
8
+ [selected, disabled] = _.map @_extract_selected_and_disabled(selected), (r) ->
9
+ _.map _.outcasts.arrayWrap(r), (item) -> item.toString()
10
+ container = _.pairs(container) if $.isPlainObject(container)
11
+ _.map(container, (element) ->
12
+ html_attributes = @_option_html_attributes(element)
13
+ [text, value] = _.map @_option_text_and_value(element), (item) -> if item? then item.toString() else ''
14
+ html_attributes['value'] = value
15
+ html_attributes['selected'] = 'selected' if @_option_value_selected(value, selected)
16
+ html_attributes['disabled'] = 'disabled' if disabled and @_option_value_selected(value, disabled)
17
+ Ultimate.Helpers.Tag.content_tag_string 'option', text, html_attributes
18
+ ).join("\n")
19
+
20
+ options_from_collection_for_select: (collection, value_method, text_method, selected = null) ->
21
+ options = _.map collection, (element) ->
22
+ [@_value_for_collection(element, text_method), @_value_for_collection(element, value_method)]
23
+ [selected, disabled] = @_extract_selected_and_disabled(selected)
24
+ select_deselect =
25
+ selected: @_extract_values_from_collection(collection, value_method, selected)
26
+ disabled: @_extract_values_from_collection(collection, value_method, disabled)
27
+ @options_for_select(options, select_deselect)
28
+
29
+
30
+
31
+ _option_html_attributes: (element) ->
32
+ result = {}
33
+ if _.isArray(element)
34
+ _.extend(result, e) for e in element when $.isPlainObject(e)
35
+ result
36
+
37
+ _option_text_and_value: (option) ->
38
+ # Options are [text, value] pairs or strings used for both.
39
+ if _.isArray(option)
40
+ option = _.reject(option, (e) -> $.isPlainObject(e))
41
+ [_.first(option), _.last(option)]
42
+ else
43
+ [option, option]
44
+
45
+ _option_value_selected: (value, selected) ->
46
+ value in selected
47
+
48
+ _extract_selected_and_disabled: (selected) ->
49
+ if _.isFunction(selected)
50
+ [selected, null]
51
+ else
52
+ selected = _.outcasts.arrayWrap(selected)
53
+ options = if $.isPlainObject(_.last(selected)) then selected.pop() else {}
54
+ selected_items = options['selected'] ? selected
55
+ [selected_items, options['disabled']]
56
+
57
+ _extract_values_from_collection: (collection, value_method, selected) ->
58
+ if _.isFunction(selected)
59
+ _.compact _.map collection, (element) ->
60
+ _.result(element, value_method) if selected(element)
61
+ else
62
+ selected
63
+
64
+ _value_for_collection: (item, value) ->
65
+ if _.isFunction(value) then value(item) else _.result(item, value)
@@ -0,0 +1,177 @@
1
+ #= require ./base
2
+ #= require ./tag
3
+ #= require ./url
4
+ #= require ./asset_tag
5
+
6
+ @Ultimate.Helpers.FormTag =
7
+
8
+ embed_authenticity_token_in_remote_forms: false
9
+
10
+ form_tag: (url_for_options = {}, options = {}, block = null) ->
11
+ html_options = @_html_options_for_form(url_for_options, options)
12
+ if block = _.outcasts.blockGiven(arguments)
13
+ @_form_tag_in_block(html_options, block)
14
+ else
15
+ @_form_tag_html(html_options)
16
+
17
+ select_tag: (name, option_tags = '', options = {}) ->
18
+ html_name = if options.multiple is true and not _.string.endsWith(name, '[]') then "#{name}[]" else name
19
+ if _.outcasts.delete(options, 'include_blank')
20
+ option_tags = Ultimate.Helpers.Tag.content_tag('option', '', value: '') + option_tags
21
+ if prompt = _.outcasts.delete(options, 'prompt')
22
+ option_tags = Ultimate.Helpers.Tag.content_tag('option', prompt, value: '') + option_tags
23
+ Ultimate.Helpers.Tag.content_tag 'select', option_tags, _.extend({name: html_name, id: @_sanitize_to_id(name)}, options), false
24
+
25
+ text_field_tag: (name, value = null, options = {}) ->
26
+ Ultimate.Helpers.Tag.tag 'input', _.extend({type: 'text', name: name, id: @_sanitize_to_id(name), value: value}, options)
27
+
28
+ label_tag: (name = null, content_or_options = null, options = null, block = null) ->
29
+ if (block = _.outcasts.blockGiven(arguments)) and $.isPlainObject(content_or_options)
30
+ options = content_or_options
31
+ else
32
+ options ||= {}
33
+ if _.isString(name) and not _.string.isBlank(name)
34
+ unless _.has(options, 'for')
35
+ options = _.clone(options)
36
+ options['for'] = @_sanitize_to_id(name)
37
+ content_or_options ||= _.string.humanize(name)
38
+ content_or_options = options if block
39
+ Ultimate.Helpers.Tag.content_tag 'label', content_or_options, options, block
40
+
41
+ hidden_field_tag: (name, value = null, options = {}) ->
42
+ @text_field_tag name, value, _.extend(options, type: 'hidden')
43
+
44
+ file_field_tag: (name, options = {}) ->
45
+ @text_field_tag name, null, _.extend(options, type: 'file')
46
+
47
+ password_field_tag: (name = 'password', value = null, options = {}) ->
48
+ @text_field_tag name, value, _.extend(options, type: 'password')
49
+
50
+ text_area_tag: (name, content = null, options = {}) ->
51
+ if size = _.outcasts.delete(options, 'size')
52
+ [options['cols'], options['rows']] = size.split("x") if _.isFunction(size.split)
53
+ escape = _.outcasts.delete(options, 'escape') ? true
54
+ Ultimate.Helpers.Tag.content_tag 'textarea', content, _.extend({name: name, id: @_sanitize_to_id(name)}, options), escape
55
+
56
+ check_box_tag: (name, value = '1', checked = false, options = {}) ->
57
+ html_options = _.extend({type: 'checkbox', name: name, id: @_sanitize_to_id(name), value: value}, options)
58
+ html_options['checked'] = 'checked' if checked
59
+ Ultimate.Helpers.Tag.tag 'input', html_options
60
+
61
+ radio_button_tag: (name, value, checked = false, options = {}) ->
62
+ html_options = _.extend({type: 'radio', name: name, id: "#{@_sanitize_to_id(name)}_#{@_sanitize_to_id(value)}", value: value}, options)
63
+ html_options['checked'] = 'checked' if checked
64
+ Ultimate.Helpers.Tag.tag 'input', html_options
65
+
66
+ submit_tag: (value = 'Save changes', options = {}) ->
67
+ Ultimate.Helpers.Tag.tag 'input', _.extend({type: 'submit', name: 'commit', value: value}, options)
68
+
69
+ button_tag: (content_or_options = null, options = null, block = null) ->
70
+ options = content_or_options if (block = _.outcasts.blockGiven(arguments)) and $.isPlainObject(content_or_options)
71
+ options ||= {}
72
+ options = _.extend({name: 'button', type: 'submit'}, options)
73
+ content_or_options = options if block
74
+ Ultimate.Helpers.Tag.content_tag 'button', content_or_options ? 'Button', options, not block, block
75
+
76
+ image_submit_tag: (source, options = {}) ->
77
+ Ultimate.Helpers.Tag.tag 'input', _.extend({type: 'image', src: Ultimate.Helpers.AssetTag.path_to_image(source)}, options)
78
+
79
+ field_set_tag: (legend = null, options = null, block) ->
80
+ output = Ultimate.Helpers.Tag.tag('fieldset', options, true)
81
+ output += Ultimate.Helpers.Tag.content_tag('legend', legend) if _.isString(legend) and not _.string.isBlank(legend)
82
+ output += block() if block = _.outcasts.blockGiven(arguments)
83
+ output += '</fieldset>'
84
+ output
85
+
86
+ color_field_tag: (name, value = null, options = {}) ->
87
+ @text_field_tag name, value, _.extend(options, type: 'color')
88
+
89
+ search_field_tag: (name, value = null, options = {}) ->
90
+ @text_field_tag name, value, _.extend(options, type: 'search')
91
+
92
+ telephone_field_tag: (name, value = null, options = {}) ->
93
+ @text_field_tag name, value, _.extend(options, type: 'tel')
94
+
95
+ phone_field_tag: ->
96
+ @telephone_field_tag arguments...
97
+
98
+ date_field_tag: (name, value = null, options = {}) ->
99
+ @text_field_tag name, value, _.extend(options, type: 'date')
100
+
101
+ time_field_tag: (name, value = null, options = {}) ->
102
+ @text_field_tag name, value, _.extend(options, type: 'time')
103
+
104
+ datetime_field_tag: (name, value = null, options = {}) ->
105
+ @text_field_tag name, value, _.extend(options, type: 'datetime')
106
+
107
+ datetime_local_field_tag: (name, value = null, options = {}) ->
108
+ @text_field_tag name, value, _.extend(options, type: 'datetime-local')
109
+
110
+ month_field_tag: (name, value = null, options = {}) ->
111
+ @text_field_tag name, value, _.extend(options, type: 'month')
112
+
113
+ week_field_tag: (name, value = null, options = {}) ->
114
+ @text_field_tag name, value, _.extend(options, type: 'week')
115
+
116
+ url_field_tag: (name, value = null, options = {}) ->
117
+ @text_field_tag name, value, _.extend(options, type: 'url')
118
+
119
+ email_field_tag: (name, value = null, options = {}) ->
120
+ @text_field_tag name, value, _.extend(options, type: 'email')
121
+
122
+ number_field_tag: (name, value = null, options = {}) ->
123
+ options['type'] ||= 'number'
124
+ if range = _.outcasts.delete(options, 'in') or _.outcasts.delete(options, 'within')
125
+ _.extend options, min: range.min, max: range.max
126
+ @text_field_tag name, value, options
127
+
128
+ range_field_tag: (name, value = null, options = {}) ->
129
+ @number_field_tag name, value, _.extend(options, type: 'range')
130
+
131
+ utf8_enforcer_tag: ->
132
+ Ultimate.Helpers.Tag.tag 'input', type: 'hidden', name: 'utf8', value: '&#x2713;', false, false
133
+
134
+ _html_options_for_form: (url_for_options, options) ->
135
+ html_options = options
136
+ html_options['enctype'] = 'multipart/form-data' if _.outcasts.delete(html_options, 'multipart')
137
+ # The following URL is unescaped, this is just a hash of options, and it is the
138
+ # responsibility of the caller to escape all the values.
139
+ html_options['action'] = Ultimate.Helpers.Url.url_for(url_for_options)
140
+ html_options['accept-charset'] = 'UTF-8'
141
+ html_options['data-remote'] = true if _.outcasts.delete(html_options, 'remote')
142
+ if html_options['data-remote'] and
143
+ not @embed_authenticity_token_in_remote_forms and
144
+ _.string.isBlank(html_options['authenticity_token'])
145
+ # The authenticity token is taken from the meta tag in this case
146
+ html_options['authenticity_token'] = false
147
+ else if html_options['authenticity_token'] is true
148
+ # Include the default authenticity_token, which is only generated when its set to nil,
149
+ # but we needed the true value to override the default of no authenticity_token on data-remote.
150
+ html_options['authenticity_token'] = null
151
+ html_options
152
+
153
+ _extra_tags_for_form: (html_options) ->
154
+ authenticity_token = _.outcasts.delete(html_options, 'authenticity_token')
155
+ method = _.outcasts.delete(html_options, 'method')
156
+ method_tag =
157
+ if /^get$/i.test(method) # must be case-insensitive, but can't use downcase as might be nil
158
+ html_options['method'] = 'get'
159
+ ''
160
+ else if _.string.isBlank(method) or /^post$/i.test(method)
161
+ html_options['method'] = 'post'
162
+ Ultimate.Helpers.Url._token_tag(authenticity_token)
163
+ else
164
+ html_options['method'] = 'post'
165
+ Ultimate.Helpers.Url._method_tag(method) + Ultimate.Helpers.Url._token_tag(authenticity_token)
166
+ tags = @utf8_enforcer_tag() + method_tag
167
+ Ultimate.Helpers.Tag.content_tag('div', tags, style: 'margin:0;padding:0;display:inline', false)
168
+
169
+ _form_tag_html: (html_options) ->
170
+ extra_tags = @_extra_tags_for_form(html_options)
171
+ Ultimate.Helpers.Tag.tag('form', html_options, true) + extra_tags
172
+
173
+ _form_tag_in_block: (html_options, block) ->
174
+ "#{@_form_tag_html(html_options)}#{block()}</form>"
175
+
176
+ _sanitize_to_id: (name) ->
177
+ if name? then name.toString().replace(/\]/g, '').replace(/[^-a-zA-Z0-9:.]/g, '_') else ''
@@ -0,0 +1,31 @@
1
+ #= require ./base
2
+ #= require ./tag
3
+
4
+ JS_ESCAPE_MAP =
5
+ '\\' : '\\\\'
6
+ '</' : '<\\/'
7
+ "\r\n" : '\\n'
8
+ "\n" : '\\n'
9
+ "\r" : '\\n'
10
+ '"' : '\\"'
11
+ "'" : "\\'"
12
+
13
+ @Ultimate.Helpers.Javascript =
14
+
15
+ escape_javascript: (javascript) ->
16
+ return '' unless _.isString(javascript)
17
+ javascript.replace( /(\\|<\/|\r\n|[\n\r"'])/g, (match) -> JS_ESCAPE_MAP[match] )
18
+
19
+ j: -> @escape_javascript arguments...
20
+
21
+ javascript_tag: (content_or_options_with_block = null, html_options = {}, block = null) ->
22
+ content =
23
+ if block = _.outcasts.blockGiven(arguments)
24
+ html_options = content_or_options_with_block if $.isPlainObject(content_or_options_with_block)
25
+ block()
26
+ else
27
+ content_or_options_with_block
28
+ Ultimate.Helpers.Tag.content_tag 'script', @javascript_cdata_section(content), html_options, false
29
+
30
+ javascript_cdata_section: (content) ->
31
+ "\n//#{Ultimate.Helpers.Tag.cdata_section("\n#{content}\n//")}\n"
@@ -21,11 +21,21 @@
21
21
  options = if _.isObject(options) then _.clone(options) else {}
22
22
  _.extend options, {class: Ultimate.Helpers.Tag.concat_class(@dom_class(record, prefix), options['class']), id: @dom_id(record, prefix)}
23
23
  if block
24
- Ultimate.Helpers.Tag.content_tag(tag_name, block(record), options)
24
+ Ultimate.Helpers.Tag.content_tag(tag_name, block(record), options, false)
25
25
  else
26
- Ultimate.Helpers.Tag.content_tag(tag_name, block, options)
26
+ Ultimate.Helpers.Tag.content_tag(tag_name, block, options, false)
27
27
 
28
- # ============= from ActionController::RecordIdentifier ===============
28
+
29
+
30
+ # from ActionView::ModelNaming
31
+ model_name_from_record_or_class: (record_or_class) ->
32
+ modelClass = record_or_class.constructor ? record_or_class
33
+ if modelClass?
34
+ modelClass.modelName or modelClass.className or modelClass.name or 'Model'
35
+ else
36
+ 'Model'
37
+
38
+ # ============= from ActionView::RecordIdentifier ===============
29
39
 
30
40
  # The DOM class convention is to use the singular form of an object or class. Examples:
31
41
  #
@@ -34,15 +44,16 @@
34
44
  #
35
45
  # If you need to address multiple instances of the same class in the same view, you can prefix the dom_class:
36
46
  #
37
- # dom_class(post, :edit) # => "edit_post"
38
- # dom_class(Person, :edit) # => "edit_person"
47
+ # dom_class(post, 'edit') # => "edit_post"
48
+ # dom_class(Person, 'edit') # => "edit_person"
39
49
  dom_class: (record_or_class, prefix = "") ->
40
- # TODO improve as in Ultimate.Backbone.Model
41
- if _.isString(record_or_class)
42
- singular = _.singularize(record_or_class)
43
- else unless singular = _.result(record_or_class, 'singular')
44
- if _.isString(record_or_class?.constructor?.name)
45
- singular = _.singularize(_.string.underscored(record_or_class.constructor.name))
50
+ singular =
51
+ _.result(record_or_class, 'singular') ?
52
+ _.singularize _.string.underscored(
53
+ if _.isString(record_or_class)
54
+ record_or_class
55
+ else
56
+ @model_name_from_record_or_class(record_or_class) )
46
57
  if prefix then "#{prefix}_#{singular}" else singular
47
58
 
48
59
  # The DOM id convention is to use the singular form of an object or class with the id following an underscore.
@@ -56,11 +67,14 @@
56
67
  # dom_id(Post.find(45), "edit") # => "edit_post_45"
57
68
  # TODO sync with rorId and ror_id
58
69
  dom_id: (record, prefix = "") ->
59
- if record_id = @sanitize_dom_id(record.id)
70
+ if record_id = @_record_key_for_dom_id(record)
60
71
  "#{@dom_class(record, prefix)}_#{record_id}"
61
72
  else
62
73
  @dom_class(record, prefix or "new")
63
74
 
64
- # Replaces characters that are invalid in HTML DOM ids with valid ones.
65
- sanitize_dom_id: (candidate_id) ->
66
- candidate_id # TODO implement conversion to valid DOM id values
75
+
76
+
77
+ # protected
78
+
79
+ _record_key_for_dom_id: (record) ->
80
+ record.id
@@ -11,7 +11,7 @@
11
11
  textarea: "\n"
12
12
 
13
13
  tag: (tag_name, options = {}, open = false, escape = true) ->
14
- "<#{tag_name}#{@tag_options(options)}#{if open then '>' else ' />'}"
14
+ "<#{tag_name}#{@tag_options(options, escape)}#{if open then '>' else ' />'}"
15
15
 
16
16
  content_tag: (name, content_or_options_with_block = null, options = null, escape = true, block = null) ->
17
17
  if block = _.outcasts.blockGiven(arguments)
@@ -20,13 +20,17 @@
20
20
  else
21
21
  @content_tag_string(name, content_or_options_with_block, options, escape)
22
22
 
23
+ cdata_section: (content) ->
24
+ splitted = content.replace(/\]\]>/g, ']]]]><![CDATA[>')
25
+ "<![CDATA[#{splitted}]]>"
26
+
23
27
  content_tag_string: (name, content = '', options = {}, escape = true) ->
24
- content = _.escape(content) if escape
28
+ content = _.string.escapeHTML(content) if escape
25
29
  "<#{name}#{@tag_options(options, escape)}>#{@PRE_CONTENT_STRINGS[name] ? ''}#{content}</#{name}>"
26
30
 
27
31
  html_options_to_s: (html_options, escape = false, prefix = "") ->
28
32
  deprecate 'html_options_to_s()', "tag_options()"
29
- @tag_options.apply(@, arguments)
33
+ @tag_options arguments...
30
34
 
31
35
  tag_options: (options, escape = true) ->
32
36
  return "" if _.isEmpty(options)
@@ -1,17 +1,22 @@
1
1
  #= require ./base
2
2
  #= require ./tag
3
+ #= require ./javascript
4
+
5
+ __char_encode = (char) -> "%#{char.charCodeAt(0).toString(16)}"
6
+ escape_path = (str) -> str.replace(/[^*\-.0-9A-Z_a-z]/g, __char_encode).replace(/\+/g, '%20')
7
+ __string_encode = (str) -> _.map(str, (char) -> "&##{char.charCodeAt(0)};" ).join('')
3
8
 
4
9
  @Ultimate.Helpers.Url =
5
10
 
6
11
  url_for: (options = null) ->
7
12
  if _.isString(options)
8
- if options is "back"
13
+ if options is 'back'
9
14
  'javascript:history.back();'
10
15
  else
11
16
  options
12
17
  else unless _.isEmpty(options)
13
18
  url = _.result(options, 'url') ? ''
14
- if _.isObject(options)
19
+ if $.isPlainObject(options)
15
20
  options = _.clone(options)
16
21
  delete options['url']
17
22
  anchor = _.outcasts.delete(options, 'anchor')
@@ -26,8 +31,41 @@
26
31
  options ||= {}
27
32
  url = @url_for(options)
28
33
  html_options = @_convert_options_to_data_attributes(options, html_options)
29
- html_options["href"] ||= url
30
- Ultimate.Helpers.Tag.content_tag("a", name or url, html_options, false, block)
34
+ html_options['href'] ||= url
35
+ if block
36
+ Ultimate.Helpers.Tag.content_tag('a', html_options, null, false, block)
37
+ else
38
+ Ultimate.Helpers.Tag.content_tag('a', name or url, html_options, false)
39
+
40
+ link_to_js: (name = null, html_options = null, block = null) ->
41
+ [options, name] = [name, null] if block = _.outcasts.blockGiven(arguments)
42
+ @link_to [name, options, html_options, block]...
43
+
44
+
45
+
46
+ mail_to: (email_address, name = null, html_options = {}) ->
47
+ email_address = _.string.escapeHTML(email_address)
48
+ encode = _.outcasts.delete(html_options, 'encode')
49
+ extras = _.compact _.map _.string.words('cc bcc body subject'), (item) ->
50
+ option = _.outcasts.delete(html_options, item)
51
+ if option?
52
+ "#{item}=#{escape_path(option)}"
53
+ extras = if _.isEmpty(extras) then '' else '?' + _.string.escapeHTML(extras.join('&'))
54
+ email_address_obfuscated = email_address
55
+ email_address_obfuscated = email_address_obfuscated.replace('@', _.outcasts.delete(html_options, 'replace_at')) if 'replace_at' of html_options
56
+ email_address_obfuscated = email_address_obfuscated.replace('.', _.outcasts.delete(html_options, 'replace_dot')) if 'replace_dot' of html_options
57
+ switch encode
58
+ when 'javascript'
59
+ html = @link_to(name or email_address_obfuscated, "mailto:#{email_address}#{extras}", html_options)
60
+ html = Ultimate.Helpers.Javascript.escape_javascript(html)
61
+ string = _.map("document.write('#{html}');", __char_encode).join('')
62
+ "<script>eval(decodeURIComponent('#{string}'))</script>"
63
+ when 'hex'
64
+ email_address_encoded = __string_encode(email_address_obfuscated)
65
+ string = __string_encode('mailto:') + _.map(email_address, (char) -> if /\w/.test(char) then __char_encode(char) else char).join('')
66
+ @link_to name or email_address_encoded, "#{string}#{extras}", html_options
67
+ else
68
+ @link_to name or email_address_obfuscated, "mailto:#{email_address}#{extras}", html_options
31
69
 
32
70
 
33
71
 
@@ -44,6 +82,23 @@
44
82
  _.isObject(options) and _.outcasts.delete(options, 'remote')
45
83
 
46
84
  _add_method_to_attributes: (html_options, method) ->
47
- if _.isString(method) and method.toLowerCase() isnt "get" and not /nofollow/.test(html_options["rel"])
48
- html_options["rel"] = _.string.lstrip("#{html_options["rel"]} nofollow")
49
- html_options["data-method"] = method
85
+ if _.isString(method) and method.toLowerCase() isnt 'get' and not /nofollow/.test(html_options['rel'])
86
+ html_options['rel'] = _.string.lstrip("#{html_options['rel']} nofollow")
87
+ html_options['data-method'] = method
88
+
89
+ _convert_boolean_attributes: (html_options, bool_attrs) ->
90
+ html_options[x] = x for x in bool_attrs when _.outcasts.delete(html_options, x)
91
+ html_options
92
+
93
+ __protect_against_forgery: false
94
+ __form_authenticity_token: 'secret'
95
+ __request_forgery_protection_token: 'form_token'
96
+
97
+ _token_tag: (token = @__form_authenticity_token) ->
98
+ if token isnt false and @__protect_against_forgery
99
+ Ultimate.Helpers.Tag.tag 'input', type: 'hidden', name: @__request_forgery_protection_token, value: token
100
+ else
101
+ ''
102
+
103
+ _method_tag: (method) ->
104
+ Ultimate.Helpers.Tag.tag 'input', type: 'hidden', name: '_method', value: method