haml 4.0.7 → 5.2.2

Sign up to get free protection for your applications and to get access to all the features.
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
-