actionpack 2.1.2 → 2.2.2
Sign up to get free protection for your applications and to get access to all the features.
Potentially problematic release.
This version of actionpack might be problematic. Click here for more details.
- data/CHANGELOG +223 -7
- data/README +6 -12
- data/Rakefile +11 -11
- data/lib/action_controller.rb +9 -9
- data/lib/action_controller/assertions/response_assertions.rb +29 -78
- data/lib/action_controller/assertions/routing_assertions.rb +33 -33
- data/lib/action_controller/assertions/selector_assertions.rb +9 -5
- data/lib/action_controller/base.rb +227 -161
- data/lib/action_controller/benchmarking.rb +37 -24
- data/lib/action_controller/caching/actions.rb +53 -21
- data/lib/action_controller/caching/fragments.rb +10 -36
- data/lib/action_controller/caching/sweeping.rb +3 -3
- data/lib/action_controller/cgi_ext/session.rb +2 -22
- data/lib/action_controller/cgi_process.rb +8 -46
- data/lib/action_controller/components.rb +4 -1
- data/lib/action_controller/cookies.rb +10 -0
- data/lib/action_controller/dispatcher.rb +49 -15
- data/lib/action_controller/filters.rb +48 -10
- data/lib/action_controller/headers.rb +16 -14
- data/lib/action_controller/helpers.rb +2 -2
- data/lib/action_controller/http_authentication.rb +1 -1
- data/lib/action_controller/integration.rb +57 -60
- data/lib/action_controller/layout.rb +27 -53
- data/lib/action_controller/mime_responds.rb +5 -1
- data/lib/action_controller/mime_type.rb +64 -42
- data/lib/action_controller/mime_types.rb +2 -1
- data/lib/action_controller/performance_test.rb +16 -0
- data/lib/action_controller/polymorphic_routes.rb +16 -9
- data/lib/action_controller/rack_process.rb +303 -0
- data/lib/action_controller/request.rb +205 -97
- data/lib/action_controller/request_forgery_protection.rb +2 -2
- data/lib/action_controller/request_profiler.rb +0 -0
- data/lib/action_controller/rescue.rb +20 -115
- data/lib/action_controller/resources.rb +186 -83
- data/lib/action_controller/response.rb +140 -26
- data/lib/action_controller/routing.rb +28 -30
- data/lib/action_controller/routing/builder.rb +45 -54
- data/lib/action_controller/routing/optimisations.rb +31 -21
- data/lib/action_controller/routing/recognition_optimisation.rb +33 -27
- data/lib/action_controller/routing/route.rb +162 -147
- data/lib/action_controller/routing/route_set.rb +8 -7
- data/lib/action_controller/routing/routing_ext.rb +4 -1
- data/lib/action_controller/routing/segments.rb +50 -21
- data/lib/action_controller/session/cookie_store.rb +3 -2
- data/lib/action_controller/session/drb_server.rb +7 -7
- data/lib/action_controller/session_management.rb +6 -2
- data/lib/action_controller/streaming.rb +15 -8
- data/lib/action_controller/templates/rescues/diagnostics.erb +2 -2
- data/lib/action_controller/templates/rescues/template_error.erb +2 -2
- data/lib/action_controller/test_case.rb +66 -2
- data/lib/action_controller/test_process.rb +71 -66
- data/lib/action_controller/translation.rb +13 -0
- data/lib/action_controller/url_rewriter.rb +90 -13
- data/lib/action_controller/vendor/html-scanner/html/node.rb +9 -2
- data/lib/action_controller/vendor/html-scanner/html/sanitizer.rb +1 -1
- data/lib/action_controller/vendor/html-scanner/html/selector.rb +2 -2
- data/lib/action_controller/verification.rb +2 -2
- data/lib/action_pack/version.rb +1 -1
- data/lib/action_view.rb +19 -11
- data/lib/action_view/base.rb +184 -150
- data/lib/action_view/helpers.rb +38 -0
- data/lib/action_view/helpers/active_record_helper.rb +56 -27
- data/lib/action_view/helpers/asset_tag_helper.rb +356 -153
- data/lib/action_view/helpers/atom_feed_helper.rb +74 -19
- data/lib/action_view/helpers/benchmark_helper.rb +3 -3
- data/lib/action_view/helpers/cache_helper.rb +1 -2
- data/lib/action_view/helpers/capture_helper.rb +19 -44
- data/lib/action_view/helpers/date_helper.rb +486 -296
- data/lib/action_view/helpers/debug_helper.rb +20 -13
- data/lib/action_view/helpers/form_helper.rb +71 -30
- data/lib/action_view/helpers/form_options_helper.rb +15 -85
- data/lib/action_view/helpers/form_tag_helper.rb +61 -38
- data/lib/action_view/helpers/javascript_helper.rb +80 -89
- data/lib/action_view/helpers/number_helper.rb +179 -74
- data/lib/action_view/helpers/prototype_helper.rb +216 -201
- data/lib/action_view/helpers/record_tag_helper.rb +4 -5
- data/lib/action_view/helpers/sanitize_helper.rb +65 -33
- data/lib/action_view/helpers/scriptaculous_helper.rb +2 -2
- data/lib/action_view/helpers/tag_helper.rb +39 -22
- data/lib/action_view/helpers/text_helper.rb +212 -118
- data/lib/action_view/helpers/translation_helper.rb +21 -0
- data/lib/action_view/helpers/url_helper.rb +100 -58
- data/lib/action_view/inline_template.rb +13 -14
- data/lib/action_view/locale/en.yml +91 -0
- data/lib/action_view/partials.rb +100 -55
- data/lib/action_view/paths.rb +125 -0
- data/lib/action_view/renderable.rb +102 -0
- data/lib/action_view/renderable_partial.rb +48 -0
- data/lib/action_view/template.rb +90 -101
- data/lib/action_view/template_error.rb +11 -21
- data/lib/action_view/template_handler.rb +8 -28
- data/lib/action_view/template_handlers.rb +45 -0
- data/lib/action_view/template_handlers/builder.rb +5 -15
- data/lib/action_view/template_handlers/erb.rb +9 -6
- data/lib/action_view/template_handlers/rjs.rb +2 -17
- data/lib/action_view/test_case.rb +7 -4
- data/test/abstract_unit.rb +4 -1
- data/test/active_record_unit.rb +28 -30
- data/test/activerecord/render_partial_with_record_identification_test.rb +25 -12
- data/test/controller/action_pack_assertions_test.rb +8 -37
- data/test/controller/addresses_render_test.rb +0 -3
- data/test/controller/assert_select_test.rb +51 -24
- data/test/controller/base_test.rb +4 -4
- data/test/controller/caching_test.rb +136 -66
- data/test/controller/capture_test.rb +1 -21
- data/test/controller/cgi_test.rb +157 -10
- data/test/controller/components_test.rb +41 -25
- data/test/controller/content_type_test.rb +49 -17
- data/test/controller/cookie_test.rb +1 -1
- data/test/controller/deprecation/deprecated_base_methods_test.rb +0 -3
- data/test/controller/dispatcher_test.rb +9 -1
- data/test/controller/filter_params_test.rb +2 -2
- data/test/controller/filters_test.rb +13 -13
- data/test/controller/html-scanner/cdata_node_test.rb +15 -0
- data/test/controller/html-scanner/node_test.rb +21 -0
- data/test/controller/html-scanner/sanitizer_test.rb +14 -0
- data/test/controller/integration_test.rb +167 -6
- data/test/controller/layout_test.rb +11 -68
- data/test/controller/logging_test.rb +46 -0
- data/test/controller/mime_responds_test.rb +61 -59
- data/test/controller/mime_type_test.rb +6 -6
- data/test/controller/polymorphic_routes_test.rb +37 -2
- data/test/controller/rack_test.rb +323 -0
- data/test/controller/redirect_test.rb +72 -71
- data/test/controller/render_test.rb +1120 -108
- data/test/controller/request_forgery_protection_test.rb +66 -52
- data/test/controller/request_test.rb +103 -146
- data/test/controller/rescue_test.rb +20 -24
- data/test/controller/resources_test.rb +408 -25
- data/test/controller/routing_test.rb +1774 -1774
- data/test/controller/send_file_test.rb +0 -4
- data/test/controller/session/cookie_store_test.rb +53 -1
- data/test/controller/test_test.rb +15 -37
- data/test/controller/translation_test.rb +26 -0
- data/test/controller/url_rewriter_test.rb +27 -28
- data/test/controller/view_paths_test.rb +48 -47
- data/test/fixtures/_top_level_partial.html.erb +1 -0
- data/test/fixtures/_top_level_partial_only.erb +1 -0
- data/test/fixtures/developers/_developer.erb +1 -0
- data/test/fixtures/fun/games/_game.erb +1 -0
- data/test/fixtures/fun/serious/games/_game.erb +1 -0
- data/test/fixtures/functional_caching/formatted_fragment_cached.html.erb +3 -0
- data/test/fixtures/functional_caching/formatted_fragment_cached.js.rjs +6 -0
- data/test/fixtures/functional_caching/formatted_fragment_cached.xml.builder +5 -0
- data/test/fixtures/functional_caching/inline_fragment_cached.html.erb +2 -0
- data/test/fixtures/layouts/_column.html.erb +2 -0
- data/test/fixtures/projects/_project.erb +1 -0
- data/test/fixtures/public/javascripts/subdir/subdir.js +1 -0
- data/test/fixtures/public/stylesheets/subdir/subdir.css +1 -0
- data/test/fixtures/replies/_reply.erb +1 -0
- data/test/fixtures/test/_counter.html.erb +1 -0
- data/test/fixtures/test/_customer.erb +1 -1
- data/test/fixtures/test/_customer_with_var.erb +1 -0
- data/test/fixtures/test/_layout_for_block_with_args.html.erb +3 -0
- data/test/fixtures/test/_local_inspector.html.erb +1 -0
- data/test/fixtures/test/_partial_with_only_html_version.html.erb +1 -0
- data/test/fixtures/test/hello.builder +1 -1
- data/test/fixtures/test/hyphen-ated.erb +1 -0
- data/test/fixtures/test/implicit_content_type.atom.builder +2 -0
- data/test/fixtures/test/nested_layout.erb +3 -0
- data/test/fixtures/test/non_erb_block_content_for.builder +1 -1
- data/test/fixtures/test/sub_template_raise.html.erb +1 -0
- data/test/fixtures/test/template.erb +1 -0
- data/test/fixtures/test/using_layout_around_block_with_args.html.erb +1 -0
- data/test/template/active_record_helper_i18n_test.rb +46 -0
- data/test/template/active_record_helper_test.rb +24 -24
- data/test/template/asset_tag_helper_test.rb +161 -29
- data/test/template/atom_feed_helper_test.rb +114 -5
- data/test/template/compiled_templates_test.rb +59 -0
- data/test/template/date_helper_i18n_test.rb +113 -0
- data/test/template/date_helper_test.rb +403 -109
- data/test/template/form_helper_test.rb +213 -154
- data/test/template/form_options_helper_test.rb +249 -897
- data/test/template/form_tag_helper_test.rb +80 -32
- data/test/template/javascript_helper_test.rb +17 -18
- data/test/template/number_helper_i18n_test.rb +54 -0
- data/test/template/number_helper_test.rb +43 -13
- data/test/template/prototype_helper_test.rb +101 -84
- data/test/template/record_tag_helper_test.rb +24 -20
- data/test/template/render_test.rb +193 -0
- data/test/template/sanitize_helper_test.rb +3 -3
- data/test/template/tag_helper_test.rb +34 -14
- data/test/template/text_helper_test.rb +83 -9
- data/test/template/translation_helper_test.rb +28 -0
- data/test/template/url_helper_test.rb +55 -18
- metadata +57 -18
- data/lib/action_view/helpers/javascripts/controls.js +0 -963
- data/lib/action_view/helpers/javascripts/dragdrop.js +0 -972
- data/lib/action_view/helpers/javascripts/effects.js +0 -1120
- data/lib/action_view/helpers/javascripts/prototype.js +0 -4225
- data/lib/action_view/partial_template.rb +0 -70
- data/lib/action_view/template_finder.rb +0 -177
- data/lib/action_view/template_handlers/compilable.rb +0 -128
- data/test/controller/custom_handler_test.rb +0 -45
- data/test/controller/new_render_test.rb +0 -945
- data/test/fixtures/test/block_content_for.erb +0 -2
- data/test/fixtures/test/erb_content_for.erb +0 -2
- data/test/template/deprecated_erb_variable_test.rb +0 -9
- data/test/template/template_finder_test.rb +0 -73
- data/test/template/template_object_test.rb +0 -95
@@ -49,11 +49,10 @@ module ActionView
|
|
49
49
|
#
|
50
50
|
def content_tag_for(tag_name, record, *args, &block)
|
51
51
|
prefix = args.first.is_a?(Hash) ? nil : args.shift
|
52
|
-
options = args.
|
53
|
-
|
54
|
-
|
55
|
-
block.binding
|
52
|
+
options = args.extract_options!
|
53
|
+
options.merge!({ :class => "#{dom_class(record)} #{options[:class]}".strip, :id => dom_id(record, prefix) })
|
54
|
+
content_tag(tag_name, options, &block)
|
56
55
|
end
|
57
56
|
end
|
58
57
|
end
|
59
|
-
end
|
58
|
+
end
|
@@ -1,22 +1,27 @@
|
|
1
1
|
require 'action_view/helpers/tag_helper'
|
2
|
-
|
2
|
+
|
3
|
+
begin
|
4
|
+
require 'html/document'
|
5
|
+
rescue LoadError
|
6
|
+
html_scanner_path = "#{File.dirname(__FILE__)}/../../action_controller/vendor/html-scanner"
|
7
|
+
if File.directory?(html_scanner_path)
|
8
|
+
$:.unshift html_scanner_path
|
9
|
+
require 'html/document'
|
10
|
+
end
|
11
|
+
end
|
3
12
|
|
4
13
|
module ActionView
|
5
14
|
module Helpers #:nodoc:
|
6
15
|
# The SanitizeHelper module provides a set of methods for scrubbing text of undesired HTML elements.
|
7
16
|
# These helper methods extend ActionView making them callable within your template files.
|
8
17
|
module SanitizeHelper
|
9
|
-
def self.included(base)
|
10
|
-
base.extend(ClassMethods)
|
11
|
-
end
|
12
|
-
|
13
18
|
# This +sanitize+ helper will html encode all tags and strip all attributes that aren't specifically allowed.
|
14
19
|
# It also strips href/src tags with invalid protocols, like javascript: especially. It does its best to counter any
|
15
20
|
# tricks that hackers may use, like throwing in unicode/ascii/hex values to get past the javascript: filters. Check out
|
16
21
|
# the extensive test suite.
|
17
22
|
#
|
18
23
|
# <%= sanitize @article.body %>
|
19
|
-
#
|
24
|
+
#
|
20
25
|
# You can add or remove tags/attributes if you want to customize it a bit. See ActionView::Base for full docs on the
|
21
26
|
# available options. You can add tags/attributes for single uses of +sanitize+ by passing either the <tt>:attributes</tt> or <tt>:tags</tt> options:
|
22
27
|
#
|
@@ -27,27 +32,27 @@ module ActionView
|
|
27
32
|
# Custom Use (only the mentioned tags and attributes are allowed, nothing else)
|
28
33
|
#
|
29
34
|
# <%= sanitize @article.body, :tags => %w(table tr td), :attributes => %w(id class style)
|
30
|
-
#
|
35
|
+
#
|
31
36
|
# Add table tags to the default allowed tags
|
32
|
-
#
|
37
|
+
#
|
33
38
|
# Rails::Initializer.run do |config|
|
34
39
|
# config.action_view.sanitized_allowed_tags = 'table', 'tr', 'td'
|
35
40
|
# end
|
36
|
-
#
|
41
|
+
#
|
37
42
|
# Remove tags to the default allowed tags
|
38
|
-
#
|
43
|
+
#
|
39
44
|
# Rails::Initializer.run do |config|
|
40
45
|
# config.after_initialize do
|
41
46
|
# ActionView::Base.sanitized_allowed_tags.delete 'div'
|
42
47
|
# end
|
43
48
|
# end
|
44
|
-
#
|
49
|
+
#
|
45
50
|
# Change allowed default attributes
|
46
|
-
#
|
51
|
+
#
|
47
52
|
# Rails::Initializer.run do |config|
|
48
53
|
# config.action_view.sanitized_allowed_attributes = 'id', 'class', 'style'
|
49
54
|
# end
|
50
|
-
#
|
55
|
+
#
|
51
56
|
# Please note that sanitizing user-provided text does not guarantee that the
|
52
57
|
# resulting markup is valid (conforming to a document type) or even well-formed.
|
53
58
|
# The output may still contain e.g. unescaped '<', '>', '&' characters and
|
@@ -62,8 +67,8 @@ module ActionView
|
|
62
67
|
self.class.white_list_sanitizer.sanitize_css(style)
|
63
68
|
end
|
64
69
|
|
65
|
-
# Strips all HTML tags from the +html+, including comments. This uses the
|
66
|
-
# html-scanner tokenizer and so its HTML parsing ability is limited by
|
70
|
+
# Strips all HTML tags from the +html+, including comments. This uses the
|
71
|
+
# html-scanner tokenizer and so its HTML parsing ability is limited by
|
67
72
|
# that of html-scanner.
|
68
73
|
#
|
69
74
|
# ==== Examples
|
@@ -73,10 +78,10 @@ module ActionView
|
|
73
78
|
#
|
74
79
|
# strip_tags("<b>Bold</b> no more! <a href='more.html'>See more here</a>...")
|
75
80
|
# # => Bold no more! See more here...
|
76
|
-
#
|
81
|
+
#
|
77
82
|
# strip_tags("<div id='top-bar'>Welcome to my website!</div>")
|
78
83
|
# # => Welcome to my website!
|
79
|
-
def strip_tags(html)
|
84
|
+
def strip_tags(html)
|
80
85
|
self.class.full_sanitizer.sanitize(html)
|
81
86
|
end
|
82
87
|
|
@@ -96,21 +101,48 @@ module ActionView
|
|
96
101
|
end
|
97
102
|
|
98
103
|
module ClassMethods #:nodoc:
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
104
|
+
attr_writer :full_sanitizer, :link_sanitizer, :white_list_sanitizer
|
105
|
+
|
106
|
+
def sanitized_protocol_separator
|
107
|
+
white_list_sanitizer.protocol_separator
|
108
|
+
end
|
109
|
+
|
110
|
+
def sanitized_uri_attributes
|
111
|
+
white_list_sanitizer.uri_attributes
|
112
|
+
end
|
113
|
+
|
114
|
+
def sanitized_bad_tags
|
115
|
+
white_list_sanitizer.bad_tags
|
116
|
+
end
|
117
|
+
|
118
|
+
def sanitized_allowed_tags
|
119
|
+
white_list_sanitizer.allowed_tags
|
120
|
+
end
|
121
|
+
|
122
|
+
def sanitized_allowed_attributes
|
123
|
+
white_list_sanitizer.allowed_attributes
|
124
|
+
end
|
125
|
+
|
126
|
+
def sanitized_allowed_css_properties
|
127
|
+
white_list_sanitizer.allowed_css_properties
|
128
|
+
end
|
129
|
+
|
130
|
+
def sanitized_allowed_css_keywords
|
131
|
+
white_list_sanitizer.allowed_css_keywords
|
132
|
+
end
|
133
|
+
|
134
|
+
def sanitized_shorthand_css_properties
|
135
|
+
white_list_sanitizer.shorthand_css_properties
|
136
|
+
end
|
137
|
+
|
138
|
+
def sanitized_allowed_protocols
|
139
|
+
white_list_sanitizer.allowed_protocols
|
140
|
+
end
|
141
|
+
|
142
|
+
def sanitized_protocol_separator=(value)
|
143
|
+
white_list_sanitizer.protocol_separator = value
|
144
|
+
end
|
145
|
+
|
114
146
|
# Gets the HTML::FullSanitizer instance used by +strip_tags+. Replace with
|
115
147
|
# any object that responds to +sanitize+.
|
116
148
|
#
|
@@ -184,7 +216,7 @@ module ActionView
|
|
184
216
|
HTML::WhiteListSanitizer.allowed_attributes.merge(attributes)
|
185
217
|
end
|
186
218
|
|
187
|
-
# Adds to the Set of allowed CSS properties for the #sanitize and +sanitize_css+
|
219
|
+
# Adds to the Set of allowed CSS properties for the #sanitize and +sanitize_css+ helpers.
|
188
220
|
#
|
189
221
|
# Rails::Initializer.run do |config|
|
190
222
|
# config.action_view.sanitized_allowed_css_properties = 'expression'
|
@@ -95,7 +95,7 @@ module ActionView
|
|
95
95
|
# * <tt>:containment</tt> - Takes an element or array of elements to treat as
|
96
96
|
# potential drop targets (defaults to the original target element).
|
97
97
|
#
|
98
|
-
# * <tt>:only</tt> - A CSS class name or
|
98
|
+
# * <tt>:only</tt> - A CSS class name or array of class names used to filter
|
99
99
|
# out child elements as candidates.
|
100
100
|
#
|
101
101
|
# * <tt>:scroll</tt> - Determines whether to scroll the list during drag
|
@@ -193,7 +193,7 @@ module ActionView
|
|
193
193
|
#
|
194
194
|
# * <tt>:onDrop</tt> - Called when a +draggable_element+ is dropped onto
|
195
195
|
# this element. Override this callback with a JavaScript expression to
|
196
|
-
# change the default drop
|
196
|
+
# change the default drop behaviour. Example:
|
197
197
|
#
|
198
198
|
# :onDrop => "function(draggable_element, droppable_element, event) { alert('I like bananas') }"
|
199
199
|
#
|
@@ -9,17 +9,17 @@ module ActionView
|
|
9
9
|
module TagHelper
|
10
10
|
include ERB::Util
|
11
11
|
|
12
|
-
BOOLEAN_ATTRIBUTES = %w(disabled readonly multiple).to_set
|
12
|
+
BOOLEAN_ATTRIBUTES = %w(disabled readonly multiple checked).to_set
|
13
13
|
BOOLEAN_ATTRIBUTES.merge(BOOLEAN_ATTRIBUTES.map(&:to_sym))
|
14
14
|
|
15
|
-
# Returns an empty HTML tag of type +name+ which by default is XHTML
|
16
|
-
# compliant. Set +open+ to true to create an open tag compatible
|
17
|
-
# with HTML 4.0 and below. Add HTML attributes by passing an attributes
|
15
|
+
# Returns an empty HTML tag of type +name+ which by default is XHTML
|
16
|
+
# compliant. Set +open+ to true to create an open tag compatible
|
17
|
+
# with HTML 4.0 and below. Add HTML attributes by passing an attributes
|
18
18
|
# hash to +options+. Set +escape+ to false to disable attribute value
|
19
19
|
# escaping.
|
20
20
|
#
|
21
21
|
# ==== Options
|
22
|
-
# The +options+ hash is used with attributes with no value like (<tt>disabled</tt> and
|
22
|
+
# The +options+ hash is used with attributes with no value like (<tt>disabled</tt> and
|
23
23
|
# <tt>readonly</tt>), which you can give a value of true in the +options+ hash. You can use
|
24
24
|
# symbols or strings for the attribute names.
|
25
25
|
#
|
@@ -30,7 +30,7 @@ module ActionView
|
|
30
30
|
# tag("br", nil, true)
|
31
31
|
# # => <br>
|
32
32
|
#
|
33
|
-
# tag("input", { :type => 'text', :disabled => true })
|
33
|
+
# tag("input", { :type => 'text', :disabled => true })
|
34
34
|
# # => <input type="text" disabled="disabled" />
|
35
35
|
#
|
36
36
|
# tag("img", { :src => "open & shut.png" })
|
@@ -43,13 +43,13 @@ module ActionView
|
|
43
43
|
end
|
44
44
|
|
45
45
|
# Returns an HTML block tag of type +name+ surrounding the +content+. Add
|
46
|
-
# HTML attributes by passing an attributes hash to +options+.
|
46
|
+
# HTML attributes by passing an attributes hash to +options+.
|
47
47
|
# Instead of passing the content as an argument, you can also use a block
|
48
48
|
# in which case, you pass your +options+ as the second parameter.
|
49
49
|
# Set escape to false to disable attribute value escaping.
|
50
50
|
#
|
51
51
|
# ==== Options
|
52
|
-
# The +options+ hash is used with attributes with no value like (<tt>disabled</tt> and
|
52
|
+
# The +options+ hash is used with attributes with no value like (<tt>disabled</tt> and
|
53
53
|
# <tt>readonly</tt>), which you can give a value of true in the +options+ hash. You can use
|
54
54
|
# symbols or strings for the attribute names.
|
55
55
|
#
|
@@ -64,16 +64,19 @@ module ActionView
|
|
64
64
|
# <% content_tag :div, :class => "strong" do -%>
|
65
65
|
# Hello world!
|
66
66
|
# <% end -%>
|
67
|
-
# # => <div class="strong"
|
67
|
+
# # => <div class="strong">Hello world!</div>
|
68
68
|
def content_tag(name, content_or_options_with_block = nil, options = nil, escape = true, &block)
|
69
69
|
if block_given?
|
70
70
|
options = content_or_options_with_block if content_or_options_with_block.is_a?(Hash)
|
71
|
-
|
72
|
-
|
73
|
-
|
71
|
+
content_tag = content_tag_string(name, capture(&block), options, escape)
|
72
|
+
|
73
|
+
if block_called_from_erb?(block)
|
74
|
+
concat(content_tag)
|
75
|
+
else
|
76
|
+
content_tag
|
77
|
+
end
|
74
78
|
else
|
75
|
-
|
76
|
-
content_tag_string(name, content, options, escape)
|
79
|
+
content_tag_string(name, content_or_options_with_block, options, escape)
|
77
80
|
end
|
78
81
|
end
|
79
82
|
|
@@ -105,6 +108,22 @@ module ActionView
|
|
105
108
|
end
|
106
109
|
|
107
110
|
private
|
111
|
+
BLOCK_CALLED_FROM_ERB = 'defined? __in_erb_template'
|
112
|
+
|
113
|
+
if RUBY_VERSION < '1.9.0'
|
114
|
+
# Check whether we're called from an erb template.
|
115
|
+
# We'd return a string in any other case, but erb <%= ... %>
|
116
|
+
# can't take an <% end %> later on, so we have to use <% ... %>
|
117
|
+
# and implicitly concat.
|
118
|
+
def block_called_from_erb?(block)
|
119
|
+
block && eval(BLOCK_CALLED_FROM_ERB, block)
|
120
|
+
end
|
121
|
+
else
|
122
|
+
def block_called_from_erb?(block)
|
123
|
+
block && eval(BLOCK_CALLED_FROM_ERB, block.binding)
|
124
|
+
end
|
125
|
+
end
|
126
|
+
|
108
127
|
def content_tag_string(name, content, options, escape = true)
|
109
128
|
tag_options = tag_options(options, escape) if options
|
110
129
|
"<#{name}#{tag_options}>#{content}</#{name}>"
|
@@ -114,10 +133,12 @@ module ActionView
|
|
114
133
|
unless options.blank?
|
115
134
|
attrs = []
|
116
135
|
if escape
|
117
|
-
options.
|
118
|
-
|
119
|
-
|
120
|
-
|
136
|
+
options.each_pair do |key, value|
|
137
|
+
if BOOLEAN_ATTRIBUTES.include?(key)
|
138
|
+
attrs << %(#{key}="#{key}") if value
|
139
|
+
else
|
140
|
+
attrs << %(#{key}="#{escape_once(value)}") if !value.nil?
|
141
|
+
end
|
121
142
|
end
|
122
143
|
else
|
123
144
|
attrs = options.map { |key, value| %(#{key}="#{value}") }
|
@@ -125,10 +146,6 @@ module ActionView
|
|
125
146
|
" #{attrs.sort * ' '}" unless attrs.empty?
|
126
147
|
end
|
127
148
|
end
|
128
|
-
|
129
|
-
def block_is_within_action_view?(block)
|
130
|
-
eval("defined? _erbout", block.binding)
|
131
|
-
end
|
132
149
|
end
|
133
150
|
end
|
134
151
|
end
|
@@ -1,5 +1,14 @@
|
|
1
1
|
require 'action_view/helpers/tag_helper'
|
2
|
-
|
2
|
+
|
3
|
+
begin
|
4
|
+
require 'html/document'
|
5
|
+
rescue LoadError
|
6
|
+
html_scanner_path = "#{File.dirname(__FILE__)}/../../action_controller/vendor/html-scanner"
|
7
|
+
if File.directory?(html_scanner_path)
|
8
|
+
$:.unshift html_scanner_path
|
9
|
+
require 'html/document'
|
10
|
+
end
|
11
|
+
end
|
3
12
|
|
4
13
|
module ActionView
|
5
14
|
module Helpers #:nodoc:
|
@@ -15,55 +24,69 @@ module ActionView
|
|
15
24
|
#
|
16
25
|
# ==== Examples
|
17
26
|
# <%
|
18
|
-
# concat "hello"
|
27
|
+
# concat "hello"
|
19
28
|
# # is the equivalent of <%= "hello" %>
|
20
29
|
#
|
21
30
|
# if (logged_in == true):
|
22
|
-
# concat "Logged in!"
|
31
|
+
# concat "Logged in!"
|
23
32
|
# else
|
24
|
-
# concat link_to('login', :action => login)
|
33
|
+
# concat link_to('login', :action => login)
|
25
34
|
# end
|
26
35
|
# # will either display "Logged in!" or a login link
|
27
36
|
# %>
|
28
|
-
def concat(string,
|
29
|
-
|
37
|
+
def concat(string, unused_binding = nil)
|
38
|
+
if unused_binding
|
39
|
+
ActiveSupport::Deprecation.warn("The binding argument of #concat is no longer needed. Please remove it from your views and helpers.", caller)
|
40
|
+
end
|
41
|
+
|
42
|
+
output_buffer << string
|
30
43
|
end
|
31
44
|
|
32
|
-
if
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
45
|
+
# Truncates a given +text+ after a given <tt>:length</tt> if +text+ is longer than <tt>:length</tt>
|
46
|
+
# (defaults to 30). The last characters will be replaced with the <tt>:omission</tt> (defaults to "...").
|
47
|
+
#
|
48
|
+
# ==== Examples
|
49
|
+
#
|
50
|
+
# truncate("Once upon a time in a world far far away")
|
51
|
+
# # => Once upon a time in a world f...
|
52
|
+
#
|
53
|
+
# truncate("Once upon a time in a world far far away", :length => 14)
|
54
|
+
# # => Once upon a...
|
55
|
+
#
|
56
|
+
# truncate("And they found that many people were sleeping better.", :length => 25, "(clipped)")
|
57
|
+
# # => And they found that many (clipped)
|
58
|
+
#
|
59
|
+
# truncate("And they found that many people were sleeping better.", :omission => "... (continued)", :length => 15)
|
60
|
+
# # => And they found... (continued)
|
61
|
+
#
|
62
|
+
# You can still use <tt>truncate</tt> with the old API that accepts the
|
63
|
+
# +length+ as its optional second and the +ellipsis+ as its
|
64
|
+
# optional third parameter:
|
65
|
+
# truncate("Once upon a time in a world far far away", 14)
|
66
|
+
# # => Once upon a time in a world f...
|
67
|
+
#
|
68
|
+
# truncate("And they found that many people were sleeping better.", 15, "... (continued)")
|
69
|
+
# # => And they found... (continued)
|
70
|
+
def truncate(text, *args)
|
71
|
+
options = args.extract_options!
|
72
|
+
unless args.empty?
|
73
|
+
ActiveSupport::Deprecation.warn('truncate takes an option hash instead of separate ' +
|
74
|
+
'length and omission arguments', caller)
|
75
|
+
|
76
|
+
options[:length] = args[0] || 30
|
77
|
+
options[:omission] = args[1] || "..."
|
55
78
|
end
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
79
|
+
options.reverse_merge!(:length => 30, :omission => "...")
|
80
|
+
|
81
|
+
if text
|
82
|
+
l = options[:length] - options[:omission].mb_chars.length
|
83
|
+
chars = text.mb_chars
|
84
|
+
(chars.length > options[:length] ? chars[0...l] + options[:omission] : text).to_s
|
62
85
|
end
|
63
86
|
end
|
64
87
|
|
65
88
|
# Highlights one or more +phrases+ everywhere in +text+ by inserting it into
|
66
|
-
# a
|
89
|
+
# a <tt>:highlighter</tt> string. The highlighter can be specialized by passing <tt>:highlighter</tt>
|
67
90
|
# as a single-quoted string with \1 where the phrase is to be inserted (defaults to
|
68
91
|
# '<strong class="highlight">\1</strong>')
|
69
92
|
#
|
@@ -74,75 +97,78 @@ module ActionView
|
|
74
97
|
# highlight('You searched for: ruby, rails, dhh', 'actionpack')
|
75
98
|
# # => You searched for: ruby, rails, dhh
|
76
99
|
#
|
77
|
-
# highlight('You searched for: rails', ['for', 'rails'], '<em>\1</em>')
|
100
|
+
# highlight('You searched for: rails', ['for', 'rails'], :highlighter => '<em>\1</em>')
|
78
101
|
# # => You searched <em>for</em>: <em>rails</em>
|
79
102
|
#
|
80
|
-
# highlight('You searched for: rails', 'rails',
|
81
|
-
# # => You searched for: <a href=
|
82
|
-
|
103
|
+
# highlight('You searched for: rails', 'rails', :highlighter => '<a href="search?q=\1">\1</a>')
|
104
|
+
# # => You searched for: <a href="search?q=rails">rails</a>
|
105
|
+
#
|
106
|
+
# You can still use <tt>highlight</tt> with the old API that accepts the
|
107
|
+
# +highlighter+ as its optional third parameter:
|
108
|
+
# highlight('You searched for: rails', 'rails', '<a href="search?q=\1">\1</a>') # => You searched for: <a href="search?q=rails">rails</a>
|
109
|
+
def highlight(text, phrases, *args)
|
110
|
+
options = args.extract_options!
|
111
|
+
unless args.empty?
|
112
|
+
options[:highlighter] = args[0] || '<strong class="highlight">\1</strong>'
|
113
|
+
end
|
114
|
+
options.reverse_merge!(:highlighter => '<strong class="highlight">\1</strong>')
|
115
|
+
|
83
116
|
if text.blank? || phrases.blank?
|
84
117
|
text
|
85
118
|
else
|
86
119
|
match = Array(phrases).map { |p| Regexp.escape(p) }.join('|')
|
87
|
-
text.gsub(/(#{match})/i, highlighter)
|
120
|
+
text.gsub(/(#{match})/i, options[:highlighter])
|
88
121
|
end
|
89
122
|
end
|
90
123
|
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
prefix + text.chars[start_pos..end_pos].strip + postfix
|
125
|
-
else
|
126
|
-
nil
|
127
|
-
end
|
128
|
-
end
|
124
|
+
# Extracts an excerpt from +text+ that matches the first instance of +phrase+.
|
125
|
+
# The <tt>:radius</tt> option expands the excerpt on each side of the first occurrence of +phrase+ by the number of characters
|
126
|
+
# defined in <tt>:radius</tt> (which defaults to 100). If the excerpt radius overflows the beginning or end of the +text+,
|
127
|
+
# then the <tt>:omission</tt> option (which defaults to "...") will be prepended/appended accordingly. The resulting string
|
128
|
+
# will be stripped in any case. If the +phrase+ isn't found, nil is returned.
|
129
|
+
#
|
130
|
+
# ==== Examples
|
131
|
+
# excerpt('This is an example', 'an', :radius => 5)
|
132
|
+
# # => ...s is an exam...
|
133
|
+
#
|
134
|
+
# excerpt('This is an example', 'is', :radius => 5)
|
135
|
+
# # => This is a...
|
136
|
+
#
|
137
|
+
# excerpt('This is an example', 'is')
|
138
|
+
# # => This is an example
|
139
|
+
#
|
140
|
+
# excerpt('This next thing is an example', 'ex', :radius => 2)
|
141
|
+
# # => ...next...
|
142
|
+
#
|
143
|
+
# excerpt('This is also an example', 'an', :radius => 8, :omission => '<chop> ')
|
144
|
+
# # => <chop> is also an example
|
145
|
+
#
|
146
|
+
# You can still use <tt>excerpt</tt> with the old API that accepts the
|
147
|
+
# +radius+ as its optional third and the +ellipsis+ as its
|
148
|
+
# optional forth parameter:
|
149
|
+
# excerpt('This is an example', 'an', 5) # => ...s is an exam...
|
150
|
+
# excerpt('This is also an example', 'an', 8, '<chop> ') # => <chop> is also an example
|
151
|
+
def excerpt(text, phrase, *args)
|
152
|
+
options = args.extract_options!
|
153
|
+
unless args.empty?
|
154
|
+
options[:radius] = args[0] || 100
|
155
|
+
options[:omission] = args[1] || "..."
|
129
156
|
end
|
130
|
-
|
131
|
-
def excerpt(text, phrase, radius = 100, excerpt_string = "...") #:nodoc:
|
132
|
-
if text && phrase
|
133
|
-
phrase = Regexp.escape(phrase)
|
157
|
+
options.reverse_merge!(:radius => 100, :omission => "...")
|
134
158
|
|
135
|
-
|
136
|
-
|
137
|
-
end_pos = [ [ found_pos + phrase.length + radius - 1, 0].max, text.length ].min
|
159
|
+
if text && phrase
|
160
|
+
phrase = Regexp.escape(phrase)
|
138
161
|
|
139
|
-
|
140
|
-
|
162
|
+
if found_pos = text.mb_chars =~ /(#{phrase})/i
|
163
|
+
start_pos = [ found_pos - options[:radius], 0 ].max
|
164
|
+
end_pos = [ [ found_pos + phrase.mb_chars.length + options[:radius] - 1, 0].max, text.mb_chars.length ].min
|
141
165
|
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
|
166
|
+
prefix = start_pos > 0 ? options[:omission] : ""
|
167
|
+
postfix = end_pos < text.mb_chars.length - 1 ? options[:omission] : ""
|
168
|
+
|
169
|
+
prefix + text.mb_chars[start_pos..end_pos].strip + postfix
|
170
|
+
else
|
171
|
+
nil
|
146
172
|
end
|
147
173
|
end
|
148
174
|
end
|
@@ -172,20 +198,31 @@ module ActionView
|
|
172
198
|
# (which is 80 by default).
|
173
199
|
#
|
174
200
|
# ==== Examples
|
175
|
-
# word_wrap('Once upon a time', 4)
|
176
|
-
# # => Once\nupon\na\ntime
|
177
|
-
#
|
178
|
-
# word_wrap('Once upon a time', 8)
|
179
|
-
# # => Once upon\na time
|
180
201
|
#
|
181
202
|
# word_wrap('Once upon a time')
|
182
203
|
# # => Once upon a time
|
183
204
|
#
|
184
|
-
# word_wrap('Once upon a time
|
205
|
+
# word_wrap('Once upon a time, in a kingdom called Far Far Away, a king fell ill, and finding a successor to the throne turned out to be more trouble than anyone could have imagined...')
|
206
|
+
# # => Once upon a time, in a kingdom called Far Far Away, a king fell ill, and finding\n a successor to the throne turned out to be more trouble than anyone could have\n imagined...
|
207
|
+
#
|
208
|
+
# word_wrap('Once upon a time', :line_width => 8)
|
209
|
+
# # => Once upon\na time
|
210
|
+
#
|
211
|
+
# word_wrap('Once upon a time', :line_width => 1)
|
185
212
|
# # => Once\nupon\na\ntime
|
186
|
-
|
213
|
+
#
|
214
|
+
# You can still use <tt>word_wrap</tt> with the old API that accepts the
|
215
|
+
# +line_width+ as its optional second parameter:
|
216
|
+
# word_wrap('Once upon a time', 8) # => Once upon\na time
|
217
|
+
def word_wrap(text, *args)
|
218
|
+
options = args.extract_options!
|
219
|
+
unless args.blank?
|
220
|
+
options[:line_width] = args[0] || 80
|
221
|
+
end
|
222
|
+
options.reverse_merge!(:line_width => 80)
|
223
|
+
|
187
224
|
text.split("\n").collect do |line|
|
188
|
-
line.length > line_width ? line.gsub(/(.{1,#{line_width}})(\s+|$)/, "\\1\n").strip : line
|
225
|
+
line.length > options[:line_width] ? line.gsub(/(.{1,#{options[:line_width]}})(\s+|$)/, "\\1\n").strip : line
|
189
226
|
end * "\n"
|
190
227
|
end
|
191
228
|
|
@@ -215,7 +252,7 @@ module ActionView
|
|
215
252
|
""
|
216
253
|
else
|
217
254
|
textilized = RedCloth.new(text, [ :hard_breaks ])
|
218
|
-
textilized.hard_breaks = true if textilized.respond_to?(
|
255
|
+
textilized.hard_breaks = true if textilized.respond_to?(:hard_breaks=)
|
219
256
|
textilized.to_html
|
220
257
|
end
|
221
258
|
end
|
@@ -307,9 +344,9 @@ module ActionView
|
|
307
344
|
text << "</p>"
|
308
345
|
end
|
309
346
|
|
310
|
-
# Turns all URLs and e-mail addresses into clickable links. The
|
347
|
+
# Turns all URLs and e-mail addresses into clickable links. The <tt>:link</tt> option
|
311
348
|
# will limit what should be linked. You can add HTML attributes to the links using
|
312
|
-
#
|
349
|
+
# <tt>:href_options</tt>. Possible values for <tt>:link</tt> are <tt>:all</tt> (default),
|
313
350
|
# <tt>:email_addresses</tt>, and <tt>:urls</tt>. If a block is given, each URL and
|
314
351
|
# e-mail address is yielded and the result is used as the link text.
|
315
352
|
#
|
@@ -318,26 +355,46 @@ module ActionView
|
|
318
355
|
# # => "Go to <a href=\"http://www.rubyonrails.org\">http://www.rubyonrails.org</a> and
|
319
356
|
# # say hello to <a href=\"mailto:david@loudthinking.com\">david@loudthinking.com</a>"
|
320
357
|
#
|
321
|
-
# auto_link("Visit http://www.loudthinking.com/ or e-mail david@loudthinking.com", :urls)
|
358
|
+
# auto_link("Visit http://www.loudthinking.com/ or e-mail david@loudthinking.com", :link => :urls)
|
322
359
|
# # => "Visit <a href=\"http://www.loudthinking.com/\">http://www.loudthinking.com/</a>
|
323
360
|
# # or e-mail david@loudthinking.com"
|
324
361
|
#
|
325
|
-
# auto_link("Visit http://www.loudthinking.com/ or e-mail david@loudthinking.com", :email_addresses)
|
362
|
+
# auto_link("Visit http://www.loudthinking.com/ or e-mail david@loudthinking.com", :link => :email_addresses)
|
326
363
|
# # => "Visit http://www.loudthinking.com/ or e-mail <a href=\"mailto:david@loudthinking.com\">david@loudthinking.com</a>"
|
327
364
|
#
|
328
365
|
# post_body = "Welcome to my new blog at http://www.myblog.com/. Please e-mail me at me@email.com."
|
329
|
-
# auto_link(post_body, :
|
366
|
+
# auto_link(post_body, :href_options => { :target => '_blank' }) do |text|
|
330
367
|
# truncate(text, 15)
|
331
368
|
# end
|
332
369
|
# # => "Welcome to my new blog at <a href=\"http://www.myblog.com/\" target=\"_blank\">http://www.m...</a>.
|
333
370
|
# Please e-mail me at <a href=\"mailto:me@email.com\">me@email.com</a>."
|
334
371
|
#
|
335
|
-
|
372
|
+
#
|
373
|
+
# You can still use <tt>auto_link</tt> with the old API that accepts the
|
374
|
+
# +link+ as its optional second parameter and the +html_options+ hash
|
375
|
+
# as its optional third parameter:
|
376
|
+
# post_body = "Welcome to my new blog at http://www.myblog.com/. Please e-mail me at me@email.com."
|
377
|
+
# auto_link(post_body, :urls) # => Once upon\na time
|
378
|
+
# # => "Welcome to my new blog at <a href=\"http://www.myblog.com/\">http://www.myblog.com</a>.
|
379
|
+
# Please e-mail me at me@email.com."
|
380
|
+
#
|
381
|
+
# auto_link(post_body, :all, :target => "_blank") # => Once upon\na time
|
382
|
+
# # => "Welcome to my new blog at <a href=\"http://www.myblog.com/\" target=\"_blank\">http://www.myblog.com</a>.
|
383
|
+
# Please e-mail me at <a href=\"mailto:me@email.com\">me@email.com</a>."
|
384
|
+
def auto_link(text, *args, &block)#link = :all, href_options = {}, &block)
|
336
385
|
return '' if text.blank?
|
337
|
-
|
338
|
-
|
339
|
-
|
340
|
-
|
386
|
+
|
387
|
+
options = args.size == 2 ? {} : args.extract_options! # this is necessary because the old auto_link API has a Hash as its last parameter
|
388
|
+
unless args.empty?
|
389
|
+
options[:link] = args[0] || :all
|
390
|
+
options[:html] = args[1] || {}
|
391
|
+
end
|
392
|
+
options.reverse_merge!(:link => :all, :html => {})
|
393
|
+
|
394
|
+
case options[:link].to_sym
|
395
|
+
when :all then auto_link_email_addresses(auto_link_urls(text, options[:html], &block), &block)
|
396
|
+
when :email_addresses then auto_link_email_addresses(text, &block)
|
397
|
+
when :urls then auto_link_urls(text, options[:html], &block)
|
341
398
|
end
|
342
399
|
end
|
343
400
|
|
@@ -345,8 +402,10 @@ module ActionView
|
|
345
402
|
# array every time it is called. This can be used for example, to alternate
|
346
403
|
# classes for table rows. You can use named cycles to allow nesting in loops.
|
347
404
|
# Passing a Hash as the last parameter with a <tt>:name</tt> key will create a
|
348
|
-
# named cycle.
|
349
|
-
#
|
405
|
+
# named cycle. The default name for a cycle without a +:name+ key is
|
406
|
+
# <tt>"default"</tt>. You can manually reset a cycle by calling reset_cycle
|
407
|
+
# and passing the name of the cycle. The current cycle string can be obtained
|
408
|
+
# anytime using the current_cycle method.
|
350
409
|
#
|
351
410
|
# ==== Examples
|
352
411
|
# # Alternate CSS classes for even and odd numbers...
|
@@ -393,6 +452,23 @@ module ActionView
|
|
393
452
|
return cycle.to_s
|
394
453
|
end
|
395
454
|
|
455
|
+
# Returns the current cycle string after a cycle has been started. Useful
|
456
|
+
# for complex table highlighing or any other design need which requires
|
457
|
+
# the current cycle string in more than one place.
|
458
|
+
#
|
459
|
+
# ==== Example
|
460
|
+
# # Alternate background colors
|
461
|
+
# @items = [1,2,3,4]
|
462
|
+
# <% @items.each do |item| %>
|
463
|
+
# <div style="background-color:<%= cycle("red","white","blue") %>">
|
464
|
+
# <span style="background-color:<%= current_cycle %>"><%= item %></span>
|
465
|
+
# </div>
|
466
|
+
# <% end %>
|
467
|
+
def current_cycle(name = "default")
|
468
|
+
cycle = get_cycle(name)
|
469
|
+
cycle.current_value unless cycle.nil?
|
470
|
+
end
|
471
|
+
|
396
472
|
# Resets a cycle so that it starts from the first element the next time
|
397
473
|
# it is called. Pass in +name+ to reset a named cycle.
|
398
474
|
#
|
@@ -429,11 +505,29 @@ module ActionView
|
|
429
505
|
@index = 0
|
430
506
|
end
|
431
507
|
|
508
|
+
def current_value
|
509
|
+
@values[previous_index].to_s
|
510
|
+
end
|
511
|
+
|
432
512
|
def to_s
|
433
513
|
value = @values[@index].to_s
|
434
|
-
@index =
|
514
|
+
@index = next_index
|
435
515
|
return value
|
436
516
|
end
|
517
|
+
|
518
|
+
private
|
519
|
+
|
520
|
+
def next_index
|
521
|
+
step_index(1)
|
522
|
+
end
|
523
|
+
|
524
|
+
def previous_index
|
525
|
+
step_index(-1)
|
526
|
+
end
|
527
|
+
|
528
|
+
def step_index(n)
|
529
|
+
(@index + n) % @values.size
|
530
|
+
end
|
437
531
|
end
|
438
532
|
|
439
533
|
private
|
@@ -464,8 +558,8 @@ module ActionView
|
|
464
558
|
[-\w]+ # subdomain or domain
|
465
559
|
(?:\.[-\w]+)* # remaining subdomains or domain
|
466
560
|
(?::\d+)? # port
|
467
|
-
(?:/(?:
|
468
|
-
(?:\?[\w
|
561
|
+
(?:/(?:[~\w\+@%=\(\)-]|(?:[,.;:'][^\s$]))*)* # path
|
562
|
+
(?:\?[\w\+@%&=.;:-]+)? # query string
|
469
563
|
(?:\#[\w\-]*)? # trailing anchor
|
470
564
|
)
|
471
565
|
([[:punct:]]|<|$|) # trailing text
|
@@ -473,8 +567,8 @@ module ActionView
|
|
473
567
|
|
474
568
|
# Turns all urls into clickable links. If a block is given, each url
|
475
569
|
# is yielded and the result is used as the link text.
|
476
|
-
def auto_link_urls(text,
|
477
|
-
extra_options = tag_options(
|
570
|
+
def auto_link_urls(text, html_options = {})
|
571
|
+
extra_options = tag_options(html_options.stringify_keys) || ""
|
478
572
|
text.gsub(AUTO_LINK_RE) do
|
479
573
|
all, a, b, c, d = $&, $1, $2, $3, $4
|
480
574
|
if a =~ /<a\s/i # don't replace URL's that are already linked
|