haml-edge 2.1.1

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 (180) hide show
  1. data/EDGE_GEM_VERSION +1 -0
  2. data/FAQ +138 -0
  3. data/MIT-LICENSE +20 -0
  4. data/README.rdoc +332 -0
  5. data/REVISION +1 -0
  6. data/Rakefile +226 -0
  7. data/VERSION +1 -0
  8. data/bin/css2sass +7 -0
  9. data/bin/haml +9 -0
  10. data/bin/html2haml +7 -0
  11. data/bin/sass +8 -0
  12. data/extra/edge_gem_watch.rb +13 -0
  13. data/extra/haml-mode.el +596 -0
  14. data/extra/sass-mode.el +200 -0
  15. data/init.rb +8 -0
  16. data/lib/haml/buffer.rb +255 -0
  17. data/lib/haml/engine.rb +268 -0
  18. data/lib/haml/error.rb +22 -0
  19. data/lib/haml/exec.rb +395 -0
  20. data/lib/haml/filters.rb +275 -0
  21. data/lib/haml/helpers/action_view_extensions.rb +45 -0
  22. data/lib/haml/helpers/action_view_mods.rb +181 -0
  23. data/lib/haml/helpers.rb +488 -0
  24. data/lib/haml/html.rb +222 -0
  25. data/lib/haml/precompiler.rb +904 -0
  26. data/lib/haml/shared.rb +45 -0
  27. data/lib/haml/template/patch.rb +58 -0
  28. data/lib/haml/template/plugin.rb +72 -0
  29. data/lib/haml/template.rb +42 -0
  30. data/lib/haml/util.rb +88 -0
  31. data/lib/haml/version.rb +47 -0
  32. data/lib/haml.rb +1044 -0
  33. data/lib/sass/css.rb +388 -0
  34. data/lib/sass/engine.rb +495 -0
  35. data/lib/sass/environment.rb +46 -0
  36. data/lib/sass/error.rb +35 -0
  37. data/lib/sass/plugin/merb.rb +56 -0
  38. data/lib/sass/plugin/rails.rb +24 -0
  39. data/lib/sass/plugin.rb +204 -0
  40. data/lib/sass/repl.rb +51 -0
  41. data/lib/sass/script/bool.rb +13 -0
  42. data/lib/sass/script/color.rb +97 -0
  43. data/lib/sass/script/funcall.rb +29 -0
  44. data/lib/sass/script/functions.rb +134 -0
  45. data/lib/sass/script/lexer.rb +148 -0
  46. data/lib/sass/script/literal.rb +82 -0
  47. data/lib/sass/script/number.rb +231 -0
  48. data/lib/sass/script/operation.rb +30 -0
  49. data/lib/sass/script/parser.rb +142 -0
  50. data/lib/sass/script/string.rb +9 -0
  51. data/lib/sass/script/unary_operation.rb +21 -0
  52. data/lib/sass/script/variable.rb +20 -0
  53. data/lib/sass/script.rb +38 -0
  54. data/lib/sass/tree/attr_node.rb +64 -0
  55. data/lib/sass/tree/comment_node.rb +30 -0
  56. data/lib/sass/tree/debug_node.rb +22 -0
  57. data/lib/sass/tree/directive_node.rb +50 -0
  58. data/lib/sass/tree/file_node.rb +27 -0
  59. data/lib/sass/tree/for_node.rb +29 -0
  60. data/lib/sass/tree/if_node.rb +27 -0
  61. data/lib/sass/tree/mixin_def_node.rb +18 -0
  62. data/lib/sass/tree/mixin_node.rb +35 -0
  63. data/lib/sass/tree/node.rb +99 -0
  64. data/lib/sass/tree/rule_node.rb +161 -0
  65. data/lib/sass/tree/variable_node.rb +24 -0
  66. data/lib/sass/tree/while_node.rb +21 -0
  67. data/lib/sass.rb +1062 -0
  68. data/rails/init.rb +1 -0
  69. data/test/benchmark.rb +99 -0
  70. data/test/haml/engine_test.rb +795 -0
  71. data/test/haml/helper_test.rb +228 -0
  72. data/test/haml/html2haml_test.rb +108 -0
  73. data/test/haml/markaby/standard.mab +52 -0
  74. data/test/haml/mocks/article.rb +6 -0
  75. data/test/haml/results/content_for_layout.xhtml +15 -0
  76. data/test/haml/results/eval_suppressed.xhtml +9 -0
  77. data/test/haml/results/filters.xhtml +62 -0
  78. data/test/haml/results/helpers.xhtml +93 -0
  79. data/test/haml/results/helpful.xhtml +10 -0
  80. data/test/haml/results/just_stuff.xhtml +68 -0
  81. data/test/haml/results/list.xhtml +12 -0
  82. data/test/haml/results/nuke_inner_whitespace.xhtml +40 -0
  83. data/test/haml/results/nuke_outer_whitespace.xhtml +148 -0
  84. data/test/haml/results/original_engine.xhtml +20 -0
  85. data/test/haml/results/partial_layout.xhtml +5 -0
  86. data/test/haml/results/partials.xhtml +21 -0
  87. data/test/haml/results/render_layout.xhtml +3 -0
  88. data/test/haml/results/silent_script.xhtml +74 -0
  89. data/test/haml/results/standard.xhtml +162 -0
  90. data/test/haml/results/tag_parsing.xhtml +23 -0
  91. data/test/haml/results/very_basic.xhtml +5 -0
  92. data/test/haml/results/whitespace_handling.xhtml +89 -0
  93. data/test/haml/rhtml/_av_partial_1.rhtml +12 -0
  94. data/test/haml/rhtml/_av_partial_2.rhtml +8 -0
  95. data/test/haml/rhtml/action_view.rhtml +62 -0
  96. data/test/haml/rhtml/standard.rhtml +54 -0
  97. data/test/haml/template_test.rb +204 -0
  98. data/test/haml/templates/_av_partial_1.haml +9 -0
  99. data/test/haml/templates/_av_partial_1_ugly.haml +9 -0
  100. data/test/haml/templates/_av_partial_2.haml +5 -0
  101. data/test/haml/templates/_av_partial_2_ugly.haml +5 -0
  102. data/test/haml/templates/_layout.erb +3 -0
  103. data/test/haml/templates/_layout_for_partial.haml +3 -0
  104. data/test/haml/templates/_partial.haml +8 -0
  105. data/test/haml/templates/_text_area.haml +3 -0
  106. data/test/haml/templates/action_view.haml +47 -0
  107. data/test/haml/templates/action_view_ugly.haml +47 -0
  108. data/test/haml/templates/breakage.haml +8 -0
  109. data/test/haml/templates/content_for_layout.haml +10 -0
  110. data/test/haml/templates/eval_suppressed.haml +11 -0
  111. data/test/haml/templates/filters.haml +66 -0
  112. data/test/haml/templates/helpers.haml +95 -0
  113. data/test/haml/templates/helpful.haml +11 -0
  114. data/test/haml/templates/just_stuff.haml +83 -0
  115. data/test/haml/templates/list.haml +12 -0
  116. data/test/haml/templates/nuke_inner_whitespace.haml +32 -0
  117. data/test/haml/templates/nuke_outer_whitespace.haml +144 -0
  118. data/test/haml/templates/original_engine.haml +17 -0
  119. data/test/haml/templates/partial_layout.haml +3 -0
  120. data/test/haml/templates/partialize.haml +1 -0
  121. data/test/haml/templates/partials.haml +12 -0
  122. data/test/haml/templates/render_layout.haml +2 -0
  123. data/test/haml/templates/silent_script.haml +40 -0
  124. data/test/haml/templates/standard.haml +42 -0
  125. data/test/haml/templates/standard_ugly.haml +42 -0
  126. data/test/haml/templates/tag_parsing.haml +21 -0
  127. data/test/haml/templates/very_basic.haml +4 -0
  128. data/test/haml/templates/whitespace_handling.haml +87 -0
  129. data/test/haml/util_test.rb +87 -0
  130. data/test/linked_rails.rb +12 -0
  131. data/test/sass/css2sass_test.rb +193 -0
  132. data/test/sass/engine_test.rb +709 -0
  133. data/test/sass/functions_test.rb +109 -0
  134. data/test/sass/more_results/more1.css +9 -0
  135. data/test/sass/more_results/more1_with_line_comments.css +26 -0
  136. data/test/sass/more_results/more_import.css +29 -0
  137. data/test/sass/more_templates/_more_partial.sass +2 -0
  138. data/test/sass/more_templates/more1.sass +23 -0
  139. data/test/sass/more_templates/more_import.sass +11 -0
  140. data/test/sass/plugin_test.rb +213 -0
  141. data/test/sass/results/alt.css +4 -0
  142. data/test/sass/results/basic.css +9 -0
  143. data/test/sass/results/compact.css +5 -0
  144. data/test/sass/results/complex.css +87 -0
  145. data/test/sass/results/compressed.css +1 -0
  146. data/test/sass/results/expanded.css +19 -0
  147. data/test/sass/results/import.css +29 -0
  148. data/test/sass/results/line_numbers.css +49 -0
  149. data/test/sass/results/mixins.css +95 -0
  150. data/test/sass/results/multiline.css +24 -0
  151. data/test/sass/results/nested.css +22 -0
  152. data/test/sass/results/parent_ref.css +13 -0
  153. data/test/sass/results/script.css +16 -0
  154. data/test/sass/results/subdir/nested_subdir/nested_subdir.css +1 -0
  155. data/test/sass/results/subdir/subdir.css +3 -0
  156. data/test/sass/results/units.css +11 -0
  157. data/test/sass/script_test.rb +250 -0
  158. data/test/sass/templates/_partial.sass +2 -0
  159. data/test/sass/templates/alt.sass +16 -0
  160. data/test/sass/templates/basic.sass +23 -0
  161. data/test/sass/templates/bork.sass +2 -0
  162. data/test/sass/templates/bork2.sass +2 -0
  163. data/test/sass/templates/compact.sass +17 -0
  164. data/test/sass/templates/complex.sass +309 -0
  165. data/test/sass/templates/compressed.sass +15 -0
  166. data/test/sass/templates/expanded.sass +17 -0
  167. data/test/sass/templates/import.sass +11 -0
  168. data/test/sass/templates/importee.sass +19 -0
  169. data/test/sass/templates/line_numbers.sass +13 -0
  170. data/test/sass/templates/mixins.sass +76 -0
  171. data/test/sass/templates/multiline.sass +20 -0
  172. data/test/sass/templates/nested.sass +25 -0
  173. data/test/sass/templates/parent_ref.sass +25 -0
  174. data/test/sass/templates/script.sass +101 -0
  175. data/test/sass/templates/subdir/nested_subdir/_nested_partial.sass +2 -0
  176. data/test/sass/templates/subdir/nested_subdir/nested_subdir.sass +3 -0
  177. data/test/sass/templates/subdir/subdir.sass +6 -0
  178. data/test/sass/templates/units.sass +11 -0
  179. data/test/test_helper.rb +21 -0
  180. metadata +278 -0
@@ -0,0 +1,488 @@
1
+ require 'haml/helpers/action_view_mods'
2
+ require 'haml/helpers/action_view_extensions'
3
+
4
+ module Haml
5
+ # This module contains various helpful methods to make it easier to do
6
+ # various tasks. Haml::Helpers is automatically included in the context
7
+ # that a Haml template is parsed in, so all these methods are at your
8
+ # disposal from within the template.
9
+ module Helpers
10
+ # An object that raises an error when #to_s is called.
11
+ # It's used to raise an error when the return value of a helper is used
12
+ # when it shouldn't be.
13
+ class ErrorReturn
14
+ def initialize(message)
15
+ @message = message
16
+ end
17
+
18
+ def to_s
19
+ raise Haml::Error.new(@message)
20
+ end
21
+
22
+ def inspect
23
+ "Haml::Helpers::ErrorReturn(#{@message.inspect})"
24
+ end
25
+ end
26
+
27
+ self.extend self
28
+
29
+ @@action_view_defined = defined?(ActionView)
30
+ @@force_no_action_view = false
31
+
32
+ # Returns whether or not ActionView is installed on the system.
33
+ def self.action_view?
34
+ @@action_view_defined
35
+ end
36
+
37
+ # Note: this does *not* need to be called
38
+ # when using Haml helpers normally
39
+ # in Rails.
40
+ #
41
+ # Initializes the current object
42
+ # as though it were in the same context
43
+ # as a normal ActionView rendering
44
+ # using Haml.
45
+ # This is useful if you want to use the helpers in a context
46
+ # other than the normal setup with ActionView.
47
+ # For example:
48
+ #
49
+ # context = Object.new
50
+ # class << context
51
+ # include Haml::Helpers
52
+ # end
53
+ # context.init_haml_helpers
54
+ # context.haml_tag :p, "Stuff"
55
+ #
56
+ def init_haml_helpers
57
+ @haml_buffer = Haml::Buffer.new(@haml_buffer, Haml::Engine.new('').send(:options_for_buffer))
58
+ nil
59
+ end
60
+
61
+ # call-seq:
62
+ # non_haml { ... }
63
+ #
64
+ # Runs a block of code in a non-Haml context
65
+ # (i.e. #is_haml? will return false).
66
+ #
67
+ # This is mainly useful for rendering sub-templates such as partials in a non-Haml language,
68
+ # particularly where helpers may behave differently when run from Haml.
69
+ #
70
+ # Note that this is automatically applied to Rails partials.
71
+ def non_haml
72
+ was_active = @haml_buffer.active?
73
+ @haml_buffer.active = false
74
+ yield
75
+ ensure
76
+ @haml_buffer.active = was_active
77
+ end
78
+
79
+ # call-seq:
80
+ # find_and_preserve(input, tags = haml_buffer.options[:preserve])
81
+ # find_and_preserve {...}
82
+ #
83
+ # Uses preserve to convert any newlines inside whitespace-sensitive tags
84
+ # into the HTML entities for endlines.
85
+ # +tags+ is an array of tags to preserve.
86
+ # It defaults to the value of the <tt>:preserve</tt> option.
87
+ def find_and_preserve(input = '', tags = haml_buffer.options[:preserve], &block)
88
+ return find_and_preserve(capture_haml(&block)) if block
89
+
90
+ input = input.to_s
91
+ input.gsub(/<(#{tags.map(&Regexp.method(:escape)).join('|')})([^>]*)>(.*?)(<\/\1>)/im) do
92
+ "<#{$1}#{$2}>#{preserve($3)}</#{$1}>"
93
+ end
94
+ end
95
+
96
+ # call-seq:
97
+ # preserve(input)
98
+ # preserve {...}
99
+ #
100
+ # Takes any string, finds all the endlines and converts them to
101
+ # HTML entities for endlines so they'll render correctly in
102
+ # whitespace-sensitive tags without screwing up the indentation.
103
+ def preserve(input = '', &block)
104
+ return preserve(capture_haml(&block)) if block
105
+
106
+ input.chomp("\n").gsub(/\n/, '&#x000A;').gsub(/\r/, '')
107
+ end
108
+
109
+ alias_method :flatten, :preserve
110
+
111
+ # Takes an Enumerable object and a block
112
+ # and iterates over the object,
113
+ # yielding each element to a Haml block
114
+ # and putting the result into <tt><li></tt> elements.
115
+ # This creates a list of the results of the block.
116
+ # For example:
117
+ #
118
+ # = list_of([['hello'], ['yall']]) do |i|
119
+ # = i[0]
120
+ #
121
+ # Produces:
122
+ #
123
+ # <li>hello</li>
124
+ # <li>yall</li>
125
+ #
126
+ # And
127
+ #
128
+ # = list_of({:title => 'All the stuff', :description => 'A book about all the stuff.'}) do |key, val|
129
+ # %h3= key.humanize
130
+ # %p= val
131
+ #
132
+ # Produces:
133
+ #
134
+ # <li>
135
+ # <h3>Title</h3>
136
+ # <p>All the stuff</p>
137
+ # </li>
138
+ # <li>
139
+ # <h3>Description</h3>
140
+ # <p>A book about all the stuff.</p>
141
+ # </li>
142
+ #
143
+ def list_of(array, &block) # :yields: item
144
+ to_return = array.collect do |i|
145
+ result = capture_haml(i, &block)
146
+
147
+ if result.count("\n") > 1
148
+ result.gsub!("\n", "\n ")
149
+ result = "\n #{result.strip}\n"
150
+ else
151
+ result.strip!
152
+ end
153
+
154
+ "<li>#{result}</li>"
155
+ end
156
+ to_return.join("\n")
157
+ end
158
+
159
+ # Returns a hash containing default assignments for the xmlns and xml:lang
160
+ # attributes of the <tt>html</tt> HTML element.
161
+ # It also takes an optional argument for the value of xml:lang and lang,
162
+ # which defaults to 'en-US'.
163
+ # For example,
164
+ #
165
+ # %html{html_attrs}
166
+ #
167
+ # becomes
168
+ #
169
+ # <html xmlns='http://www.w3.org/1999/xhtml' xml:lang='en-US' lang='en-US'>
170
+ #
171
+ def html_attrs(lang = 'en-US')
172
+ {:xmlns => "http://www.w3.org/1999/xhtml", 'xml:lang' => lang, :lang => lang}
173
+ end
174
+
175
+ # Increments the number of tabs the buffer automatically adds
176
+ # to the lines of the template.
177
+ # For example:
178
+ #
179
+ # %h1 foo
180
+ # - tab_up
181
+ # %p bar
182
+ # - tab_down
183
+ # %strong baz
184
+ #
185
+ # Produces:
186
+ #
187
+ # <h1>foo</h1>
188
+ # <p>bar</p>
189
+ # <strong>baz</strong>
190
+ #
191
+ def tab_up(i = 1)
192
+ haml_buffer.tabulation += i
193
+ end
194
+
195
+ # Decrements the number of tabs the buffer automatically adds
196
+ # to the lines of the template.
197
+ #
198
+ # See also tab_up.
199
+ def tab_down(i = 1)
200
+ haml_buffer.tabulation -= i
201
+ end
202
+
203
+ # Surrounds the given block of Haml code with the given characters,
204
+ # with no whitespace in between.
205
+ # For example:
206
+ #
207
+ # = surround '(', ')' do
208
+ # %a{:href => "food"} chicken
209
+ #
210
+ # Produces:
211
+ #
212
+ # (<a href='food'>chicken</a>)
213
+ #
214
+ # and
215
+ #
216
+ # = surround '*' do
217
+ # %strong angry
218
+ #
219
+ # Produces:
220
+ #
221
+ # *<strong>angry</strong>*
222
+ #
223
+ def surround(front, back = nil, &block)
224
+ back ||= front
225
+ output = capture_haml(&block)
226
+
227
+ "#{front}#{output.chomp}#{back}\n"
228
+ end
229
+
230
+ # Prepends the given character to the beginning of the Haml block,
231
+ # with no whitespace between.
232
+ # For example:
233
+ #
234
+ # = precede '*' do
235
+ # %span.small Not really
236
+ #
237
+ # Produces:
238
+ #
239
+ # *<span class='small'>Not really</span>
240
+ #
241
+ def precede(char, &block)
242
+ "#{char}#{capture_haml(&block).chomp}\n"
243
+ end
244
+
245
+ # Appends the given character to the end of the Haml block,
246
+ # with no whitespace between.
247
+ # For example:
248
+ #
249
+ # click
250
+ # = succeed '.' do
251
+ # %a{:href=>"thing"} here
252
+ #
253
+ # Produces:
254
+ #
255
+ # click
256
+ # <a href='thing'>here</a>.
257
+ #
258
+ def succeed(char, &block)
259
+ "#{capture_haml(&block).chomp}#{char}\n"
260
+ end
261
+
262
+ # Captures the result of the given block of Haml code,
263
+ # gets rid of the excess indentation,
264
+ # and returns it as a string.
265
+ # For example, after the following,
266
+ #
267
+ # .foo
268
+ # - foo = capture_haml(13) do |a|
269
+ # %p= a
270
+ #
271
+ # the local variable <tt>foo</tt> would be assigned to "<p>13</p>\n".
272
+ #
273
+ def capture_haml(*args, &block)
274
+ buffer = eval('_hamlout', block.binding) rescue haml_buffer
275
+ with_haml_buffer(buffer) do
276
+ position = haml_buffer.buffer.length
277
+
278
+ block.call(*args)
279
+
280
+ captured = haml_buffer.buffer.slice!(position..-1).split(/^/)
281
+
282
+ min_tabs = nil
283
+ captured.each do |line|
284
+ tabs = line.index(/[^ ]/) || line.length
285
+ min_tabs ||= tabs
286
+ min_tabs = min_tabs > tabs ? tabs : min_tabs
287
+ end
288
+
289
+ captured.map do |line|
290
+ line[min_tabs..-1]
291
+ end.join
292
+ end
293
+ end
294
+
295
+ def puts(*args) # :nodoc:
296
+ warn <<END
297
+ DEPRECATION WARNING:
298
+ The Haml #puts helper is deprecated and will be removed in version 2.4.
299
+ Use the #haml_concat helper instead.
300
+ END
301
+ haml_concat(*args)
302
+ end
303
+
304
+ # Outputs text directly to the Haml buffer, with the proper tabulation
305
+ def haml_concat(text = "")
306
+ haml_buffer.buffer << haml_indent << text.to_s << "\n"
307
+ nil
308
+ end
309
+
310
+ # Returns the string that should be used to indent the current line
311
+ def haml_indent
312
+ ' ' * haml_buffer.tabulation
313
+ end
314
+
315
+ #
316
+ # call-seq:
317
+ # haml_tag(name, *flags, attributes = {}) {...}
318
+ # haml_tag(name, text, *flags, attributes = {}) {...}
319
+ #
320
+ # Creates an HTML tag with the given name and optionally text and attributes.
321
+ # Can take a block that will be executed
322
+ # between when the opening and closing tags are output.
323
+ # If the block is a Haml block or outputs text using haml_concat,
324
+ # the text will be properly indented.
325
+ #
326
+ # <tt>flags</tt> is a list of symbol flags
327
+ # like those that can be put at the end of a Haml tag
328
+ # (<tt>:/</tt>, <tt>:<</tt>, and <tt>:></tt>).
329
+ # Currently, only <tt>:/</tt> and <tt>:<</tt> are supported.
330
+ #
331
+ # For example,
332
+ #
333
+ # haml_tag :table do
334
+ # haml_tag :tr do
335
+ # haml_tag :td, {:class => 'cell'} do
336
+ # haml_tag :strong, "strong!"
337
+ # haml_concat "data"
338
+ # end
339
+ # haml_tag :td do
340
+ # haml_concat "more_data"
341
+ # end
342
+ # end
343
+ # end
344
+ #
345
+ # outputs
346
+ #
347
+ # <table>
348
+ # <tr>
349
+ # <td class='cell'>
350
+ # <strong>
351
+ # strong!
352
+ # </strong>
353
+ # data
354
+ # </td>
355
+ # <td>
356
+ # more_data
357
+ # </td>
358
+ # </tr>
359
+ # </table>
360
+ #
361
+ def haml_tag(name, *rest, &block)
362
+ ret = ErrorReturn.new(<<MESSAGE)
363
+ haml_tag outputs directly to the Haml template.
364
+ Disregard its return value and use the - operator.
365
+ MESSAGE
366
+
367
+ name = name.to_s
368
+ text = rest.shift.to_s unless [Symbol, Hash, NilClass].any? {|t| rest.first.is_a? t}
369
+ flags = []
370
+ flags << rest.shift while rest.first.is_a? Symbol
371
+ attributes = Haml::Precompiler.build_attributes(haml_buffer.html?,
372
+ haml_buffer.options[:attr_wrapper],
373
+ rest.shift || {})
374
+
375
+ if text.nil? && block.nil? && (haml_buffer.options[:autoclose].include?(name) || flags.include?(:/))
376
+ haml_concat "<#{name}#{attributes} />"
377
+ return ret
378
+ end
379
+
380
+ if flags.include?(:/)
381
+ raise Error.new("Self-closing tags can't have content.") if text
382
+ raise Error.new("Illegal nesting: nesting within a self-closing tag is illegal.") if block
383
+ end
384
+
385
+ tag = "<#{name}#{attributes}>"
386
+ if block.nil?
387
+ tag << text.to_s << "</#{name}>"
388
+ haml_concat tag
389
+ return ret
390
+ end
391
+
392
+ if text
393
+ raise Error.new("Illegal nesting: content can't be both given to haml_tag :#{name} and nested within it.")
394
+ end
395
+
396
+ if flags.include?(:<)
397
+ tag << capture_haml(&block).strip << "</#{name}>"
398
+ haml_concat tag
399
+ return ret
400
+ end
401
+
402
+ haml_concat tag
403
+ tab_up
404
+ block.call
405
+ tab_down
406
+ haml_concat "</#{name}>"
407
+
408
+ ret
409
+ end
410
+
411
+ # Characters that need to be escaped to HTML entities from user input
412
+ HTML_ESCAPE = { '&'=>'&amp;', '<'=>'&lt;', '>'=>'&gt;', '"'=>'&quot;', "'"=>'&#039;', }
413
+
414
+ # Returns a copy of <tt>text</tt> with ampersands, angle brackets and quotes
415
+ # escaped into HTML entities.
416
+ def html_escape(text)
417
+ text.to_s.gsub(/[\"><&]/) { |s| HTML_ESCAPE[s] }
418
+ end
419
+
420
+ # Escapes HTML entities in <tt>text</tt>, but without escaping an ampersand
421
+ # that is already part of an escaped entity.
422
+ def escape_once(text)
423
+ text.to_s.gsub(/[\"><]|&(?!([a-zA-Z]+|(#\d+));)/) { |s| HTML_ESCAPE[s] }
424
+ end
425
+
426
+ # Returns whether or not the current template is a Haml template.
427
+ #
428
+ # This function, unlike other Haml::Helpers functions,
429
+ # also works in other ActionView templates,
430
+ # where it will always return false.
431
+ def is_haml?
432
+ !@haml_buffer.nil? && @haml_buffer.active?
433
+ end
434
+
435
+ # Returns whether or not +block+ is defined directly in a Haml template.
436
+ def block_is_haml?(block)
437
+ eval('_hamlout', block.binding)
438
+ true
439
+ rescue
440
+ false
441
+ end
442
+
443
+ private
444
+
445
+ # call-seq:
446
+ # with_haml_buffer(buffer) {...}
447
+ #
448
+ # Runs the block with the given buffer as the currently active buffer.
449
+ def with_haml_buffer(buffer)
450
+ @haml_buffer, old_buffer = buffer, @haml_buffer
451
+ old_buffer.active, was_active = false, old_buffer.active? if old_buffer
452
+ @haml_buffer.active = true
453
+ yield
454
+ ensure
455
+ @haml_buffer.active = false
456
+ old_buffer.active = was_active if old_buffer
457
+ @haml_buffer = old_buffer
458
+ end
459
+
460
+ # Gets a reference to the current Haml::Buffer object.
461
+ def haml_buffer
462
+ @haml_buffer
463
+ end
464
+
465
+ # Gives a proc the same local "_hamlout" and "_erbout" variables
466
+ # that the current template has.
467
+ def haml_bind_proc(&proc)
468
+ _hamlout = haml_buffer
469
+ _erbout = _hamlout.buffer
470
+ proc { |*args| proc.call(*args) }
471
+ end
472
+
473
+ include ActionViewExtensions if self.const_defined? "ActionViewExtensions"
474
+ end
475
+ end
476
+
477
+ class Object
478
+ # Haml overrides various ActionView helpers,
479
+ # which call an #is_haml? method
480
+ # to determine whether or not the current context object
481
+ # is a proper Haml context.
482
+ # Because ActionView helpers may be included in non-ActionView::Base classes,
483
+ # it's a good idea to define is_haml? for all objects.
484
+ def is_haml?
485
+ false
486
+ end
487
+ end
488
+
data/lib/haml/html.rb ADDED
@@ -0,0 +1,222 @@
1
+ require File.dirname(__FILE__) + '/../haml'
2
+
3
+ require 'haml/engine'
4
+ require 'rubygems'
5
+ require 'hpricot'
6
+ require 'cgi'
7
+
8
+ module Haml
9
+ # This class contains the functionality used in the +html2haml+ utility,
10
+ # namely converting HTML documents to Haml templates.
11
+ # It depends on Hpricot for HTML parsing (http://code.whytheluckystiff.net/hpricot/).
12
+ class HTML
13
+ # Creates a new instance of Haml::HTML that will compile the given template,
14
+ # which can either be a string containing HTML or an Hpricot node,
15
+ # to a Haml string when +render+ is called.
16
+ def initialize(template, options = {})
17
+ @@options = options
18
+
19
+ if template.is_a? Hpricot::Node
20
+ @template = template
21
+ else
22
+ if template.is_a? IO
23
+ template = template.read
24
+ end
25
+
26
+ if @@options[:rhtml]
27
+ match_to_html(template, /<%=(.*?)-?%>/m, 'loud')
28
+ match_to_html(template, /<%-?(.*?)-?%>/m, 'silent')
29
+ end
30
+
31
+ method = @@options[:xhtml] ? Hpricot.method(:XML) : method(:Hpricot)
32
+ @template = method.call(template.gsub('&', '&amp;'))
33
+ end
34
+ end
35
+
36
+ # Processes the document and returns the result as a string
37
+ # containing the Haml template.
38
+ def render
39
+ @template.to_haml(0)
40
+ end
41
+ alias_method :to_haml, :render
42
+
43
+ module ::Hpricot::Node
44
+ # Returns the Haml representation of the given node,
45
+ # at the given tabulation.
46
+ def to_haml(tabs = 0)
47
+ parse_text(self.to_s, tabs)
48
+ end
49
+
50
+ private
51
+
52
+ def tabulate(tabs)
53
+ ' ' * tabs
54
+ end
55
+
56
+ def parse_text(text, tabs)
57
+ text.strip!
58
+ if text.empty?
59
+ String.new
60
+ else
61
+ lines = text.split("\n")
62
+
63
+ lines.map do |line|
64
+ line.strip!
65
+ "#{tabulate(tabs)}#{'\\' if Haml::Engine::SPECIAL_CHARACTERS.include?(line[0])}#{line}\n"
66
+ end.join
67
+ end
68
+ end
69
+ end
70
+
71
+ # :stopdoc:
72
+
73
+ def self.options
74
+ @@options
75
+ end
76
+
77
+ TEXT_REGEXP = /^(\s*).*$/
78
+
79
+ class ::Hpricot::Doc
80
+ def to_haml(tabs = 0)
81
+ (children || []).inject('') {|s, c| s << c.to_haml(0)}
82
+ end
83
+ end
84
+
85
+ class ::Hpricot::XMLDecl
86
+ def to_haml(tabs = 0)
87
+ "#{tabulate(tabs)}!!! XML\n"
88
+ end
89
+ end
90
+
91
+ class ::Hpricot::CData
92
+ def to_haml(tabs = 0)
93
+ "#{tabulate(tabs)}:cdata\n#{parse_text(self.content, tabs + 1)}"
94
+ end
95
+ end
96
+
97
+ class ::Hpricot::DocType
98
+ def to_haml(tabs = 0)
99
+ attrs = public_id.scan(/DTD\s+([^\s]+)\s*([^\s]*)\s*([^\s]*)\s*\/\//)[0]
100
+ if attrs == nil
101
+ raise Exception.new("Invalid doctype")
102
+ end
103
+
104
+ type, version, strictness = attrs.map { |a| a.downcase }
105
+ if type == "html"
106
+ version = "1.0"
107
+ strictness = "transitional"
108
+ end
109
+
110
+ if version == "1.0" || version.empty?
111
+ version = nil
112
+ end
113
+
114
+ if strictness == 'transitional' || strictness.empty?
115
+ strictness = nil
116
+ end
117
+
118
+ version = " #{version}" if version
119
+ if strictness
120
+ strictness[0] = strictness[0] - 32
121
+ strictness = " #{strictness}"
122
+ end
123
+
124
+ "#{tabulate(tabs)}!!!#{version}#{strictness}\n"
125
+ end
126
+ end
127
+
128
+ class ::Hpricot::Comment
129
+ def to_haml(tabs = 0)
130
+ "#{tabulate(tabs)}/\n#{parse_text(self.content, tabs + 1)}"
131
+ end
132
+ end
133
+
134
+ class ::Hpricot::Elem
135
+ def to_haml(tabs = 0)
136
+ output = "#{tabulate(tabs)}"
137
+ if HTML.options[:rhtml] && name[0...5] == 'haml:'
138
+ return output + HTML.send("haml_tag_#{name[5..-1]}", CGI.unescapeHTML(self.inner_text))
139
+ end
140
+
141
+ output += "%#{name}" unless name == 'div' && (static_id? || static_classname?)
142
+
143
+ if attributes
144
+ if static_id?
145
+ output += "##{attributes['id']}"
146
+ remove_attribute('id')
147
+ end
148
+ if static_classname?
149
+ attributes['class'].split(' ').each { |c| output += ".#{c}" }
150
+ remove_attribute('class')
151
+ end
152
+ output += haml_attributes if attributes.length > 0
153
+ end
154
+
155
+ (self.children || []).inject(output + "\n") do |output, child|
156
+ output + child.to_haml(tabs + 1)
157
+ end
158
+ end
159
+
160
+ private
161
+
162
+ def dynamic_attributes
163
+ @dynamic_attributes ||= begin
164
+ Haml::Util.map_hash(attributes) do |name, value|
165
+ next if value.empty?
166
+ full_match = nil
167
+ ruby_value = value.gsub(%r{<haml:loud>\s*(.+?)\s*</haml:loud>}) do
168
+ full_match = $`.empty? && $'.empty?
169
+ full_match ? $1: "\#{#{$1}}"
170
+ end
171
+ next if ruby_value == value
172
+ [name, full_match ? ruby_value : %("#{ruby_value}")]
173
+ end
174
+ end
175
+ end
176
+
177
+ def static_attribute?(name)
178
+ attributes[name] and !dynamic_attribute?(name)
179
+ end
180
+
181
+ def dynamic_attribute?(name)
182
+ HTML.options[:rhtml] and dynamic_attributes.key?(name)
183
+ end
184
+
185
+ def static_id?
186
+ static_attribute? 'id'
187
+ end
188
+
189
+ def static_classname?
190
+ static_attribute? 'class'
191
+ end
192
+
193
+ # Returns a string representation of an attributes hash
194
+ # that's prettier than that produced by Hash#inspect
195
+ def haml_attributes
196
+ attrs = attributes.map do |name, value|
197
+ value = dynamic_attribute?(name) ? dynamic_attributes[name] : value.inspect
198
+ name = name.index(/\W/) ? name.inspect : ":#{name}"
199
+ "#{name} => #{value}"
200
+ end
201
+ "{ #{attrs.join(', ')} }"
202
+ end
203
+ end
204
+
205
+ def self.haml_tag_loud(text)
206
+ "= #{text.gsub(/\n\s*/, ' ').strip}\n"
207
+ end
208
+
209
+ def self.haml_tag_silent(text)
210
+ text.split("\n").map { |line| "- #{line.strip}\n" }.join
211
+ end
212
+
213
+ private
214
+
215
+ def match_to_html(string, regex, tag)
216
+ string.gsub!(regex) do
217
+ "<haml:#{tag}>#{CGI.escapeHTML($1)}</haml:#{tag}>"
218
+ end
219
+ end
220
+ # :startdoc:
221
+ end
222
+ end