haml 3.2.0.alpha.13 → 3.2.0.alpha.14

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of haml might be problematic. Click here for more details.

@@ -1,7 +0,0 @@
1
- #!/usr/bin/env ruby
2
-
3
- require File.dirname(__FILE__) + '/../lib/haml'
4
- require 'haml/exec'
5
-
6
- opts = Haml::Exec::HTML2Haml.new(ARGV)
7
- opts.parse!
@@ -1,425 +0,0 @@
1
- require File.dirname(__FILE__) + '/../haml'
2
-
3
- require 'haml/engine'
4
- require 'rubygems'
5
- require 'cgi'
6
- require 'hpricot'
7
-
8
- # Haml monkeypatches various Hpricot classes
9
- # to add methods for conversion to Haml.
10
- # @private
11
- module Hpricot
12
- # @see Hpricot
13
- module Node
14
- # Whether this node has already been converted to Haml.
15
- # Only used for text nodes and elements.
16
- #
17
- # @return [Boolean]
18
- attr_accessor :converted_to_haml
19
-
20
- # Returns the Haml representation of the given node.
21
- #
22
- # @param tabs [Fixnum] The indentation level of the resulting Haml.
23
- # @option options (see Haml::HTML#initialize)
24
- def to_haml(tabs, options)
25
- return "" if converted_to_haml || to_s.strip.empty?
26
- text = uninterp(self.to_s)
27
- node = next_node
28
- while node.is_a?(::Hpricot::Elem) && node.name == "haml:loud"
29
- node.converted_to_haml = true
30
- text << '#{' <<
31
- CGI.unescapeHTML(node.inner_text).gsub(/\n\s*/, ' ').strip << '}'
32
-
33
- if node.next_node.is_a?(::Hpricot::Text)
34
- node = node.next_node
35
- text << uninterp(node.to_s)
36
- node.converted_to_haml = true
37
- end
38
-
39
- node = node.next_node
40
- end
41
- return parse_text_with_interpolation(text, tabs)
42
- end
43
-
44
- private
45
-
46
- def erb_to_interpolation(text, options)
47
- return text unless options[:erb]
48
- text = CGI.escapeHTML(uninterp(text))
49
- %w[<haml:loud> </haml:loud>].each {|str| text.gsub!(CGI.escapeHTML(str), str)}
50
- ::Hpricot::XML(text).children.inject("") do |str, elem|
51
- if elem.is_a?(::Hpricot::Text)
52
- str + CGI.unescapeHTML(elem.to_s)
53
- else # <haml:loud> element
54
- str + '#{' + CGI.unescapeHTML(elem.innerText.strip) + '}'
55
- end
56
- end
57
- end
58
-
59
- def tabulate(tabs)
60
- ' ' * tabs
61
- end
62
-
63
- def uninterp(text)
64
- text.gsub('#{', '\#{') #'
65
- end
66
-
67
- def attr_hash
68
- attributes.to_hash
69
- end
70
-
71
- def parse_text(text, tabs)
72
- parse_text_with_interpolation(uninterp(text), tabs)
73
- end
74
-
75
- def parse_text_with_interpolation(text, tabs)
76
- text.strip!
77
- return "" if text.empty?
78
-
79
- text.split("\n").map do |line|
80
- line.strip!
81
- "#{tabulate(tabs)}#{'\\' if Haml::Engine::SPECIAL_CHARACTERS.include?(line[0])}#{line}\n"
82
- end.join
83
- end
84
- end
85
- end
86
-
87
- # @private
88
- HAML_TAGS = %w[haml:block haml:loud haml:silent]
89
-
90
- HAML_TAGS.each do |t|
91
- Hpricot::ElementContent[t] = {}
92
- Hpricot::ElementContent.keys.each do |key|
93
- Hpricot::ElementContent[t][key.hash] = true
94
- end
95
- end
96
-
97
- Hpricot::ElementContent.keys.each do |k|
98
- HAML_TAGS.each do |el|
99
- val = Hpricot::ElementContent[k]
100
- val[el.hash] = true if val.is_a?(Hash)
101
- end
102
- end
103
-
104
- module Haml
105
- # Converts HTML documents into Haml templates.
106
- # Depends on [Hpricot](http://github.com/whymirror/hpricot) for HTML parsing.
107
- # If ERB conversion is being used, also depends on
108
- # [Erubis](http://www.kuwata-lab.com/erubis) to parse the ERB
109
- # and [ruby_parser](http://parsetree.rubyforge.org/) to parse the Ruby code.
110
- #
111
- # Example usage:
112
- #
113
- # Haml::HTML.new("<a href='http://google.com'>Blat</a>").render
114
- # #=> "%a{:href => 'http://google.com'} Blat"
115
- class HTML
116
- # @param template [String, Hpricot::Node] The HTML template to convert
117
- # @option options :erb [Boolean] (false) Whether or not to parse
118
- # ERB's `<%= %>` and `<% %>` into Haml's `=` and `-`
119
- # @option options :xhtml [Boolean] (false) Whether or not to parse
120
- # the HTML strictly as XHTML
121
- def initialize(template, options = {})
122
- @options = options
123
-
124
- if template.is_a? Hpricot::Node
125
- @template = template
126
- else
127
- if template.is_a? IO
128
- template = template.read
129
- end
130
-
131
- template = Haml::Util.check_encoding(template) {|msg, line| raise Haml::Error.new(msg, line)}
132
-
133
- if @options[:erb]
134
- require 'haml/html/erb'
135
- template = ERB.compile(template)
136
- end
137
-
138
- method = @options[:xhtml] ? Hpricot.method(:XML) : method(:Hpricot)
139
- @template = method.call(template.gsub('&', '&amp;'))
140
- end
141
- end
142
-
143
- # Processes the document and returns the result as a string
144
- # containing the Haml template.
145
- def render
146
- @template.to_haml(0, @options)
147
- end
148
- alias_method :to_haml, :render
149
-
150
- TEXT_REGEXP = /^(\s*).*$/
151
-
152
- # @see Hpricot
153
- # @private
154
- class ::Hpricot::Doc
155
- # @see Haml::HTML::Node#to_haml
156
- def to_haml(tabs, options)
157
- (children || []).inject('') {|s, c| s << c.to_haml(0, options)}
158
- end
159
- end
160
-
161
- # @see Hpricot
162
- # @private
163
- class ::Hpricot::XMLDecl
164
- # @see Haml::HTML::Node#to_haml
165
- def to_haml(tabs, options)
166
- "#{tabulate(tabs)}!!! XML\n"
167
- end
168
- end
169
-
170
- # @see Hpricot
171
- # @private
172
- class ::Hpricot::CData
173
- # @see Haml::HTML::Node#to_haml
174
- def to_haml(tabs, options)
175
- content = parse_text_with_interpolation(
176
- erb_to_interpolation(self.content, options), tabs + 1)
177
- "#{tabulate(tabs)}:cdata\n#{content}"
178
- end
179
- end
180
-
181
- # @see Hpricot
182
- # @private
183
- class ::Hpricot::DocType
184
- # @see Haml::HTML::Node#to_haml
185
- def to_haml(tabs, options)
186
- attrs = public_id.nil? ? ["", "", ""] :
187
- public_id.scan(/DTD\s+([^\s]+)\s*([^\s]*)\s*([^\s]*)\s*\/\//)[0]
188
- raise Haml::SyntaxError.new("Invalid doctype") if attrs == nil
189
-
190
- type, version, strictness = attrs.map { |a| a.downcase }
191
- if type == "html"
192
- version = ""
193
- strictness = "strict" if strictness == ""
194
- end
195
-
196
- if version == "1.0" || version.empty?
197
- version = nil
198
- end
199
-
200
- if strictness == 'transitional' || strictness.empty?
201
- strictness = nil
202
- end
203
-
204
- version = " #{version.capitalize}" if version
205
- strictness = " #{strictness.capitalize}" if strictness
206
-
207
- "#{tabulate(tabs)}!!!#{version}#{strictness}\n"
208
- end
209
- end
210
-
211
- # @see Hpricot
212
- # @private
213
- class ::Hpricot::Comment
214
- # @see Haml::HTML::Node#to_haml
215
- def to_haml(tabs, options)
216
- content = self.content
217
- if content =~ /\A(\[[^\]]+\])>(.*)<!\[endif\]\z/m
218
- condition = $1
219
- content = $2
220
- end
221
-
222
- if content.include?("\n")
223
- "#{tabulate(tabs)}/#{condition}\n#{parse_text(content, tabs + 1)}"
224
- else
225
- "#{tabulate(tabs)}/#{condition} #{content.strip}\n"
226
- end
227
- end
228
- end
229
-
230
- # @see Hpricot
231
- # @private
232
- class ::Hpricot::Elem
233
- # @see Haml::HTML::Node#to_haml
234
- def to_haml(tabs, options)
235
- return "" if converted_to_haml
236
- if name == "script" &&
237
- (attr_hash['type'].nil? || attr_hash['type'] == "text/javascript") &&
238
- (attr_hash.keys - ['type']).empty?
239
- return to_haml_filter(:javascript, tabs, options)
240
- elsif name == "style" &&
241
- (attr_hash['type'].nil? || attr_hash['type'] == "text/css") &&
242
- (attr_hash.keys - ['type']).empty?
243
- return to_haml_filter(:css, tabs, options)
244
- end
245
-
246
- output = tabulate(tabs)
247
- if options[:erb] && name[0...5] == 'haml:'
248
- case name[5..-1]
249
- when "loud"
250
- lines = CGI.unescapeHTML(inner_text).split("\n").
251
- map {|s| s.rstrip}.reject {|s| s.strip.empty?}
252
- lines.first.gsub!(/^[ \t]*/, "= ")
253
-
254
- if lines.size > 1 # Multiline script block
255
- # Normalize the indentation so that the last line is the base
256
- indent_str = lines.last[/^[ \t]*/]
257
- indent_re = /^[ \t]{0,#{indent_str.count(" ") + 8 * indent_str.count("\t")}}/
258
- lines.map! {|s| s.gsub!(indent_re, '')}
259
-
260
- # Add an extra " " to make it indented relative to "= "
261
- lines[1..-1].each {|s| s.gsub!(/^/, " ")}
262
-
263
- # Add | at the end, properly aligned
264
- length = lines.map {|s| s.size}.max + 1
265
- lines.map! {|s| "%#{-length}s|" % s}
266
-
267
- if next_sibling && next_sibling.is_a?(Hpricot::Elem) && next_sibling.name == "haml:loud" &&
268
- next_sibling.inner_text.split("\n").reject {|s| s.strip.empty?}.size > 1
269
- lines << "-#"
270
- end
271
- end
272
- return lines.map {|s| output + s + "\n"}.join
273
- when "silent"
274
- return CGI.unescapeHTML(inner_text).split("\n").map do |line|
275
- next "" if line.strip.empty?
276
- "#{output}- #{line.strip}\n"
277
- end.join
278
- when "block"
279
- return render_children("", tabs, options)
280
- end
281
- end
282
-
283
- if self.next && self.next.text? && self.next.content =~ /\A[^\s]/
284
- if self.previous.nil? || self.previous.text? &&
285
- (self.previous.content =~ /[^\s]\Z/ ||
286
- self.previous.content =~ /\A\s*\Z/ && self.previous.previous.nil?)
287
- nuke_outer_whitespace = true
288
- else
289
- output << "= succeed #{self.next.content.slice!(/\A[^\s]+/).dump} do\n"
290
- tabs += 1
291
- output << tabulate(tabs)
292
- end
293
- end
294
-
295
- output << "%#{name}" unless name == 'div' &&
296
- (static_id?(options) ||
297
- static_classname?(options) &&
298
- attr_hash['class'].split(' ').any?(&method(:haml_css_attr?)))
299
-
300
- if attr_hash
301
- if static_id?(options)
302
- output << "##{attr_hash['id']}"
303
- remove_attribute('id')
304
- end
305
- if static_classname?(options)
306
- leftover = attr_hash['class'].split(' ').reject do |c|
307
- next unless haml_css_attr?(c)
308
- output << ".#{c}"
309
- end
310
- remove_attribute('class')
311
- set_attribute('class', leftover.join(' ')) unless leftover.empty?
312
- end
313
- output << haml_attributes(options) if attr_hash.length > 0
314
- end
315
-
316
- output << ">" if nuke_outer_whitespace
317
- output << "/" if empty? && !etag
318
-
319
- if children && children.size == 1
320
- child = children.first
321
- if child.is_a?(::Hpricot::Text)
322
- if !child.to_s.include?("\n")
323
- text = child.to_haml(tabs + 1, options)
324
- return output + " " + text.lstrip.gsub(/^\\/, '') unless text.chomp.include?("\n")
325
- return output + "\n" + text
326
- elsif ["pre", "textarea"].include?(name) ||
327
- (name == "code" && parent.is_a?(::Hpricot::Elem) && parent.name == "pre")
328
- return output + "\n#{tabulate(tabs + 1)}:preserve\n" +
329
- innerText.gsub(/^/, tabulate(tabs + 2))
330
- end
331
- elsif child.is_a?(::Hpricot::Elem) && child.name == "haml:loud"
332
- return output + child.to_haml(tabs + 1, options).lstrip
333
- end
334
- end
335
-
336
- render_children(output + "\n", tabs, options)
337
- end
338
-
339
- private
340
-
341
- def render_children(so_far, tabs, options)
342
- (self.children || []).inject(so_far) do |output, child|
343
- output + child.to_haml(tabs + 1, options)
344
- end
345
- end
346
-
347
- def dynamic_attributes
348
- @dynamic_attributes ||= begin
349
- Hash[attr_hash.map do |name, value|
350
- next if value.empty?
351
- full_match = nil
352
- ruby_value = value.gsub(%r{<haml:loud>\s*(.+?)\s*</haml:loud>}) do
353
- full_match = $`.empty? && $'.empty?
354
- CGI.unescapeHTML(full_match ? $1: "\#{#{$1}}")
355
- end
356
- next if ruby_value == value
357
- [name, full_match ? ruby_value : %("#{ruby_value}")]
358
- end]
359
- end
360
- end
361
-
362
- def to_haml_filter(filter, tabs, options)
363
- content =
364
- if children.first.is_a?(::Hpricot::CData)
365
- children.first.content
366
- else
367
- CGI.unescapeHTML(self.innerText)
368
- end
369
-
370
- content = erb_to_interpolation(content, options)
371
- content.gsub!(/\A\s*\n(\s*)/, '\1')
372
- original_indent = content[/\A(\s*)/, 1]
373
- if content.split("\n").all? {|l| l.strip.empty? || l =~ /^#{original_indent}/}
374
- content.gsub!(/^#{original_indent}/, tabulate(tabs + 1))
375
- end
376
-
377
- "#{tabulate(tabs)}:#{filter}\n#{content}"
378
- end
379
-
380
- def static_attribute?(name, options)
381
- attr_hash[name] && !dynamic_attribute?(name, options)
382
- end
383
-
384
- def dynamic_attribute?(name, options)
385
- options[:erb] and dynamic_attributes.key?(name)
386
- end
387
-
388
- def static_id?(options)
389
- static_attribute?('id', options) && haml_css_attr?(attr_hash['id'])
390
- end
391
-
392
- def static_classname?(options)
393
- static_attribute?('class', options)
394
- end
395
-
396
- def haml_css_attr?(attr)
397
- attr =~ /^[-:\w]+$/
398
- end
399
-
400
- # Returns a string representation of an attributes hash
401
- # that's prettier than that produced by Hash#inspect
402
- def haml_attributes(options)
403
- attrs = attr_hash.sort.map do |name, value|
404
- haml_attribute_pair(name, value, options)
405
- end
406
- if options[:html_style_attributes]
407
- "(#{attrs.join(' ')})"
408
- else
409
- "{#{attrs.join(', ')}}"
410
- end
411
- end
412
-
413
- # Returns the string representation of a single attribute key value pair
414
- def haml_attribute_pair(name, value, options)
415
- value = dynamic_attribute?(name, options) ? dynamic_attributes[name] : value.inspect
416
- if options[:html_style_attributes]
417
- "#{name}=#{value}"
418
- else
419
- name = name.index(/\W/) ? name.inspect : ":#{name}"
420
- "#{name} => #{value}"
421
- end
422
- end
423
- end
424
- end
425
- end
@@ -1,141 +0,0 @@
1
- require 'cgi'
2
- require 'erubis'
3
- require 'ruby_parser'
4
-
5
- module Haml
6
- class HTML
7
- # A class for converting ERB code into a format that's easier
8
- # for the {Haml::HTML} Hpricot-based parser to understand.
9
- #
10
- # Uses [Erubis](http://www.kuwata-lab.com/erubis)'s extensible parsing powers
11
- # to parse the ERB in a reliable way,
12
- # and [ruby_parser](http://parsetree.rubyforge.org/)'s Ruby knowledge
13
- # to figure out whether a given chunk of Ruby code starts a block or not.
14
- #
15
- # The ERB tags are converted to HTML tags in the following way.
16
- # `<% ... %>` is converted into `<haml:silent> ... </haml:silent>`.
17
- # `<%= ... %>` is converted into `<haml:loud> ... </haml:loud>`.
18
- # Finally, if either of these opens a Ruby block,
19
- # `<haml:block> ... </haml:block>` will wrap the entire contents of the block -
20
- # that is, everything that should be indented beneath the previous silent or loud tag.
21
- class ERB < Erubis::Basic::Engine
22
- # Compiles an ERB template into a HTML document containing `haml:` tags.
23
- #
24
- # @param template [String] The ERB template
25
- # @return [String] The output document
26
- # @see Haml::HTML::ERB
27
- def self.compile(template)
28
- new(template).src
29
- end
30
-
31
- # `html2haml` doesn't support HTML-escaped expressions.
32
- def escaped_expr(code)
33
- raise Haml::Error.new("html2haml doesn't support escaped expressions.")
34
- end
35
-
36
- # The ERB-to-Hamlized-HTML conversion has no preamble.
37
- def add_preamble(src); end
38
-
39
- # The ERB-to-Hamlized-HTML conversion has no postamble.
40
- def add_postamble(src); end
41
-
42
- # Concatenates the text onto the source buffer.
43
- #
44
- # @param src [String] The source buffer
45
- # @param text [String] The raw text to add to the buffer
46
- def add_text(src, text)
47
- src << text
48
- end
49
-
50
- # Concatenates a silent Ruby statement onto the source buffer.
51
- # This uses the `<haml:silent>` tag,
52
- # and may close and/or open a Ruby block with the `<haml:block>` tag.
53
- #
54
- # In particular, a block is closed if this statement is some form of `end`,
55
- # opened if it's a block opener like `do`, `if`, or `begin`,
56
- # and both closed and opened if it's a mid-block keyword
57
- # like `else` or `when`.
58
- #
59
- # @param src [String] The source buffer
60
- # @param code [String] The Ruby statement to add to the buffer
61
- def add_stmt(src, code)
62
- src << '</haml:block>' if block_closer?(code) || mid_block?(code)
63
- src << '<haml:silent>' << h(code) << '</haml:silent>' unless code.strip == "end"
64
- src << '<haml:block>' if block_opener?(code) || mid_block?(code)
65
- end
66
-
67
- # Concatenates a Ruby expression that's printed to the document
68
- # onto the source buffer.
69
- # This uses the `<haml:silent>` tag,
70
- # and may open a Ruby block with the `<haml:block>` tag.
71
- # An expression never closes a block.
72
- #
73
- # @param src [String] The source buffer
74
- # @param code [String] The Ruby expression to add to the buffer
75
- def add_expr_literal(src, code)
76
- src << '<haml:loud>' << h(code) << '</haml:loud>'
77
- src << '<haml:block>' if block_opener?(code)
78
- end
79
-
80
- # `html2haml` doesn't support debugging expressions.
81
- def add_expr_debug(src, code)
82
- raise Haml::Error.new("html2haml doesn't support debugging expressions.")
83
- end
84
-
85
- private
86
-
87
- # HTML-escaped some text (in practice, always Ruby code).
88
- # A utility method.
89
- #
90
- # @param text [String] The text to escape
91
- # @return [String] The escaped text
92
- def h(text)
93
- CGI.escapeHTML(text)
94
- end
95
-
96
- # Returns whether the code is valid Ruby code on its own.
97
- #
98
- # @param code [String] Ruby code to check
99
- # @return [Boolean]
100
- def valid_ruby?(code)
101
- RubyParser.new.parse(code)
102
- rescue Racc::ParseError
103
- false
104
- end
105
-
106
- # Checks if a string of Ruby code opens a block.
107
- # This could either be something like `foo do |a|`
108
- # or a keyword that requires a matching `end`
109
- # like `if`, `begin`, or `case`.
110
- #
111
- # @param code [String] Ruby code to check
112
- # @return [Boolean]
113
- def block_opener?(code)
114
- valid_ruby?(code + "\nend") ||
115
- valid_ruby?(code + "\nwhen foo\nend")
116
- end
117
-
118
- # Checks if a string of Ruby code closes a block.
119
- # This is always `end` followed optionally by some method calls.
120
- #
121
- # @param code [String] Ruby code to check
122
- # @return [Boolean]
123
- def block_closer?(code)
124
- valid_ruby?("begin\n" + code)
125
- end
126
-
127
- # Checks if a string of Ruby code comes in the middle of a block.
128
- # This could be a keyword like `else`, `rescue`, or `when`,
129
- # or even `end` with a method call that takes a block.
130
- #
131
- # @param code [String] Ruby code to check
132
- # @return [Boolean]
133
- def mid_block?(code)
134
- return if valid_ruby?(code)
135
- valid_ruby?("if foo\n#{code}\nend") || # else, elsif
136
- valid_ruby?("begin\n#{code}\nend") || # rescue, ensure
137
- valid_ruby?("case foo\n#{code}\nend") # when
138
- end
139
- end
140
- end
141
- end