haml 4.0.7 → 5.2.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 (124) hide show
  1. checksums.yaml +5 -5
  2. data/.github/workflows/test.yml +36 -0
  3. data/.gitignore +19 -0
  4. data/.gitmodules +3 -0
  5. data/.yardopts +2 -3
  6. data/CHANGELOG.md +146 -4
  7. data/FAQ.md +4 -14
  8. data/Gemfile +16 -0
  9. data/MIT-LICENSE +2 -2
  10. data/README.md +90 -47
  11. data/REFERENCE.md +160 -74
  12. data/Rakefile +44 -63
  13. data/TODO +24 -0
  14. data/benchmark.rb +70 -0
  15. data/haml.gemspec +45 -0
  16. data/lib/haml/.gitattributes +1 -0
  17. data/lib/haml/attribute_builder.rb +219 -0
  18. data/lib/haml/attribute_compiler.rb +237 -0
  19. data/lib/haml/attribute_parser.rb +150 -0
  20. data/lib/haml/buffer.rb +12 -175
  21. data/lib/haml/compiler.rb +110 -320
  22. data/lib/haml/engine.rb +34 -41
  23. data/lib/haml/error.rb +28 -24
  24. data/lib/haml/escapable.rb +77 -0
  25. data/lib/haml/exec.rb +38 -20
  26. data/lib/haml/filters.rb +22 -27
  27. data/lib/haml/generator.rb +42 -0
  28. data/lib/haml/helpers/action_view_extensions.rb +4 -2
  29. data/lib/haml/helpers/action_view_mods.rb +45 -60
  30. data/lib/haml/helpers/action_view_xss_mods.rb +2 -0
  31. data/lib/haml/helpers/safe_erubi_template.rb +20 -0
  32. data/lib/haml/helpers/safe_erubis_template.rb +5 -1
  33. data/lib/haml/helpers/xss_mods.rb +23 -13
  34. data/lib/haml/helpers.rb +134 -89
  35. data/lib/haml/options.rb +63 -69
  36. data/lib/haml/parser.rb +319 -227
  37. data/lib/haml/plugin.rb +54 -0
  38. data/lib/haml/railtie.rb +43 -12
  39. data/lib/haml/sass_rails_filter.rb +18 -4
  40. data/lib/haml/template/options.rb +13 -2
  41. data/lib/haml/template.rb +13 -6
  42. data/lib/haml/temple_engine.rb +124 -0
  43. data/lib/haml/temple_line_counter.rb +30 -0
  44. data/lib/haml/util.rb +83 -202
  45. data/lib/haml/version.rb +3 -1
  46. data/lib/haml.rb +2 -0
  47. data/yard/default/.gitignore +1 -0
  48. data/yard/default/fulldoc/html/css/common.sass +15 -0
  49. data/yard/default/layout/html/footer.erb +12 -0
  50. metadata +73 -115
  51. data/lib/haml/template/plugin.rb +0 -41
  52. data/test/engine_test.rb +0 -2013
  53. data/test/erb/_av_partial_1.erb +0 -12
  54. data/test/erb/_av_partial_2.erb +0 -8
  55. data/test/erb/action_view.erb +0 -62
  56. data/test/erb/standard.erb +0 -55
  57. data/test/filters_test.rb +0 -254
  58. data/test/gemfiles/Gemfile.rails-3.0.x +0 -5
  59. data/test/gemfiles/Gemfile.rails-3.1.x +0 -6
  60. data/test/gemfiles/Gemfile.rails-3.2.x +0 -5
  61. data/test/gemfiles/Gemfile.rails-4.0.x +0 -5
  62. data/test/haml-spec/LICENSE +0 -14
  63. data/test/haml-spec/README.md +0 -106
  64. data/test/haml-spec/lua_haml_spec.lua +0 -38
  65. data/test/haml-spec/perl_haml_test.pl +0 -81
  66. data/test/haml-spec/ruby_haml_test.rb +0 -23
  67. data/test/haml-spec/tests.json +0 -660
  68. data/test/helper_test.rb +0 -583
  69. data/test/markaby/standard.mab +0 -52
  70. data/test/mocks/article.rb +0 -6
  71. data/test/parser_test.rb +0 -105
  72. data/test/results/content_for_layout.xhtml +0 -12
  73. data/test/results/eval_suppressed.xhtml +0 -9
  74. data/test/results/helpers.xhtml +0 -70
  75. data/test/results/helpful.xhtml +0 -10
  76. data/test/results/just_stuff.xhtml +0 -70
  77. data/test/results/list.xhtml +0 -12
  78. data/test/results/nuke_inner_whitespace.xhtml +0 -40
  79. data/test/results/nuke_outer_whitespace.xhtml +0 -148
  80. data/test/results/original_engine.xhtml +0 -20
  81. data/test/results/partial_layout.xhtml +0 -5
  82. data/test/results/partial_layout_erb.xhtml +0 -5
  83. data/test/results/partials.xhtml +0 -21
  84. data/test/results/render_layout.xhtml +0 -3
  85. data/test/results/silent_script.xhtml +0 -74
  86. data/test/results/standard.xhtml +0 -162
  87. data/test/results/tag_parsing.xhtml +0 -23
  88. data/test/results/very_basic.xhtml +0 -5
  89. data/test/results/whitespace_handling.xhtml +0 -90
  90. data/test/template_test.rb +0 -354
  91. data/test/templates/_av_partial_1.haml +0 -9
  92. data/test/templates/_av_partial_1_ugly.haml +0 -9
  93. data/test/templates/_av_partial_2.haml +0 -5
  94. data/test/templates/_av_partial_2_ugly.haml +0 -5
  95. data/test/templates/_layout.erb +0 -3
  96. data/test/templates/_layout_for_partial.haml +0 -3
  97. data/test/templates/_partial.haml +0 -8
  98. data/test/templates/_text_area.haml +0 -3
  99. data/test/templates/_text_area_helper.html.haml +0 -4
  100. data/test/templates/action_view.haml +0 -47
  101. data/test/templates/action_view_ugly.haml +0 -47
  102. data/test/templates/breakage.haml +0 -8
  103. data/test/templates/content_for_layout.haml +0 -8
  104. data/test/templates/eval_suppressed.haml +0 -11
  105. data/test/templates/helpers.haml +0 -55
  106. data/test/templates/helpful.haml +0 -11
  107. data/test/templates/just_stuff.haml +0 -85
  108. data/test/templates/list.haml +0 -12
  109. data/test/templates/nuke_inner_whitespace.haml +0 -32
  110. data/test/templates/nuke_outer_whitespace.haml +0 -144
  111. data/test/templates/original_engine.haml +0 -17
  112. data/test/templates/partial_layout.haml +0 -3
  113. data/test/templates/partial_layout_erb.erb +0 -4
  114. data/test/templates/partialize.haml +0 -1
  115. data/test/templates/partials.haml +0 -12
  116. data/test/templates/render_layout.haml +0 -2
  117. data/test/templates/silent_script.haml +0 -45
  118. data/test/templates/standard.haml +0 -43
  119. data/test/templates/standard_ugly.haml +0 -43
  120. data/test/templates/tag_parsing.haml +0 -21
  121. data/test/templates/very_basic.haml +0 -4
  122. data/test/templates/whitespace_handling.haml +0 -87
  123. data/test/test_helper.rb +0 -81
  124. data/test/util_test.rb +0 -63
@@ -1,40 +1,41 @@
1
- module ActionView
2
- class Base
3
- def render_with_haml(*args, &block)
4
- options = args.first
5
-
6
- # If render :layout is used with a block, it concats rather than returning
7
- # a string so we need it to keep thinking it's Haml until it hits the
8
- # sub-render.
9
- if is_haml? && !(options.is_a?(Hash) && options[:layout] && block_given?)
10
- return non_haml { render_without_haml(*args, &block) }
1
+ # frozen_string_literal: true
2
+
3
+ module Haml
4
+ module Helpers
5
+ module ActionViewMods
6
+ def render(*args, &block)
7
+ options = args.first
8
+
9
+ # If render :layout is used with a block, it concats rather than returning
10
+ # a string so we need it to keep thinking it's Haml until it hits the
11
+ # sub-render.
12
+ if is_haml? && !(options.is_a?(Hash) && options[:layout] && block_given?)
13
+ return non_haml { super }
14
+ end
15
+ super
11
16
  end
12
- render_without_haml(*args, &block)
13
- end
14
- alias_method :render_without_haml, :render
15
- alias_method :render, :render_with_haml
16
17
 
17
- def output_buffer_with_haml
18
- return haml_buffer.buffer if is_haml?
19
- output_buffer_without_haml
20
- end
21
- alias_method :output_buffer_without_haml, :output_buffer
22
- alias_method :output_buffer, :output_buffer_with_haml
18
+ def output_buffer
19
+ return haml_buffer.buffer if is_haml?
20
+ super
21
+ end
23
22
 
24
- def set_output_buffer_with_haml(new_buffer)
25
- if is_haml?
26
- if Haml::Util.rails_xss_safe? && new_buffer.is_a?(ActiveSupport::SafeBuffer)
27
- new_buffer = String.new(new_buffer)
23
+ def output_buffer=(new_buffer)
24
+ if is_haml?
25
+ if Haml::Util.rails_xss_safe? && new_buffer.is_a?(ActiveSupport::SafeBuffer)
26
+ new_buffer = String.new(new_buffer)
27
+ end
28
+ haml_buffer.buffer = new_buffer
29
+ else
30
+ super
28
31
  end
29
- haml_buffer.buffer = new_buffer
30
- else
31
- set_output_buffer_without_haml new_buffer
32
32
  end
33
33
  end
34
- alias_method :set_output_buffer_without_haml, :output_buffer=
35
- alias_method :output_buffer=, :set_output_buffer_with_haml
34
+ ActionView::Base.send(:prepend, ActionViewMods)
36
35
  end
36
+ end
37
37
 
38
+ module ActionView
38
39
  module Helpers
39
40
  module CaptureHelper
40
41
  def capture_with_haml(*args, &block)
@@ -42,12 +43,7 @@ module ActionView
42
43
  #double assignment is to avoid warnings
43
44
  _hamlout = _hamlout = eval('_hamlout', block.binding) # Necessary since capture_haml checks _hamlout
44
45
 
45
- str = capture_haml(*args, &block)
46
-
47
- # NonCattingString is present in Rails less than 3.1.0. When support
48
- # for 3.0 is dropped, this can be removed.
49
- return ActionView::NonConcattingString.new(str) if str && defined?(ActionView::NonConcattingString)
50
- return str
46
+ capture_haml(*args, &block)
51
47
  else
52
48
  capture_without_haml(*args, &block)
53
49
  end
@@ -57,17 +53,23 @@ module ActionView
57
53
  end
58
54
 
59
55
  module TagHelper
56
+ DEFAULT_PRESERVE_OPTIONS = %w(textarea pre code).freeze
57
+
60
58
  def content_tag_with_haml(name, *args, &block)
61
59
  return content_tag_without_haml(name, *args, &block) unless is_haml?
62
60
 
63
- preserve = haml_buffer.options[:preserve].include?(name.to_s)
61
+ preserve = haml_buffer.options.fetch(:preserve, DEFAULT_PRESERVE_OPTIONS).include?(name.to_s)
64
62
 
65
63
  if block_given? && block_is_haml?(block) && preserve
66
- return content_tag_without_haml(name, *args) {preserve(&block)}
64
+ return content_tag_without_haml(name, *args) do
65
+ haml_buffer.fix_textareas!(Haml::Helpers.preserve(&block)).html_safe
66
+ end
67
67
  end
68
68
 
69
69
  content = content_tag_without_haml(name, *args, &block)
70
- content = Haml::Helpers.preserve(content) if preserve && content
70
+ if preserve && content
71
+ content = haml_buffer.fix_textareas!(Haml::Helpers.preserve(content)).html_safe
72
+ end
71
73
  content
72
74
  end
73
75
 
@@ -87,11 +89,9 @@ module ActionView
87
89
  end
88
90
  end
89
91
 
90
- if ActionPack::VERSION::MAJOR == 4
91
- module Tags
92
- class TextArea
93
- include HamlSupport
94
- end
92
+ module Tags
93
+ class TextArea
94
+ include HamlSupport
95
95
  end
96
96
  end
97
97
 
@@ -118,7 +118,7 @@ module ActionView
118
118
  with_tabs(1) {oldproc.call(*args)}
119
119
  end
120
120
  end
121
- res = form_tag_without_haml(url_for_options, options, *parameters_for_url, &proc) + "\n"
121
+ res = form_tag_without_haml(url_for_options, options, *parameters_for_url, &proc) << "\n"
122
122
  res << "\n" if wrap_block
123
123
  res
124
124
  else
@@ -128,20 +128,5 @@ module ActionView
128
128
  alias_method :form_tag_without_haml, :form_tag
129
129
  alias_method :form_tag, :form_tag_with_haml
130
130
  end
131
-
132
- module FormHelper
133
- def form_for_with_haml(object_name, *args, &proc)
134
- wrap_block = block_given? && is_haml? && block_is_haml?(proc)
135
- if wrap_block
136
- oldproc = proc
137
- proc = proc {|*subargs| with_tabs(1) {oldproc.call(*subargs)}}
138
- end
139
- res = form_for_without_haml(object_name, *args, &proc)
140
- res << "\n" if wrap_block
141
- res
142
- end
143
- alias_method :form_for_without_haml, :form_for
144
- alias_method :form_for, :form_for_with_haml
145
- end
146
131
  end
147
- end
132
+ end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module ActionView
2
4
  module Helpers
3
5
  module CaptureHelper
@@ -0,0 +1,20 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'action_view'
4
+
5
+ module Haml
6
+ class ErubiTemplateHandler < ActionView::Template::Handlers::ERB::Erubi
7
+
8
+ def initialize(*args, &blk)
9
+ @newline_pending = 0
10
+ super
11
+ end
12
+ end
13
+
14
+ class SafeErubiTemplate < Tilt::ErubiTemplate
15
+ def prepare
16
+ @options.merge! engine_class: Haml::ErubiTemplateHandler
17
+ super
18
+ end
19
+ end
20
+ end
@@ -1,3 +1,7 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'action_view'
4
+
1
5
  module Haml
2
6
 
3
7
  class ErubisTemplateHandler < ActionView::Template::Handlers::Erubis
@@ -26,4 +30,4 @@ module Haml
26
30
  [super, '@output_buffer.to_s'].join("\n")
27
31
  end
28
32
  end
29
- end
33
+ end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Haml
2
4
  module Helpers
3
5
  # This module overrides Haml helpers to work properly
@@ -6,12 +8,15 @@ module Haml
6
8
  # to work with Rails' XSS protection methods.
7
9
  module XssMods
8
10
  def self.included(base)
9
- %w[html_escape find_and_preserve preserve list_of surround
10
- precede succeed capture_haml haml_concat haml_indent
11
- haml_tag escape_once].each do |name|
11
+ %w[find_and_preserve preserve list_of surround
12
+ precede succeed capture_haml haml_concat haml_internal_concat haml_indent].each do |name|
12
13
  base.send(:alias_method, "#{name}_without_haml_xss", name)
13
14
  base.send(:alias_method, name, "#{name}_with_haml_xss")
14
15
  end
16
+ # Those two always have _without_haml_xss
17
+ %w[html_escape escape_once].each do |name|
18
+ base.send(:alias_method, name, "#{name}_with_haml_xss")
19
+ end
15
20
  end
16
21
 
17
22
  # Don't escape text that's already safe,
@@ -61,24 +66,29 @@ module Haml
61
66
  Haml::Util.html_safe(capture_haml_without_haml_xss(*args, &block))
62
67
  end
63
68
 
64
- # Input is escaped
69
+ # Input will be escaped unless this is in a `with_raw_haml_concat`
70
+ # block. See #Haml::Helpers::ActionViewExtensions#with_raw_haml_concat.
65
71
  def haml_concat_with_haml_xss(text = "")
66
- raw = instance_variable_defined?('@_haml_concat_raw') ? @_haml_concat_raw : false
67
- haml_concat_without_haml_xss(raw ? text : haml_xss_html_escape(text))
72
+ raw = instance_variable_defined?(:@_haml_concat_raw) ? @_haml_concat_raw : false
73
+ if raw
74
+ haml_internal_concat_raw text
75
+ else
76
+ haml_internal_concat text
77
+ end
78
+ ErrorReturn.new("haml_concat")
68
79
  end
69
80
 
81
+ # Input is escaped
82
+ def haml_internal_concat_with_haml_xss(text="", newline=true, indent=true)
83
+ haml_internal_concat_without_haml_xss(haml_xss_html_escape(text), newline, indent)
84
+ end
85
+ private :haml_internal_concat_with_haml_xss
86
+
70
87
  # Output is always HTML safe
71
88
  def haml_indent_with_haml_xss
72
89
  Haml::Util.html_safe(haml_indent_without_haml_xss)
73
90
  end
74
91
 
75
- # Input is escaped, haml_concat'ed output is always HTML safe
76
- def haml_tag_with_haml_xss(name, *rest, &block)
77
- name = haml_xss_html_escape(name.to_s)
78
- rest.unshift(haml_xss_html_escape(rest.shift.to_s)) unless [Symbol, Hash, NilClass].any? {|t| rest.first.is_a? t}
79
- with_raw_haml_concat {haml_tag_without_haml_xss(name, *rest, &block)}
80
- end
81
-
82
92
  # Output is always HTML safe
83
93
  def escape_once_with_haml_xss(*args)
84
94
  Haml::Util.html_safe(escape_once_without_haml_xss(*args))
data/lib/haml/helpers.rb CHANGED
@@ -1,3 +1,7 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'erb'
4
+
1
5
  module Haml
2
6
  # This module contains various helpful methods to make it easier to do various tasks.
3
7
  # {Haml::Helpers} is automatically included in the context
@@ -106,7 +110,8 @@ MESSAGE
106
110
  # @yield The block within which to escape newlines
107
111
  def find_and_preserve(input = nil, tags = haml_buffer.options[:preserve], &block)
108
112
  return find_and_preserve(capture_haml(&block), input || tags) if block
109
- re = /<(#{tags.map(&Regexp.method(:escape)).join('|')})([^>]*)>(.*?)(<\/\1>)/im
113
+ tags = tags.map { |tag| Regexp.escape(tag) }.join('|')
114
+ re = /<(#{tags})([^>]*)>(.*?)(<\/\1>)/im
110
115
  input.to_s.gsub(re) do |s|
111
116
  s =~ re # Can't rely on $1, etc. existing since Rails' SafeBuffer#gsub is incompatible
112
117
  "<#{$1}#{$2}>#{preserve($3)}</#{$1}>"
@@ -117,17 +122,20 @@ MESSAGE
117
122
  # HTML entities so they'll render correctly in
118
123
  # whitespace-sensitive tags without screwing up the indentation.
119
124
  #
120
- # @overload perserve(input)
125
+ # @overload preserve(input)
121
126
  # Escapes newlines within a string.
122
127
  #
123
128
  # @param input [String] The string within which to escape all newlines
124
- # @overload perserve
129
+ # @overload preserve
125
130
  # Escapes newlines within a block of Haml code.
126
131
  #
127
132
  # @yield The block within which to escape newlines
128
133
  def preserve(input = nil, &block)
129
134
  return preserve(capture_haml(&block)) if block
130
- input.to_s.chomp("\n").gsub(/\n/, '&#x000A;').gsub(/\r/, '')
135
+ s = input.to_s.chomp("\n")
136
+ s.gsub!(/\n/, '&#x000A;')
137
+ s.delete!("\r")
138
+ s
131
139
  end
132
140
  alias_method :flatten, :preserve
133
141
 
@@ -190,20 +198,19 @@ MESSAGE
190
198
  # @yield [item] A block which contains Haml code that goes within list items
191
199
  # @yieldparam item An element of `enum`
192
200
  def list_of(enum, opts={}, &block)
193
- opts_attributes = opts.empty? ? "" : " ".<<(opts.map{|k,v| "#{k}='#{v}'" }.join(" "))
194
- to_return = enum.collect do |i|
201
+ opts_attributes = opts.map { |k, v| " #{k}='#{v}'" }.join
202
+ enum.map do |i|
195
203
  result = capture_haml(i, &block)
196
204
 
197
205
  if result.count("\n") > 1
198
- result = result.gsub("\n", "\n ")
206
+ result.gsub!("\n", "\n ")
199
207
  result = "\n #{result.strip}\n"
200
208
  else
201
- result = result.strip
209
+ result.strip!
202
210
  end
203
211
 
204
212
  %Q!<li#{opts_attributes}>#{result}</li>!
205
- end
206
- to_return.join("\n")
213
+ end.join("\n")
207
214
  end
208
215
 
209
216
  # Returns a hash containing default assignments for the `xmlns`, `lang`, and `xml:lang`
@@ -219,7 +226,11 @@ MESSAGE
219
226
  # @param lang [String] The value of `xml:lang` and `lang`
220
227
  # @return [{#to_s => String}] The attribute hash
221
228
  def html_attrs(lang = 'en-US')
222
- {:xmlns => "http://www.w3.org/1999/xhtml", 'xml:lang' => lang, :lang => lang}
229
+ if haml_buffer.options[:format] == :xhtml
230
+ {:xmlns => "http://www.w3.org/1999/xhtml", 'xml:lang' => lang, :lang => lang}
231
+ else
232
+ {:lang => lang}
233
+ end
223
234
  end
224
235
 
225
236
  # Increments the number of tabs the buffer automatically adds
@@ -370,12 +381,10 @@ MESSAGE
370
381
  captured = haml_buffer.buffer.slice!(position..-1)
371
382
 
372
383
  if captured == '' and value != haml_buffer.buffer
373
- captured = (value.is_a?(String) ? value : nil)
384
+ captured = (value.is_a?(String) ? value : nil)
374
385
  end
375
386
 
376
- return nil if captured.nil?
377
- return (haml_buffer.options[:ugly] ? captured : prettify(captured))
378
-
387
+ captured
379
388
  end
380
389
  ensure
381
390
  haml_buffer.capture_position = nil
@@ -385,14 +394,34 @@ MESSAGE
385
394
  #
386
395
  # @param text [#to_s] The text to output
387
396
  def haml_concat(text = "")
388
- unless haml_buffer.options[:ugly] || haml_indent == 0
389
- haml_buffer.buffer << haml_indent <<
390
- text.to_s.gsub("\n", "\n" + haml_indent) << "\n"
397
+ haml_internal_concat text
398
+ ErrorReturn.new("haml_concat")
399
+ end
400
+
401
+ # Internal method to write directly to the buffer with control of
402
+ # whether the first line should be indented, and if there should be a
403
+ # final newline.
404
+ #
405
+ # Lines added will have the proper indentation. This can be controlled
406
+ # for the first line.
407
+ #
408
+ # Used by #haml_concat and #haml_tag.
409
+ #
410
+ # @param text [#to_s] The text to output
411
+ # @param newline [Boolean] Whether to add a newline after the text
412
+ # @param indent [Boolean] Whether to add indentation to the first line
413
+ def haml_internal_concat(text = "", newline = true, indent = true)
414
+ if haml_buffer.tabulation == 0
415
+ haml_buffer.buffer << "#{text}#{"\n" if newline}"
391
416
  else
392
- haml_buffer.buffer << text.to_s << "\n"
417
+ haml_buffer.buffer << %[#{haml_indent if indent}#{text.to_s.gsub("\n", "\n#{haml_indent}")}#{"\n" if newline}]
393
418
  end
394
- ErrorReturn.new("haml_concat")
395
419
  end
420
+ private :haml_internal_concat
421
+
422
+ # Allows writing raw content. `haml_internal_concat_raw` isn't
423
+ # effected by XSS mods. Used by #haml_tag to write the actual tags.
424
+ alias :haml_internal_concat_raw :haml_internal_concat
396
425
 
397
426
  # @return [String] The indentation string for the current line
398
427
  def haml_indent
@@ -466,14 +495,14 @@ MESSAGE
466
495
  attrs.keys.each {|key| attrs[key.to_s] = attrs.delete(key)} unless attrs.empty?
467
496
  name, attrs = merge_name_and_attributes(name.to_s, attrs)
468
497
 
469
- attributes = Haml::Compiler.build_attributes(haml_buffer.html?,
498
+ attributes = Haml::AttributeBuilder.build_attributes(haml_buffer.html?,
470
499
  haml_buffer.options[:attr_wrapper],
471
500
  haml_buffer.options[:escape_attrs],
472
501
  haml_buffer.options[:hyphenate_data_attrs],
473
502
  attrs)
474
503
 
475
504
  if text.nil? && block.nil? && (haml_buffer.options[:autoclose].include?(name) || flags.include?(:/))
476
- haml_concat "<#{name}#{attributes} />"
505
+ haml_internal_concat_raw "<#{name}#{attributes}#{' /' if haml_buffer.options[:format] == :xhtml}>"
477
506
  return ret
478
507
  end
479
508
 
@@ -483,17 +512,19 @@ MESSAGE
483
512
  end
484
513
 
485
514
  tag = "<#{name}#{attributes}>"
515
+ end_tag = "</#{name}>"
486
516
  if block.nil?
487
517
  text = text.to_s
488
518
  if text.include?("\n")
489
- haml_concat tag
519
+ haml_internal_concat_raw tag
490
520
  tab_up
491
- haml_concat text
521
+ haml_internal_concat text
492
522
  tab_down
493
- haml_concat "</#{name}>"
523
+ haml_internal_concat_raw end_tag
494
524
  else
495
- tag << text << "</#{name}>"
496
- haml_concat tag
525
+ haml_internal_concat_raw tag, false
526
+ haml_internal_concat text, false, false
527
+ haml_internal_concat_raw end_tag, true, false
497
528
  end
498
529
  return ret
499
530
  end
@@ -503,69 +534,100 @@ MESSAGE
503
534
  end
504
535
 
505
536
  if flags.include?(:<)
506
- tag << capture_haml(&block).strip << "</#{name}>"
507
- haml_concat tag
537
+ haml_internal_concat_raw tag, false
538
+ haml_internal_concat "#{capture_haml(&block).strip}", false, false
539
+ haml_internal_concat_raw end_tag, true, false
508
540
  return ret
509
541
  end
510
542
 
511
- haml_concat tag
543
+ haml_internal_concat_raw tag
512
544
  tab_up
513
545
  block.call
514
546
  tab_down
515
- haml_concat "</#{name}>"
547
+ haml_internal_concat_raw end_tag
516
548
 
517
549
  ret
518
550
  end
519
551
 
520
- # Characters that need to be escaped to HTML entities from user input
521
- HTML_ESCAPE = { '&'=>'&amp;', '<'=>'&lt;', '>'=>'&gt;', '"'=>'&quot;', "'"=>'&#039;', }
552
+ # Conditionally wrap a block in an element. If `condition` is `true` then
553
+ # this method renders the tag described by the arguments in `tag` (using
554
+ # \{#haml_tag}) with the given block inside, otherwise it just renders the block.
555
+ #
556
+ # For example,
557
+ #
558
+ # - haml_tag_if important, '.important' do
559
+ # %p
560
+ # A (possibly) important paragraph.
561
+ #
562
+ # will produce
563
+ #
564
+ # <div class='important'>
565
+ # <p>
566
+ # A (possibly) important paragraph.
567
+ # </p>
568
+ # </div>
569
+ #
570
+ # if `important` is truthy, and just
571
+ #
572
+ # <p>
573
+ # A (possibly) important paragraph.
574
+ # </p>
575
+ #
576
+ # otherwise.
577
+ #
578
+ # Like \{#haml_tag}, `haml_tag_if` outputs directly to the buffer and its
579
+ # return value should not be used. Use \{#capture_haml} if you need to use
580
+ # its results as a string.
581
+ #
582
+ # @param condition The condition to test to determine whether to render
583
+ # the enclosing tag
584
+ # @param tag Definition of the enclosing tag. See \{#haml_tag} for details
585
+ # (specifically the form that takes a block)
586
+ def haml_tag_if(condition, *tag)
587
+ if condition
588
+ haml_tag(*tag){ yield }
589
+ else
590
+ yield
591
+ end
592
+ ErrorReturn.new("haml_tag_if")
593
+ end
522
594
 
523
- HTML_ESCAPE_REGEX = /[\"><&]/
595
+ # Characters that need to be escaped to HTML entities from user input
596
+ HTML_ESCAPE = {'&' => '&amp;', '<' => '&lt;', '>' => '&gt;', '"' => '&quot;', "'" => '&#39;'}.freeze
524
597
 
525
- if RUBY_VERSION >= '1.9'
526
- # Include docs here so they are picked up by Yard
598
+ HTML_ESCAPE_REGEX = /['"><&]/
527
599
 
528
- # Returns a copy of `text` with ampersands, angle brackets and quotes
529
- # escaped into HTML entities.
530
- #
531
- # Note that if ActionView is loaded and XSS protection is enabled
532
- # (as is the default for Rails 3.0+, and optional for version 2.3.5+),
533
- # this won't escape text declared as "safe".
534
- #
535
- # @param text [String] The string to sanitize
536
- # @return [String] The sanitized string
537
- def html_escape(text)
538
- text = text.to_s
539
- text.gsub(HTML_ESCAPE_REGEX, HTML_ESCAPE)
540
- end
541
- else
542
- def html_escape(text)
543
- text = text.to_s
544
- text.gsub(HTML_ESCAPE_REGEX) {|s| HTML_ESCAPE[s]}
545
- end
600
+ # Returns a copy of `text` with ampersands, angle brackets and quotes
601
+ # escaped into HTML entities.
602
+ #
603
+ # Note that if ActionView is loaded and XSS protection is enabled
604
+ # (as is the default for Rails 3.0+, and optional for version 2.3.5+),
605
+ # this won't escape text declared as "safe".
606
+ #
607
+ # @param text [String] The string to sanitize
608
+ # @return [String] The sanitized string
609
+ def html_escape(text)
610
+ CGI.escapeHTML(text.to_s)
546
611
  end
547
612
 
548
- HTML_ESCAPE_ONCE_REGEX = /[\"><]|&(?!(?:[a-zA-Z]+|(#\d+));)/
613
+ # Always escape text regardless of html_safe?
614
+ alias_method :html_escape_without_haml_xss, :html_escape
549
615
 
550
- if RUBY_VERSION >= '1.9'
551
- # Include docs here so they are picked up by Yard
616
+ HTML_ESCAPE_ONCE_REGEX = /['"><]|&(?!(?:[a-zA-Z]+|#(?:\d+|[xX][0-9a-fA-F]+));)/
552
617
 
553
- # Escapes HTML entities in `text`, but without escaping an ampersand
554
- # that is already part of an escaped entity.
555
- #
556
- # @param text [String] The string to sanitize
557
- # @return [String] The sanitized string
558
- def escape_once(text)
559
- text = text.to_s
560
- text.gsub(HTML_ESCAPE_ONCE_REGEX, HTML_ESCAPE)
561
- end
562
- else
563
- def escape_once(text)
564
- text = text.to_s
565
- text.gsub(HTML_ESCAPE_ONCE_REGEX){|s| HTML_ESCAPE[s]}
566
- end
618
+ # Escapes HTML entities in `text`, but without escaping an ampersand
619
+ # that is already part of an escaped entity.
620
+ #
621
+ # @param text [String] The string to sanitize
622
+ # @return [String] The sanitized string
623
+ def escape_once(text)
624
+ text = text.to_s
625
+ text.gsub(HTML_ESCAPE_ONCE_REGEX, HTML_ESCAPE)
567
626
  end
568
627
 
628
+ # Always escape text once regardless of html_safe?
629
+ alias_method :escape_once_without_haml_xss, :escape_once
630
+
569
631
  # Returns whether or not the current template is a Haml template.
570
632
  #
571
633
  # This function, unlike other {Haml::Helpers} functions,
@@ -593,7 +655,7 @@ MESSAGE
593
655
  # skip merging if no ids or classes found in name
594
656
  return name, attributes_hash unless name =~ /^(.+?)?([\.#].*)$/
595
657
 
596
- return $1 || "div", Buffer.merge_attrs(
658
+ return $1 || "div", AttributeBuilder.merge_attributes!(
597
659
  Haml::Parser.parse_class_and_id($2), attributes_hash)
598
660
  end
599
661
 
@@ -630,22 +692,6 @@ MESSAGE
630
692
  _erbout = _erbout = _hamlout.buffer
631
693
  proc { |*args| proc.call(*args) }
632
694
  end
633
-
634
- def prettify(text)
635
- text = text.split(/^/)
636
- text.delete('')
637
-
638
- min_tabs = nil
639
- text.each do |line|
640
- tabs = line.index(/[^ ]/) || line.length
641
- min_tabs ||= tabs
642
- min_tabs = min_tabs > tabs ? tabs : min_tabs
643
- end
644
-
645
- text.map do |line|
646
- line.slice(min_tabs, line.length)
647
- end.join
648
- end
649
695
  end
650
696
  end
651
697
 
@@ -661,4 +707,3 @@ class Object
661
707
  false
662
708
  end
663
709
  end
664
-