hamlit 2.10.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (111) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +17 -0
  3. data/.travis.yml +47 -0
  4. data/CHANGELOG.md +702 -0
  5. data/Gemfile +30 -0
  6. data/LICENSE.txt +44 -0
  7. data/README.md +150 -0
  8. data/REFERENCE.md +272 -0
  9. data/Rakefile +117 -0
  10. data/benchmark/boolean_attribute.haml +6 -0
  11. data/benchmark/class_attribute.haml +5 -0
  12. data/benchmark/common_attribute.haml +3 -0
  13. data/benchmark/data_attribute.haml +4 -0
  14. data/benchmark/dynamic_attributes/boolean_attribute.haml +4 -0
  15. data/benchmark/dynamic_attributes/class_attribute.haml +4 -0
  16. data/benchmark/dynamic_attributes/common_attribute.haml +2 -0
  17. data/benchmark/dynamic_attributes/data_attribute.haml +2 -0
  18. data/benchmark/dynamic_attributes/id_attribute.haml +2 -0
  19. data/benchmark/dynamic_boolean_attribute.haml +4 -0
  20. data/benchmark/dynamic_merger/benchmark.rb +25 -0
  21. data/benchmark/dynamic_merger/hello.haml +50 -0
  22. data/benchmark/dynamic_merger/hello.string +50 -0
  23. data/benchmark/etc/attribute_builder.haml +5 -0
  24. data/benchmark/etc/real_sample.haml +888 -0
  25. data/benchmark/etc/real_sample.rb +11 -0
  26. data/benchmark/etc/static_analyzer.haml +1 -0
  27. data/benchmark/etc/string_interpolation.haml +2 -0
  28. data/benchmark/etc/tags.haml +3 -0
  29. data/benchmark/etc/tags_loop.haml +2 -0
  30. data/benchmark/ext/build_data.rb +17 -0
  31. data/benchmark/ext/build_id.rb +13 -0
  32. data/benchmark/id_attribute.haml +3 -0
  33. data/benchmark/plain.haml +4 -0
  34. data/benchmark/script.haml +4 -0
  35. data/benchmark/slim/LICENSE +21 -0
  36. data/benchmark/slim/context.rb +11 -0
  37. data/benchmark/slim/run-benchmarks.rb +94 -0
  38. data/benchmark/slim/view.erb +23 -0
  39. data/benchmark/slim/view.haml +18 -0
  40. data/benchmark/slim/view.slim +17 -0
  41. data/benchmark/utils/benchmark_ips_extension.rb +43 -0
  42. data/bin/bench +77 -0
  43. data/bin/console +11 -0
  44. data/bin/ruby +3 -0
  45. data/bin/setup +7 -0
  46. data/bin/stackprof +27 -0
  47. data/bin/test +24 -0
  48. data/exe/hamlit +6 -0
  49. data/ext/hamlit/extconf.rb +10 -0
  50. data/ext/hamlit/hamlit.c +555 -0
  51. data/ext/hamlit/hescape.c +108 -0
  52. data/ext/hamlit/hescape.h +20 -0
  53. data/hamlit.gemspec +47 -0
  54. data/lib/hamlit.rb +11 -0
  55. data/lib/hamlit/attribute_builder.rb +175 -0
  56. data/lib/hamlit/attribute_compiler.rb +125 -0
  57. data/lib/hamlit/attribute_parser.rb +110 -0
  58. data/lib/hamlit/cli.rb +130 -0
  59. data/lib/hamlit/compiler.rb +97 -0
  60. data/lib/hamlit/compiler/children_compiler.rb +112 -0
  61. data/lib/hamlit/compiler/comment_compiler.rb +36 -0
  62. data/lib/hamlit/compiler/doctype_compiler.rb +46 -0
  63. data/lib/hamlit/compiler/script_compiler.rb +106 -0
  64. data/lib/hamlit/compiler/silent_script_compiler.rb +24 -0
  65. data/lib/hamlit/compiler/tag_compiler.rb +76 -0
  66. data/lib/hamlit/dynamic_merger.rb +67 -0
  67. data/lib/hamlit/engine.rb +38 -0
  68. data/lib/hamlit/error.rb +15 -0
  69. data/lib/hamlit/escapable.rb +13 -0
  70. data/lib/hamlit/filters.rb +75 -0
  71. data/lib/hamlit/filters/base.rb +12 -0
  72. data/lib/hamlit/filters/cdata.rb +20 -0
  73. data/lib/hamlit/filters/coffee.rb +17 -0
  74. data/lib/hamlit/filters/css.rb +33 -0
  75. data/lib/hamlit/filters/erb.rb +10 -0
  76. data/lib/hamlit/filters/escaped.rb +22 -0
  77. data/lib/hamlit/filters/javascript.rb +33 -0
  78. data/lib/hamlit/filters/less.rb +20 -0
  79. data/lib/hamlit/filters/markdown.rb +10 -0
  80. data/lib/hamlit/filters/plain.rb +29 -0
  81. data/lib/hamlit/filters/preserve.rb +22 -0
  82. data/lib/hamlit/filters/ruby.rb +10 -0
  83. data/lib/hamlit/filters/sass.rb +15 -0
  84. data/lib/hamlit/filters/scss.rb +15 -0
  85. data/lib/hamlit/filters/text_base.rb +25 -0
  86. data/lib/hamlit/filters/tilt_base.rb +49 -0
  87. data/lib/hamlit/force_escapable.rb +29 -0
  88. data/lib/hamlit/helpers.rb +15 -0
  89. data/lib/hamlit/html.rb +14 -0
  90. data/lib/hamlit/identity.rb +13 -0
  91. data/lib/hamlit/object_ref.rb +30 -0
  92. data/lib/hamlit/parser.rb +49 -0
  93. data/lib/hamlit/parser/MIT-LICENSE +20 -0
  94. data/lib/hamlit/parser/README.md +30 -0
  95. data/lib/hamlit/parser/haml_buffer.rb +348 -0
  96. data/lib/hamlit/parser/haml_compiler.rb +553 -0
  97. data/lib/hamlit/parser/haml_error.rb +61 -0
  98. data/lib/hamlit/parser/haml_helpers.rb +727 -0
  99. data/lib/hamlit/parser/haml_options.rb +286 -0
  100. data/lib/hamlit/parser/haml_parser.rb +800 -0
  101. data/lib/hamlit/parser/haml_util.rb +288 -0
  102. data/lib/hamlit/parser/haml_xss_mods.rb +109 -0
  103. data/lib/hamlit/rails_helpers.rb +51 -0
  104. data/lib/hamlit/rails_template.rb +59 -0
  105. data/lib/hamlit/railtie.rb +10 -0
  106. data/lib/hamlit/ruby_expression.rb +32 -0
  107. data/lib/hamlit/string_splitter.rb +19 -0
  108. data/lib/hamlit/template.rb +28 -0
  109. data/lib/hamlit/utils.rb +20 -0
  110. data/lib/hamlit/version.rb +4 -0
  111. metadata +392 -0
@@ -0,0 +1,61 @@
1
+ module Hamlit
2
+ # An exception raised by Haml code.
3
+ class HamlError < StandardError
4
+
5
+ MESSAGES = {
6
+ :bad_script_indent => '"%s" is indented at wrong level: expected %d, but was at %d.',
7
+ :cant_run_filter => 'Can\'t run "%s" filter; you must require its dependencies first',
8
+ :cant_use_tabs_and_spaces => "Indentation can't use both tabs and spaces.",
9
+ :deeper_indenting => "The line was indented %d levels deeper than the previous line.",
10
+ :filter_not_defined => 'Filter "%s" is not defined.',
11
+ :gem_install_filter_deps => '"%s" filter\'s %s dependency missing: try installing it or adding it to your Gemfile',
12
+ :illegal_element => "Illegal element: classes and ids must have values.",
13
+ :illegal_nesting_content => "Illegal nesting: nesting within a tag that already has content is illegal.",
14
+ :illegal_nesting_header => "Illegal nesting: nesting within a header command is illegal.",
15
+ :illegal_nesting_line => "Illegal nesting: content can't be both given on the same line as %%%s and nested within it.",
16
+ :illegal_nesting_plain => "Illegal nesting: nesting within plain text is illegal.",
17
+ :illegal_nesting_self_closing => "Illegal nesting: nesting within a self-closing tag is illegal.",
18
+ :inconsistent_indentation => "Inconsistent indentation: %s used for indentation, but the rest of the document was indented using %s.",
19
+ :indenting_at_start => "Indenting at the beginning of the document is illegal.",
20
+ :install_haml_contrib => 'To use the "%s" filter, please install the haml-contrib gem.',
21
+ :invalid_attribute_list => 'Invalid attribute list: %s.',
22
+ :invalid_filter_name => 'Invalid filter name ":%s".',
23
+ :invalid_tag => 'Invalid tag: "%s".',
24
+ :missing_if => 'Got "%s" with no preceding "if"',
25
+ :no_ruby_code => "There's no Ruby code for %s to evaluate.",
26
+ :self_closing_content => "Self-closing tags can't have content.",
27
+ :unbalanced_brackets => 'Unbalanced brackets.',
28
+ :no_end => <<-END
29
+ You don't need to use "- end" in Haml. Un-indent to close a block:
30
+ - if foo?
31
+ %strong Foo!
32
+ - else
33
+ Not foo.
34
+ %p This line is un-indented, so it isn't part of the "if" block
35
+ END
36
+ }
37
+
38
+ def self.message(key, *args)
39
+ string = MESSAGES[key] or raise "[HAML BUG] No error messages for #{key}"
40
+ (args.empty? ? string : string % args).rstrip
41
+ end
42
+
43
+ # The line of the template on which the error occurred.
44
+ #
45
+ # @return [Fixnum]
46
+ attr_reader :line
47
+
48
+ # @param message [String] The error message
49
+ # @param line [Fixnum] See \{#line}
50
+ def initialize(message = nil, line = nil)
51
+ super(message)
52
+ @line = line
53
+ end
54
+ end
55
+
56
+ # SyntaxError is the type of exception raised when Haml encounters an
57
+ # ill-formatted document.
58
+ # It's not particularly interesting,
59
+ # except in that it's a subclass of {Haml::Error}.
60
+ class HamlSyntaxError < HamlError; end
61
+ end
@@ -0,0 +1,727 @@
1
+ require 'hamlit/parser/haml_error'
2
+ require 'hamlit/parser/haml_options'
3
+ require 'hamlit/parser/haml_compiler'
4
+ require 'hamlit/parser/haml_parser'
5
+
6
+ module Hamlit
7
+ # This module contains various helpful methods to make it easier to do various tasks.
8
+ # {Haml::Helpers} is automatically included in the context
9
+ # that a Haml template is parsed in, so all these methods are at your
10
+ # disposal from within the template.
11
+ module HamlHelpers
12
+ # An object that raises an error when \{#to\_s} is called.
13
+ # It's used to raise an error when the return value of a helper is used
14
+ # when it shouldn't be.
15
+ class ErrorReturn
16
+ def initialize(method)
17
+ @message = <<MESSAGE
18
+ #{method} outputs directly to the Haml template.
19
+ Disregard its return value and use the - operator,
20
+ or use capture_haml to get the value as a String.
21
+ MESSAGE
22
+ end
23
+
24
+ # Raises an error.
25
+ #
26
+ # @raise [Haml::Error] The error
27
+ def to_s
28
+ raise ::Hamlit::HamlError.new(@message)
29
+ rescue ::Hamlit::HamlError => e
30
+ e.backtrace.shift
31
+
32
+ # If the ErrorReturn is used directly in the template,
33
+ # we don't want Haml's stuff to get into the backtrace,
34
+ # so we get rid of the format_script line.
35
+ #
36
+ # We also have to subtract one from the Haml line number
37
+ # since the value is passed to format_script the line after
38
+ # it's actually used.
39
+ if e.backtrace.first =~ /^\(eval\):\d+:in `format_script/
40
+ e.backtrace.shift
41
+ e.backtrace.first.gsub!(/^\(haml\):(\d+)/) {|s| "(haml):#{$1.to_i - 1}"}
42
+ end
43
+ raise e
44
+ end
45
+
46
+ # @return [String] A human-readable string representation
47
+ def inspect
48
+ "::Hamlit::HamlHelpers::ErrorReturn(#{@message.inspect})"
49
+ end
50
+ end
51
+
52
+ self.extend self
53
+
54
+ @@action_view_defined = false
55
+
56
+ # @return [Boolean] Whether or not ActionView is loaded
57
+ def self.action_view?
58
+ @@action_view_defined
59
+ end
60
+
61
+ # Note: this does **not** need to be called when using Haml helpers
62
+ # normally in Rails.
63
+ #
64
+ # Initializes the current object as though it were in the same context
65
+ # as a normal ActionView instance using Haml.
66
+ # This is useful if you want to use the helpers in a context
67
+ # other than the normal setup with ActionView.
68
+ # For example:
69
+ #
70
+ # context = Object.new
71
+ # class << context
72
+ # include Haml::Helpers
73
+ # end
74
+ # context.init_haml_helpers
75
+ # context.haml_tag :p, "Stuff"
76
+ #
77
+ def init_haml_helpers
78
+ @haml_buffer = ::Hamlit::HamlBuffer.new(haml_buffer, ::Hamlit::HamlOptions.new.for_buffer)
79
+ nil
80
+ end
81
+
82
+ # Runs a block of code in a non-Haml context
83
+ # (i.e. \{#is\_haml?} will return false).
84
+ #
85
+ # This is mainly useful for rendering sub-templates such as partials in a non-Haml language,
86
+ # particularly where helpers may behave differently when run from Haml.
87
+ #
88
+ # Note that this is automatically applied to Rails partials.
89
+ #
90
+ # @yield A block which won't register as Haml
91
+ def non_haml
92
+ was_active = @haml_buffer.active?
93
+ @haml_buffer.active = false
94
+ yield
95
+ ensure
96
+ @haml_buffer.active = was_active
97
+ end
98
+
99
+ # Uses \{#preserve} to convert any newlines inside whitespace-sensitive tags
100
+ # into the HTML entities for endlines.
101
+ #
102
+ # @param tags [Array<String>] Tags that should have newlines escaped
103
+ #
104
+ # @overload find_and_preserve(input, tags = haml_buffer.options[:preserve])
105
+ # Escapes newlines within a string.
106
+ #
107
+ # @param input [String] The string within which to escape newlines
108
+ # @overload find_and_preserve(tags = haml_buffer.options[:preserve])
109
+ # Escapes newlines within a block of Haml code.
110
+ #
111
+ # @yield The block within which to escape newlines
112
+ def find_and_preserve(input = nil, tags = haml_buffer.options[:preserve], &block)
113
+ return find_and_preserve(capture_haml(&block), input || tags) if block
114
+ tags = tags.each_with_object('') do |t, s|
115
+ s << '|' unless s.empty?
116
+ s << Regexp.escape(t)
117
+ end
118
+ re = /<(#{tags})([^>]*)>(.*?)(<\/\1>)/im
119
+ input.to_s.gsub(re) do |s|
120
+ s =~ re # Can't rely on $1, etc. existing since Rails' SafeBuffer#gsub is incompatible
121
+ "<#{$1}#{$2}>#{preserve($3)}</#{$1}>"
122
+ end
123
+ end
124
+
125
+ # Takes any string, finds all the newlines, and converts them to
126
+ # HTML entities so they'll render correctly in
127
+ # whitespace-sensitive tags without screwing up the indentation.
128
+ #
129
+ # @overload preserve(input)
130
+ # Escapes newlines within a string.
131
+ #
132
+ # @param input [String] The string within which to escape all newlines
133
+ # @overload preserve
134
+ # Escapes newlines within a block of Haml code.
135
+ #
136
+ # @yield The block within which to escape newlines
137
+ def preserve(input = nil, &block)
138
+ return preserve(capture_haml(&block)) if block
139
+ s = input.to_s.chomp("\n")
140
+ s.gsub!(/\n/, '&#x000A;')
141
+ s.delete!("\r")
142
+ s
143
+ end
144
+ alias_method :flatten, :preserve
145
+
146
+ # Takes an `Enumerable` object and a block
147
+ # and iterates over the enum,
148
+ # yielding each element to a Haml block
149
+ # and putting the result into `<li>` elements.
150
+ # This creates a list of the results of the block.
151
+ # For example:
152
+ #
153
+ # = list_of([['hello'], ['yall']]) do |i|
154
+ # = i[0]
155
+ #
156
+ # Produces:
157
+ #
158
+ # <li>hello</li>
159
+ # <li>yall</li>
160
+ #
161
+ # And:
162
+ #
163
+ # = list_of({:title => 'All the stuff', :description => 'A book about all the stuff.'}) do |key, val|
164
+ # %h3= key.humanize
165
+ # %p= val
166
+ #
167
+ # Produces:
168
+ #
169
+ # <li>
170
+ # <h3>Title</h3>
171
+ # <p>All the stuff</p>
172
+ # </li>
173
+ # <li>
174
+ # <h3>Description</h3>
175
+ # <p>A book about all the stuff.</p>
176
+ # </li>
177
+ #
178
+ # While:
179
+ #
180
+ # = list_of(["Home", "About", "Contact", "FAQ"], {class: "nav", role: "nav"}) do |item|
181
+ # %a{ href="#" }= item
182
+ #
183
+ # Produces:
184
+ #
185
+ # <li class='nav' role='nav'>
186
+ # <a href='#'>Home</a>
187
+ # </li>
188
+ # <li class='nav' role='nav'>
189
+ # <a href='#'>About</a>
190
+ # </li>
191
+ # <li class='nav' role='nav'>
192
+ # <a href='#'>Contact</a>
193
+ # </li>
194
+ # <li class='nav' role='nav'>
195
+ # <a href='#'>FAQ</a>
196
+ # </li>
197
+ #
198
+ # `[[class", "nav"], [role", "nav"]]` could have been used instead of `{class: "nav", role: "nav"}` (or any enumerable collection where each pair of items responds to #to_s)
199
+ #
200
+ # @param enum [Enumerable] The list of objects to iterate over
201
+ # @param [Enumerable<#to_s,#to_s>] opts Each key/value pair will become an attribute pair for each list item element.
202
+ # @yield [item] A block which contains Haml code that goes within list items
203
+ # @yieldparam item An element of `enum`
204
+ def list_of(enum, opts={}, &block)
205
+ opts_attributes = opts.each_with_object('') {|(k, v), s| s << " #{k}='#{v}'"}
206
+ enum.each_with_object('') do |i, ret|
207
+ result = capture_haml(i, &block)
208
+
209
+ if result.count("\n") > 1
210
+ result.gsub!("\n", "\n ")
211
+ result = "\n #{result.strip!}\n"
212
+ else
213
+ result.strip!
214
+ end
215
+
216
+ ret << "\n" unless ret.empty?
217
+ ret << %Q!<li#{opts_attributes}>#{result}</li>!
218
+ end
219
+ end
220
+
221
+ # Returns a hash containing default assignments for the `xmlns`, `lang`, and `xml:lang`
222
+ # attributes of the `html` HTML element.
223
+ # For example,
224
+ #
225
+ # %html{html_attrs}
226
+ #
227
+ # becomes
228
+ #
229
+ # <html xmlns='http://www.w3.org/1999/xhtml' xml:lang='en-US' lang='en-US'>
230
+ #
231
+ # @param lang [String] The value of `xml:lang` and `lang`
232
+ # @return [{#to_s => String}] The attribute hash
233
+ def html_attrs(lang = 'en-US')
234
+ if haml_buffer.options[:format] == :xhtml
235
+ {:xmlns => "http://www.w3.org/1999/xhtml", 'xml:lang' => lang, :lang => lang}
236
+ else
237
+ {:lang => lang}
238
+ end
239
+ end
240
+
241
+ # Increments the number of tabs the buffer automatically adds
242
+ # to the lines of the template.
243
+ # For example:
244
+ #
245
+ # %h1 foo
246
+ # - tab_up
247
+ # %p bar
248
+ # - tab_down
249
+ # %strong baz
250
+ #
251
+ # Produces:
252
+ #
253
+ # <h1>foo</h1>
254
+ # <p>bar</p>
255
+ # <strong>baz</strong>
256
+ #
257
+ # @param i [Fixnum] The number of tabs by which to increase the indentation
258
+ # @see #tab_down
259
+ def tab_up(i = 1)
260
+ haml_buffer.tabulation += i
261
+ end
262
+
263
+ # Decrements the number of tabs the buffer automatically adds
264
+ # to the lines of the template.
265
+ #
266
+ # @param i [Fixnum] The number of tabs by which to decrease the indentation
267
+ # @see #tab_up
268
+ def tab_down(i = 1)
269
+ haml_buffer.tabulation -= i
270
+ end
271
+
272
+ # Sets the number of tabs the buffer automatically adds
273
+ # to the lines of the template,
274
+ # but only for the duration of the block.
275
+ # For example:
276
+ #
277
+ # %h1 foo
278
+ # - with_tabs(2) do
279
+ # %p bar
280
+ # %strong baz
281
+ #
282
+ # Produces:
283
+ #
284
+ # <h1>foo</h1>
285
+ # <p>bar</p>
286
+ # <strong>baz</strong>
287
+ #
288
+ #
289
+ # @param i [Fixnum] The number of tabs to use
290
+ # @yield A block in which the indentation will be `i` spaces
291
+ def with_tabs(i)
292
+ old_tabs = haml_buffer.tabulation
293
+ haml_buffer.tabulation = i
294
+ yield
295
+ ensure
296
+ haml_buffer.tabulation = old_tabs
297
+ end
298
+
299
+ # Surrounds a block of Haml code with strings,
300
+ # with no whitespace in between.
301
+ # For example:
302
+ #
303
+ # = surround '(', ')' do
304
+ # %a{:href => "food"} chicken
305
+ #
306
+ # Produces:
307
+ #
308
+ # (<a href='food'>chicken</a>)
309
+ #
310
+ # and
311
+ #
312
+ # = surround '*' do
313
+ # %strong angry
314
+ #
315
+ # Produces:
316
+ #
317
+ # *<strong>angry</strong>*
318
+ #
319
+ # @param front [String] The string to add before the Haml
320
+ # @param back [String] The string to add after the Haml
321
+ # @yield A block of Haml to surround
322
+ def surround(front, back = front, &block)
323
+ output = capture_haml(&block)
324
+
325
+ "#{front}#{output.chomp}#{back}\n"
326
+ end
327
+
328
+ # Prepends a string to the beginning of a Haml block,
329
+ # with no whitespace between.
330
+ # For example:
331
+ #
332
+ # = precede '*' do
333
+ # %span.small Not really
334
+ #
335
+ # Produces:
336
+ #
337
+ # *<span class='small'>Not really</span>
338
+ #
339
+ # @param str [String] The string to add before the Haml
340
+ # @yield A block of Haml to prepend to
341
+ def precede(str, &block)
342
+ "#{str}#{capture_haml(&block).chomp}\n"
343
+ end
344
+
345
+ # Appends a string to the end of a Haml block,
346
+ # with no whitespace between.
347
+ # For example:
348
+ #
349
+ # click
350
+ # = succeed '.' do
351
+ # %a{:href=>"thing"} here
352
+ #
353
+ # Produces:
354
+ #
355
+ # click
356
+ # <a href='thing'>here</a>.
357
+ #
358
+ # @param str [String] The string to add after the Haml
359
+ # @yield A block of Haml to append to
360
+ def succeed(str, &block)
361
+ "#{capture_haml(&block).chomp}#{str}\n"
362
+ end
363
+
364
+ # Captures the result of a block of Haml code,
365
+ # gets rid of the excess indentation,
366
+ # and returns it as a string.
367
+ # For example, after the following,
368
+ #
369
+ # .foo
370
+ # - foo = capture_haml(13) do |a|
371
+ # %p= a
372
+ #
373
+ # the local variable `foo` would be assigned to `"<p>13</p>\n"`.
374
+ #
375
+ # @param args [Array] Arguments to pass into the block
376
+ # @yield [args] A block of Haml code that will be converted to a string
377
+ # @yieldparam args [Array] `args`
378
+ def capture_haml(*args, &block)
379
+ buffer = eval('if defined? _hamlout then _hamlout else nil end', block.binding) || haml_buffer
380
+ with_haml_buffer(buffer) do
381
+ position = haml_buffer.buffer.length
382
+
383
+ haml_buffer.capture_position = position
384
+ value = block.call(*args)
385
+
386
+ captured = haml_buffer.buffer.slice!(position..-1)
387
+
388
+ if captured == '' and value != haml_buffer.buffer
389
+ captured = (value.is_a?(String) ? value : nil)
390
+ end
391
+
392
+ return nil if captured.nil?
393
+ return (haml_buffer.options[:ugly] ? captured : prettify(captured))
394
+ end
395
+ ensure
396
+ haml_buffer.capture_position = nil
397
+ end
398
+
399
+ # Outputs text directly to the Haml buffer, with the proper indentation.
400
+ #
401
+ # @param text [#to_s] The text to output
402
+ def haml_concat(text = "")
403
+ haml_internal_concat text
404
+ ErrorReturn.new("haml_concat")
405
+ end
406
+
407
+ # Internal method to write directly to the buffer with control of
408
+ # whether the first line should be indented, and if there should be a
409
+ # final newline.
410
+ #
411
+ # Lines added will have the proper indentation. This can be controlled
412
+ # for the first line.
413
+ #
414
+ # Used by #haml_concat and #haml_tag.
415
+ #
416
+ # @param text [#to_s] The text to output
417
+ # @param newline [Boolean] Whether to add a newline after the text
418
+ # @param indent [Boolean] Whether to add indentation to the first line
419
+ def haml_internal_concat(text = "", newline = true, indent = true)
420
+ if haml_buffer.options[:ugly] || haml_buffer.tabulation == 0
421
+ haml_buffer.buffer << "#{text}#{"\n" if newline}"
422
+ else
423
+ haml_buffer.buffer << %[#{haml_indent if indent}#{text.to_s.gsub("\n", "\n#{haml_indent}")}#{"\n" if newline}]
424
+ end
425
+ end
426
+ private :haml_internal_concat
427
+
428
+ # Allows writing raw content. `haml_internal_concat_raw` isn't
429
+ # effected by XSS mods. Used by #haml_tag to write the actual tags.
430
+ alias :haml_internal_concat_raw :haml_internal_concat
431
+
432
+ # @return [String] The indentation string for the current line
433
+ def haml_indent
434
+ ' ' * haml_buffer.tabulation
435
+ end
436
+
437
+ # Creates an HTML tag with the given name and optionally text and attributes.
438
+ # Can take a block that will run between the opening and closing tags.
439
+ # If the block is a Haml block or outputs text using \{#haml\_concat},
440
+ # the text will be properly indented.
441
+ #
442
+ # `name` can be a string using the standard Haml class/id shorthand
443
+ # (e.g. "span#foo.bar", "#foo").
444
+ # Just like standard Haml tags, these class and id values
445
+ # will be merged with manually-specified attributes.
446
+ #
447
+ # `flags` is a list of symbol flags
448
+ # like those that can be put at the end of a Haml tag
449
+ # (`:/`, `:<`, and `:>`).
450
+ # Currently, only `:/` and `:<` are supported.
451
+ #
452
+ # `haml_tag` outputs directly to the buffer;
453
+ # its return value should not be used.
454
+ # If you need to get the results as a string,
455
+ # use \{#capture\_haml\}.
456
+ #
457
+ # For example,
458
+ #
459
+ # haml_tag :table do
460
+ # haml_tag :tr do
461
+ # haml_tag 'td.cell' do
462
+ # haml_tag :strong, "strong!"
463
+ # haml_concat "data"
464
+ # end
465
+ # haml_tag :td do
466
+ # haml_concat "more_data"
467
+ # end
468
+ # end
469
+ # end
470
+ #
471
+ # outputs
472
+ #
473
+ # <table>
474
+ # <tr>
475
+ # <td class='cell'>
476
+ # <strong>
477
+ # strong!
478
+ # </strong>
479
+ # data
480
+ # </td>
481
+ # <td>
482
+ # more_data
483
+ # </td>
484
+ # </tr>
485
+ # </table>
486
+ #
487
+ # @param name [#to_s] The name of the tag
488
+ #
489
+ # @overload haml_tag(name, *rest, attributes = {})
490
+ # @yield The block of Haml code within the tag
491
+ # @overload haml_tag(name, text, *flags, attributes = {})
492
+ # @param text [#to_s] The text within the tag
493
+ # @param flags [Array<Symbol>] Haml end-of-tag flags
494
+ def haml_tag(name, *rest, &block)
495
+ ret = ErrorReturn.new("haml_tag")
496
+
497
+ text = rest.shift.to_s unless [Symbol, Hash, NilClass].any? {|t| rest.first.is_a? t}
498
+ flags = []
499
+ flags << rest.shift while rest.first.is_a? Symbol
500
+ attrs = (rest.shift || {})
501
+ attrs.keys.each {|key| attrs[key.to_s] = attrs.delete(key)} unless attrs.empty?
502
+ name, attrs = merge_name_and_attributes(name.to_s, attrs)
503
+
504
+ attributes = ::Hamlit::HamlCompiler.build_attributes(haml_buffer.html?,
505
+ haml_buffer.options[:attr_wrapper],
506
+ haml_buffer.options[:escape_attrs],
507
+ haml_buffer.options[:hyphenate_data_attrs],
508
+ attrs)
509
+
510
+ if text.nil? && block.nil? && (haml_buffer.options[:autoclose].include?(name) || flags.include?(:/))
511
+ haml_internal_concat_raw "<#{name}#{attributes}#{' /' if haml_buffer.options[:format] == :xhtml}>"
512
+ return ret
513
+ end
514
+
515
+ if flags.include?(:/)
516
+ raise ::Hamlit::HamlError.new(::Hamlit::HamlError.message(:self_closing_content)) if text
517
+ raise ::Hamlit::HamlError.new(::Hamlit::HamlError.message(:illegal_nesting_self_closing)) if block
518
+ end
519
+
520
+ tag = "<#{name}#{attributes}>"
521
+ end_tag = "</#{name}>"
522
+ if block.nil?
523
+ text = text.to_s
524
+ if text.include?("\n")
525
+ haml_internal_concat_raw tag
526
+ tab_up
527
+ haml_internal_concat text
528
+ tab_down
529
+ haml_internal_concat_raw end_tag
530
+ else
531
+ haml_internal_concat_raw tag, false
532
+ haml_internal_concat text, false, false
533
+ haml_internal_concat_raw end_tag, true, false
534
+ end
535
+ return ret
536
+ end
537
+
538
+ if text
539
+ raise ::Hamlit::HamlError.new(::Hamlit::HamlError.message(:illegal_nesting_line, name))
540
+ end
541
+
542
+ if flags.include?(:<)
543
+ haml_internal_concat_raw tag, false
544
+ haml_internal_concat "#{capture_haml(&block).strip}", false, false
545
+ haml_internal_concat_raw end_tag, true, false
546
+ return ret
547
+ end
548
+
549
+ haml_internal_concat_raw tag
550
+ tab_up
551
+ block.call
552
+ tab_down
553
+ haml_internal_concat_raw end_tag
554
+
555
+ ret
556
+ end
557
+
558
+ # Conditionally wrap a block in an element. If `condition` is `true` then
559
+ # this method renders the tag described by the arguments in `tag` (using
560
+ # \{#haml_tag}) with the given block inside, otherwise it just renders the block.
561
+ #
562
+ # For example,
563
+ #
564
+ # - haml_tag_if important, '.important' do
565
+ # %p
566
+ # A (possibly) important paragraph.
567
+ #
568
+ # will produce
569
+ #
570
+ # <div class='important'>
571
+ # <p>
572
+ # A (possibly) important paragraph.
573
+ # </p>
574
+ # </div>
575
+ #
576
+ # if `important` is truthy, and just
577
+ #
578
+ # <p>
579
+ # A (possibly) important paragraph.
580
+ # </p>
581
+ #
582
+ # otherwise.
583
+ #
584
+ # Like \{#haml_tag}, `haml_tag_if` outputs directly to the buffer and its
585
+ # return value should not be used. Use \{#capture_haml} if you need to use
586
+ # its results as a string.
587
+ #
588
+ # @param condition The condition to test to determine whether to render
589
+ # the enclosing tag
590
+ # @param tag Definition of the enclosing tag. See \{#haml_tag} for details
591
+ # (specifically the form that takes a block)
592
+ def haml_tag_if(condition, *tag)
593
+ if condition
594
+ haml_tag(*tag){ yield }
595
+ else
596
+ yield
597
+ end
598
+ ErrorReturn.new("haml_tag_if")
599
+ end
600
+
601
+ # Characters that need to be escaped to HTML entities from user input
602
+ HTML_ESCAPE = { '&' => '&amp;', '<' => '&lt;', '>' => '&gt;', '"' => '&quot;', "'" => '&#039;' }
603
+
604
+ HTML_ESCAPE_REGEX = /[\"><&]/
605
+
606
+ # Returns a copy of `text` with ampersands, angle brackets and quotes
607
+ # escaped into HTML entities.
608
+ #
609
+ # Note that if ActionView is loaded and XSS protection is enabled
610
+ # (as is the default for Rails 3.0+, and optional for version 2.3.5+),
611
+ # this won't escape text declared as "safe".
612
+ #
613
+ # @param text [String] The string to sanitize
614
+ # @return [String] The sanitized string
615
+ def html_escape(text)
616
+ text = text.to_s
617
+ text.gsub(HTML_ESCAPE_REGEX, HTML_ESCAPE)
618
+ end
619
+
620
+ HTML_ESCAPE_ONCE_REGEX = /[\"><]|&(?!(?:[a-zA-Z]+|#(?:\d+|[xX][0-9a-fA-F]+));)/
621
+
622
+ # Escapes HTML entities in `text`, but without escaping an ampersand
623
+ # that is already part of an escaped entity.
624
+ #
625
+ # @param text [String] The string to sanitize
626
+ # @return [String] The sanitized string
627
+ def escape_once(text)
628
+ text = text.to_s
629
+ text.gsub(HTML_ESCAPE_ONCE_REGEX, HTML_ESCAPE)
630
+ end
631
+
632
+ # Returns whether or not the current template is a Haml template.
633
+ #
634
+ # This function, unlike other {Haml::Helpers} functions,
635
+ # also works in other `ActionView` templates,
636
+ # where it will always return false.
637
+ #
638
+ # @return [Boolean] Whether or not the current template is a Haml template
639
+ def is_haml?
640
+ !@haml_buffer.nil? && @haml_buffer.active?
641
+ end
642
+
643
+ # Returns whether or not `block` is defined directly in a Haml template.
644
+ #
645
+ # @param block [Proc] A Ruby block
646
+ # @return [Boolean] Whether or not `block` is defined directly in a Haml template
647
+ def block_is_haml?(block)
648
+ eval('!!defined?(_hamlout)', block.binding)
649
+ end
650
+
651
+ private
652
+
653
+ # Parses the tag name used for \{#haml\_tag}
654
+ # and merges it with the Ruby attributes hash.
655
+ def merge_name_and_attributes(name, attributes_hash = {})
656
+ # skip merging if no ids or classes found in name
657
+ return name, attributes_hash unless name =~ /^(.+?)?([\.#].*)$/
658
+
659
+ return $1 || "div", ::Hamlit::HamlBuffer.merge_attrs(
660
+ ::Hamlit::HamlParser.parse_class_and_id($2), attributes_hash)
661
+ end
662
+
663
+ # Runs a block of code with the given buffer as the currently active buffer.
664
+ #
665
+ # @param buffer [Haml::Buffer] The Haml buffer to use temporarily
666
+ # @yield A block in which the given buffer should be used
667
+ def with_haml_buffer(buffer)
668
+ @haml_buffer, old_buffer = buffer, @haml_buffer
669
+ old_buffer.active, old_was_active = false, old_buffer.active? if old_buffer
670
+ @haml_buffer.active, was_active = true, @haml_buffer.active?
671
+ yield
672
+ ensure
673
+ @haml_buffer.active = was_active
674
+ old_buffer.active = old_was_active if old_buffer
675
+ @haml_buffer = old_buffer
676
+ end
677
+
678
+ # The current {Haml::Buffer} object.
679
+ #
680
+ # @return [Haml::Buffer]
681
+ def haml_buffer
682
+ @haml_buffer if defined? @haml_buffer
683
+ end
684
+
685
+ # Gives a proc the same local `_hamlout` and `_erbout` variables
686
+ # that the current template has.
687
+ #
688
+ # @param proc [#call] The proc to bind
689
+ # @return [Proc] A new proc with the new variables bound
690
+ def haml_bind_proc(&proc)
691
+ _hamlout = haml_buffer
692
+ #double assignment is to avoid warnings
693
+ _erbout = _erbout = _hamlout.buffer
694
+ proc { |*args| proc.call(*args) }
695
+ end
696
+
697
+ def prettify(text)
698
+ text = text.split(/^/)
699
+ text.delete('')
700
+
701
+ min_tabs = nil
702
+ text.each do |line|
703
+ tabs = line.index(/[^ ]/) || line.length
704
+ min_tabs ||= tabs
705
+ min_tabs = min_tabs > tabs ? tabs : min_tabs
706
+ end
707
+
708
+ text.each_with_object('') do |line, str|
709
+ str << line.slice(min_tabs, line.length)
710
+ end
711
+ end
712
+ end
713
+ end
714
+
715
+ # @private
716
+ class Object
717
+ # Haml overrides various `ActionView` helpers,
718
+ # which call an \{#is\_haml?} method
719
+ # to determine whether or not the current context object
720
+ # is a proper Haml context.
721
+ # Because `ActionView` helpers may be included in non-`ActionView::Base` classes,
722
+ # it's a good idea to define \{#is\_haml?} for all objects.
723
+ def is_haml?
724
+ false
725
+ end
726
+ alias :is_haml? :is_haml?
727
+ end