haml 5.0.4 → 5.2.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -6,16 +6,22 @@ Gem::Specification.new do |spec|
6
6
  spec.summary = "An elegant, structured (X)HTML/XML templating engine."
7
7
  spec.version = Haml::VERSION
8
8
  spec.authors = ['Natalie Weizenbaum', 'Hampton Catlin', 'Norman Clarke', 'Akira Matsuda']
9
- spec.email = ['haml@googlegroups.com', 'norman@njclarke.com']
9
+ spec.email = ['haml@googlegroups.com', 'ronnie@dio.jp']
10
10
 
11
- readmes = Dir['*'].reject{ |x| x =~ /(^|[^.a-z])[a-z]+/ || x == "TODO" }
12
11
  spec.executables = ['haml']
13
12
  spec.files = `git ls-files -z`.split("\x0").reject do |f|
14
13
  f.match(%r{\Atest/})
15
14
  end
16
15
  spec.homepage = 'http://haml.info/'
17
- spec.has_rdoc = false
18
16
  spec.license = "MIT"
17
+ spec.metadata = {
18
+ "bug_tracker_uri" => "https://github.com/haml/haml/issues",
19
+ "changelog_uri" => "https://github.com/haml/haml/blob/main/CHANGELOG.md",
20
+ "documentation_uri" => "http://haml.info/docs.html",
21
+ "homepage_uri" => "http://haml.info",
22
+ "mailing_list_uri" => "https://groups.google.com/forum/?fromgroups#!forum/haml",
23
+ "source_code_uri" => "https://github.com/haml/haml"
24
+ }
19
25
 
20
26
  spec.required_ruby_version = '>= 2.0.0'
21
27
 
@@ -26,6 +32,7 @@ Gem::Specification.new do |spec|
26
32
  spec.add_development_dependency 'rbench'
27
33
  spec.add_development_dependency 'minitest', '>= 4.0'
28
34
  spec.add_development_dependency 'nokogiri'
35
+ spec.add_development_dependency 'simplecov'
29
36
 
30
37
  spec.description = <<-END
31
38
  Haml (HTML Abstraction Markup Language) is a layer on top of HTML or XML that's
@@ -1,4 +1,5 @@
1
1
  # frozen_string_literal: true
2
+
2
3
  require 'haml/version'
3
4
 
4
5
  # The module that contains everything Haml-related:
@@ -1,4 +1,5 @@
1
1
  # frozen_string_literal: true
2
+
2
3
  module Haml
3
4
  module AttributeBuilder
4
5
  # https://html.spec.whatwg.org/multipage/syntax.html#attributes-2
@@ -35,9 +36,9 @@ module Haml
35
36
 
36
37
  value =
37
38
  if escape_attrs == :once
38
- Haml::Helpers.escape_once(value.to_s)
39
+ Haml::Helpers.escape_once_without_haml_xss(value.to_s)
39
40
  elsif escape_attrs
40
- Haml::Helpers.html_escape(value.to_s)
41
+ Haml::Helpers.html_escape_without_haml_xss(value.to_s)
41
42
  else
42
43
  value.to_s
43
44
  end
@@ -125,7 +126,7 @@ module Haml
125
126
  elsif key == 'class'
126
127
  merged_class = filter_and_join(from, ' ')
127
128
  if to && merged_class
128
- merged_class = (merged_class.split(' ') | to.split(' ')).sort.join(' ')
129
+ merged_class = (to.split(' ') | merged_class.split(' ')).join(' ')
129
130
  elsif to || merged_class
130
131
  merged_class ||= to
131
132
  end
@@ -1,4 +1,5 @@
1
1
  # frozen_string_literal: true
2
+
2
3
  require 'haml/attribute_parser'
3
4
 
4
5
  module Haml
@@ -6,27 +7,7 @@ module Haml
6
7
  # @param type [Symbol] :static or :dynamic
7
8
  # @param key [String]
8
9
  # @param value [String] Actual string value for :static type, value's Ruby literal for :dynamic type.
9
- class AttributeValue < Struct.new(:type, :key, :value)
10
- # @return [String] A Ruby literal of value.
11
- def to_literal
12
- case type
13
- when :static
14
- Haml::Util.inspect_obj(value)
15
- when :dynamic
16
- value
17
- end
18
- end
19
- end
20
-
21
- # Returns a script to render attributes on runtime.
22
- #
23
- # @param attributes [Hash]
24
- # @param object_ref [String,:nil]
25
- # @param dynamic_attributes [DynamicAttributes]
26
- # @return [String] Attributes rendering code
27
- def self.runtime_build(attributes, object_ref, dynamic_attributes)
28
- "_hamlout.attributes(#{Haml::Util.inspect_obj(attributes)}, #{object_ref},#{dynamic_attributes.to_literal})"
29
- end
10
+ AttributeValue = Struct.new(:type, :key, :value)
30
11
 
31
12
  # @param options [Haml::Options]
32
13
  def initialize(options)
@@ -40,16 +21,16 @@ module Haml
40
21
  #
41
22
  # @param attributes [Hash]
42
23
  # @param object_ref [String,:nil]
43
- # @param dynamic_attributes [DynamicAttributes]
24
+ # @param dynamic_attributes [Haml::Parser::DynamicAttributes]
44
25
  # @return [Array] Temple expression
45
26
  def compile(attributes, object_ref, dynamic_attributes)
46
27
  if object_ref != :nil || !AttributeParser.available?
47
- return [:dynamic, AttributeCompiler.runtime_build(attributes, object_ref, dynamic_attributes)]
28
+ return [:dynamic, compile_runtime_build(attributes, object_ref, dynamic_attributes)]
48
29
  end
49
30
 
50
31
  parsed_hashes = [dynamic_attributes.new, dynamic_attributes.old].compact.map do |attribute_hash|
51
32
  unless (hash = AttributeParser.parse(attribute_hash))
52
- return [:dynamic, AttributeCompiler.runtime_build(attributes, object_ref, dynamic_attributes)]
33
+ return [:dynamic, compile_runtime_build(attributes, object_ref, dynamic_attributes)]
53
34
  end
54
35
  hash
55
36
  end
@@ -63,6 +44,16 @@ module Haml
63
44
 
64
45
  private
65
46
 
47
+ # Returns a script to render attributes on runtime.
48
+ #
49
+ # @param attributes [Hash]
50
+ # @param object_ref [String,:nil]
51
+ # @param dynamic_attributes [Haml::Parser::DynamicAttributes]
52
+ # @return [String] Attributes rendering code
53
+ def compile_runtime_build(attributes, object_ref, dynamic_attributes)
54
+ "_hamlout.attributes(#{to_literal(attributes)}, #{object_ref}, #{dynamic_attributes.to_literal})"
55
+ end
56
+
66
57
  # Build array of grouped values whose sort order may go back and forth, which is also sorted with key name.
67
58
  # This method needs to group values with the same start because it can be changed in `Haml::AttributeBuidler#build_data_keys`.
68
59
  # @param values [Array<Haml::AttributeCompiler::AttributeValue>]
@@ -129,7 +120,7 @@ module Haml
129
120
 
130
121
  arguments = [@is_html, @attr_wrapper, @escape_attrs, @hyphenate_data_attrs]
131
122
  code = "::Haml::AttributeBuilder.build_attributes"\
132
- "(#{arguments.map { |a| Haml::Util.inspect_obj(a) }.join(', ')}, { #{hash_content} })"
123
+ "(#{arguments.map(&method(:to_literal)).join(', ')}, { #{hash_content} })"
133
124
  [:static, eval(code).to_s]
134
125
  end
135
126
 
@@ -138,16 +129,16 @@ module Haml
138
129
  # @return [String]
139
130
  def merged_value(key, values)
140
131
  if values.size == 1
141
- values.first.to_literal
132
+ attr_literal(values.first)
142
133
  else
143
- "::Haml::AttributeBuilder.merge_values(#{frozen_string(key)}, #{values.map(&:to_literal).join(', ')})"
134
+ "::Haml::AttributeBuilder.merge_values(#{frozen_string(key)}, #{values.map(&method(:attr_literal)).join(', ')})"
144
135
  end
145
136
  end
146
137
 
147
138
  # @param str [String]
148
139
  # @return [String]
149
140
  def frozen_string(str)
150
- "#{Haml::Util.inspect_obj(str)}.freeze"
141
+ "#{to_literal(str)}.freeze"
151
142
  end
152
143
 
153
144
  # Compiles attribute values for one key to Temple expression that generates ` key='value'`.
@@ -156,7 +147,7 @@ module Haml
156
147
  # @param values [Array<AttributeValue>]
157
148
  # @return [Array] Temple expression
158
149
  def compile_attribute(key, values)
159
- if values.all? { |v| Temple::StaticAnalyzer.static?(v.to_literal) }
150
+ if values.all? { |v| Temple::StaticAnalyzer.static?(attr_literal(v)) }
160
151
  return static_build(values)
161
152
  end
162
153
 
@@ -180,7 +171,7 @@ module Haml
180
171
  ['false, nil', [:multi]],
181
172
  [:else, [:multi,
182
173
  [:static, " #{id_or_class}=#{@attr_wrapper}"],
183
- [:escape, @escape_attrs, [:dynamic, var]],
174
+ [:escape, Escapable::EscapeSafeBuffer.new(@escape_attrs), [:dynamic, var]],
184
175
  [:static, @attr_wrapper]],
185
176
  ]
186
177
  ],
@@ -200,7 +191,7 @@ module Haml
200
191
  ['false, nil', [:multi]],
201
192
  [:else, [:multi,
202
193
  [:static, " #{key}=#{@attr_wrapper}"],
203
- [:escape, @escape_attrs, [:dynamic, var]],
194
+ [:escape, Escapable::EscapeSafeBuffer.new(@escape_attrs), [:dynamic, var]],
204
195
  [:static, @attr_wrapper]],
205
196
  ]
206
197
  ],
@@ -219,5 +210,26 @@ module Haml
219
210
  @unique_name ||= 0
220
211
  "_haml_attribute_compiler#{@unique_name += 1}"
221
212
  end
213
+
214
+ # @param [Haml::AttributeCompiler::AttributeValue] attr
215
+ def attr_literal(attr)
216
+ case attr.type
217
+ when :static
218
+ to_literal(attr.value)
219
+ when :dynamic
220
+ attr.value
221
+ end
222
+ end
223
+
224
+ # For haml/haml#972
225
+ # @param [Object] value
226
+ def to_literal(value)
227
+ case value
228
+ when true, false
229
+ value.to_s
230
+ else
231
+ Haml::Util.inspect_obj(value)
232
+ end
233
+ end
222
234
  end
223
235
  end
@@ -1,8 +1,10 @@
1
1
  # frozen_string_literal: true
2
+
2
3
  begin
3
4
  require 'ripper'
4
5
  rescue LoadError
5
6
  end
7
+ require 'temple/static_analyzer'
6
8
 
7
9
  module Haml
8
10
  # Haml::AttriubuteParser parses Hash literal to { String (key name) => String (value literal) }.
@@ -14,7 +16,7 @@ module Haml
14
16
  TYPE = 1
15
17
  TEXT = 2
16
18
 
17
- IGNORED_TYPES = %i[on_sp on_ignored_nl]
19
+ IGNORED_TYPES = %i[on_sp on_ignored_nl].freeze
18
20
 
19
21
  class << self
20
22
  # @return [Boolean] - return true if AttributeParser.parse can be used.
@@ -1,4 +1,5 @@
1
1
  # frozen_string_literal: true
2
+
2
3
  module Haml
3
4
  # This class is used only internally. It holds the buffer of HTML that
4
5
  # is eventually output as the resulting document.
@@ -132,7 +133,9 @@ module Haml
132
133
  def attributes(class_id, obj_ref, *attributes_hashes)
133
134
  attributes = class_id
134
135
  attributes_hashes.each do |old|
135
- AttributeBuilder.merge_attributes!(attributes, Hash[old.map {|k, v| [k.to_s, v]}])
136
+ result = {}
137
+ old.each { |k, v| result[k.to_s] = v }
138
+ AttributeBuilder.merge_attributes!(attributes, result)
136
139
  end
137
140
  AttributeBuilder.merge_attributes!(attributes, parse_object_ref(obj_ref)) if obj_ref
138
141
  AttributeBuilder.build_attributes(
@@ -1,4 +1,5 @@
1
- # frozen_string_literal: false
1
+ # frozen_string_literal: true
2
+
2
3
  require 'haml/attribute_builder'
3
4
  require 'haml/attribute_compiler'
4
5
  require 'haml/temple_line_counter'
@@ -167,7 +168,7 @@ module Haml
167
168
  end
168
169
 
169
170
  def compile_filter
170
- unless filter = @filters[@node.value[:name]]
171
+ unless (filter = @filters[@node.value[:name]])
171
172
  name = @node.value[:name]
172
173
  if ["maruku", "textile"].include?(name)
173
174
  raise Error.new(Error.message(:install_haml_contrib, name), @node.line - 1)
@@ -187,30 +188,28 @@ module Haml
187
188
 
188
189
  if @options.html5?
189
190
  '<!DOCTYPE html>'
190
- else
191
- if @options.xhtml?
192
- if @node.value[:version] == "1.1"
193
- '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">'
194
- elsif @node.value[:version] == "5"
195
- '<!DOCTYPE html>'
196
- else
197
- case @node.value[:type]
198
- when "strict"; '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">'
199
- when "frameset"; '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Frameset//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-frameset.dtd">'
200
- when "mobile"; '<!DOCTYPE html PUBLIC "-//WAPFORUM//DTD XHTML Mobile 1.2//EN" "http://www.openmobilealliance.org/tech/DTD/xhtml-mobile12.dtd">'
201
- when "rdfa"; '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML+RDFa 1.0//EN" "http://www.w3.org/MarkUp/DTD/xhtml-rdfa-1.dtd">'
202
- when "basic"; '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML Basic 1.1//EN" "http://www.w3.org/TR/xhtml-basic/xhtml-basic11.dtd">'
203
- else '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">'
204
- end
205
- end
206
-
207
- elsif @options.html4?
191
+ elsif @options.xhtml?
192
+ if @node.value[:version] == "1.1"
193
+ '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">'
194
+ elsif @node.value[:version] == "5"
195
+ '<!DOCTYPE html>'
196
+ else
208
197
  case @node.value[:type]
209
- when "strict"; '<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">'
210
- when "frameset"; '<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Frameset//EN" "http://www.w3.org/TR/html4/frameset.dtd">'
211
- else '<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">'
198
+ when "strict"; '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">'
199
+ when "frameset"; '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Frameset//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-frameset.dtd">'
200
+ when "mobile"; '<!DOCTYPE html PUBLIC "-//WAPFORUM//DTD XHTML Mobile 1.2//EN" "http://www.openmobilealliance.org/tech/DTD/xhtml-mobile12.dtd">'
201
+ when "rdfa"; '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML+RDFa 1.0//EN" "http://www.w3.org/MarkUp/DTD/xhtml-rdfa-1.dtd">'
202
+ when "basic"; '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML Basic 1.1//EN" "http://www.w3.org/TR/xhtml-basic/xhtml-basic11.dtd">'
203
+ else '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">'
212
204
  end
213
205
  end
206
+
207
+ elsif @options.html4?
208
+ case @node.value[:type]
209
+ when "strict"; '<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">'
210
+ when "frameset"; '<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Frameset//EN" "http://www.w3.org/TR/html4/frameset.dtd">'
211
+ else '<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">'
212
+ end
214
213
  end
215
214
  end
216
215
 
@@ -315,7 +314,7 @@ module Haml
315
314
 
316
315
  case last.first
317
316
  when :text
318
- last[1].rstrip!
317
+ last[1] = last[1].rstrip
319
318
  if last[1].empty?
320
319
  @to_merge.slice! index
321
320
  rstrip_buffer! index
@@ -1,4 +1,5 @@
1
- # frozen_string_literal: false
1
+ # frozen_string_literal: true
2
+
2
3
  require 'forwardable'
3
4
 
4
5
  require 'haml/parser'
@@ -51,6 +52,9 @@ module Haml
51
52
  # see {file:REFERENCE.md#options the Haml options documentation}
52
53
  # @raise [Haml::Error] if there's a Haml syntax error in the template
53
54
  def initialize(template, options = {})
55
+ # Reflect changes of `Haml::Options.defaults` to `Haml::TempleEngine` options, but `#initialize_encoding`
56
+ # should be run against the arguemnt `options[:encoding]` for backward compatibility with old `Haml::Engine`.
57
+ options = Options.defaults.dup.tap { |o| o.delete(:encoding) }.merge!(options)
54
58
  @options = Options.new(options)
55
59
 
56
60
  @template = check_haml_encoding(template) do |msg, line|
@@ -166,8 +170,13 @@ module Haml
166
170
  end
167
171
 
168
172
  begin
169
- eval("Proc.new { |*_haml_locals| _haml_locals = _haml_locals[0] || {};" <<
170
- @temple_engine.precompiled_with_ambles(local_names) << "}\n", scope, @options.filename, @options.line)
173
+ str = @temple_engine.precompiled_with_ambles(local_names)
174
+ eval(
175
+ "Proc.new { |*_haml_locals| _haml_locals = _haml_locals[0] || {}; #{str}}\n",
176
+ scope,
177
+ @options.filename,
178
+ @options.line
179
+ )
171
180
  rescue ::SyntaxError => e
172
181
  raise SyntaxError, e.message
173
182
  end
@@ -1,32 +1,33 @@
1
1
  # frozen_string_literal: true
2
+
2
3
  module Haml
3
4
  # An exception raised by Haml code.
4
5
  class Error < StandardError
5
6
 
6
7
  MESSAGES = {
7
- :bad_script_indent => '"%s" is indented at wrong level: expected %d, but was at %d.',
8
- :cant_run_filter => 'Can\'t run "%s" filter; you must require its dependencies first',
9
- :cant_use_tabs_and_spaces => "Indentation can't use both tabs and spaces.",
10
- :deeper_indenting => "The line was indented %d levels deeper than the previous line.",
11
- :filter_not_defined => 'Filter "%s" is not defined.',
12
- :gem_install_filter_deps => '"%s" filter\'s %s dependency missing: try installing it or adding it to your Gemfile',
13
- :illegal_element => "Illegal element: classes and ids must have values.",
14
- :illegal_nesting_content => "Illegal nesting: nesting within a tag that already has content is illegal.",
15
- :illegal_nesting_header => "Illegal nesting: nesting within a header command is illegal.",
16
- :illegal_nesting_line => "Illegal nesting: content can't be both given on the same line as %%%s and nested within it.",
17
- :illegal_nesting_plain => "Illegal nesting: nesting within plain text is illegal.",
18
- :illegal_nesting_self_closing => "Illegal nesting: nesting within a self-closing tag is illegal.",
19
- :inconsistent_indentation => "Inconsistent indentation: %s used for indentation, but the rest of the document was indented using %s.",
20
- :indenting_at_start => "Indenting at the beginning of the document is illegal.",
21
- :install_haml_contrib => 'To use the "%s" filter, please install the haml-contrib gem.',
22
- :invalid_attribute_list => 'Invalid attribute list: %s.',
23
- :invalid_filter_name => 'Invalid filter name ":%s".',
24
- :invalid_tag => 'Invalid tag: "%s".',
25
- :missing_if => 'Got "%s" with no preceding "if"',
26
- :no_ruby_code => "There's no Ruby code for %s to evaluate.",
27
- :self_closing_content => "Self-closing tags can't have content.",
28
- :unbalanced_brackets => 'Unbalanced brackets.',
29
- :no_end => <<-END
8
+ bad_script_indent: '"%s" is indented at wrong level: expected %d, but was at %d.',
9
+ cant_run_filter: 'Can\'t run "%s" filter; you must require its dependencies first',
10
+ cant_use_tabs_and_spaces: "Indentation can't use both tabs and spaces.",
11
+ deeper_indenting: "The line was indented %d levels deeper than the previous line.",
12
+ filter_not_defined: 'Filter "%s" is not defined.',
13
+ gem_install_filter_deps: '"%s" filter\'s %s dependency missing: try installing it or adding it to your Gemfile',
14
+ illegal_element: "Illegal element: classes and ids must have values.",
15
+ illegal_nesting_content: "Illegal nesting: nesting within a tag that already has content is illegal.",
16
+ illegal_nesting_header: "Illegal nesting: nesting within a header command is illegal.",
17
+ illegal_nesting_line: "Illegal nesting: content can't be both given on the same line as %%%s and nested within it.",
18
+ illegal_nesting_plain: "Illegal nesting: nesting within plain text is illegal.",
19
+ illegal_nesting_self_closing: "Illegal nesting: nesting within a self-closing tag is illegal.",
20
+ inconsistent_indentation: "Inconsistent indentation: %s used for indentation, but the rest of the document was indented using %s.",
21
+ indenting_at_start: "Indenting at the beginning of the document is illegal.",
22
+ install_haml_contrib: 'To use the "%s" filter, please install the haml-contrib gem.',
23
+ invalid_attribute_list: 'Invalid attribute list: %s.',
24
+ invalid_filter_name: 'Invalid filter name ":%s".',
25
+ invalid_tag: 'Invalid tag: "%s".',
26
+ missing_if: 'Got "%s" with no preceding "if"',
27
+ no_ruby_code: "There's no Ruby code for %s to evaluate.",
28
+ self_closing_content: "Self-closing tags can't have content.",
29
+ unbalanced_brackets: 'Unbalanced brackets.',
30
+ no_end: <<-END
30
31
  You don't need to use "- end" in Haml. Un-indent to close a block:
31
32
  - if foo?
32
33
  %strong Foo!
@@ -34,7 +35,7 @@ You don't need to use "- end" in Haml. Un-indent to close a block:
34
35
  Not foo.
35
36
  %p This line is un-indented, so it isn't part of the "if" block
36
37
  END
37
- }
38
+ }.freeze
38
39
 
39
40
  def self.message(key, *args)
40
41
  string = MESSAGES[key] or raise "[HAML BUG] No error messages for #{key}"
@@ -1,32 +1,34 @@
1
1
  # frozen_string_literal: true
2
+
2
3
  module Haml
3
4
  # Like Temple::Filters::Escapable, but with support for escaping by
4
5
  # Haml::Herlpers.html_escape and Haml::Herlpers.escape_once.
5
6
  class Escapable < Temple::Filter
7
+ # Special value of `flag` to ignore html_safe?
8
+ EscapeSafeBuffer = Struct.new(:value)
9
+
6
10
  def initialize(*)
7
11
  super
8
- @escape_code = "::Haml::Helpers.html_escape((%s))"
9
- @escaper = eval("proc {|v| #{@escape_code % 'v'} }")
10
- @once_escape_code = "::Haml::Helpers.escape_once((%s))"
11
- @once_escaper = eval("proc {|v| #{@once_escape_code % 'v'} }")
12
12
  @escape = false
13
+ @escape_safe_buffer = false
13
14
  end
14
15
 
15
16
  def on_escape(flag, exp)
16
- old = @escape
17
- @escape = flag
17
+ old_escape, old_escape_safe_buffer = @escape, @escape_safe_buffer
18
+ @escape_safe_buffer = flag.is_a?(EscapeSafeBuffer)
19
+ @escape = @escape_safe_buffer ? flag.value : flag
18
20
  compile(exp)
19
21
  ensure
20
- @escape = old
22
+ @escape, @escape_safe_buffer = old_escape, old_escape_safe_buffer
21
23
  end
22
24
 
23
25
  # The same as Haml::AttributeBuilder.build_attributes
24
26
  def on_static(value)
25
27
  [:static,
26
28
  if @escape == :once
27
- @once_escaper[value]
29
+ escape_once(value)
28
30
  elsif @escape
29
- @escaper[value]
31
+ escape(value)
30
32
  else
31
33
  value
32
34
  end
@@ -37,13 +39,39 @@ module Haml
37
39
  def on_dynamic(value)
38
40
  [:dynamic,
39
41
  if @escape == :once
40
- @once_escape_code % value
42
+ escape_once_code(value)
41
43
  elsif @escape
42
- @escape_code % value
44
+ escape_code(value)
43
45
  else
44
46
  "(#{value}).to_s"
45
47
  end
46
48
  ]
47
49
  end
50
+
51
+ private
52
+
53
+ def escape_once(value)
54
+ if @escape_safe_buffer
55
+ ::Haml::Helpers.escape_once_without_haml_xss(value)
56
+ else
57
+ ::Haml::Helpers.escape_once(value)
58
+ end
59
+ end
60
+
61
+ def escape(value)
62
+ if @escape_safe_buffer
63
+ ::Haml::Helpers.html_escape_without_haml_xss(value)
64
+ else
65
+ ::Haml::Helpers.html_escape(value)
66
+ end
67
+ end
68
+
69
+ def escape_once_code(value)
70
+ "::Haml::Helpers.escape_once#{('_without_haml_xss' if @escape_safe_buffer)}((#{value}))"
71
+ end
72
+
73
+ def escape_code(value)
74
+ "::Haml::Helpers.html_escape#{('_without_haml_xss' if @escape_safe_buffer)}((#{value}))"
75
+ end
48
76
  end
49
77
  end