drnic-haml 2.3.0

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 (190) hide show
  1. data/.yardopts +5 -0
  2. data/CONTRIBUTING +4 -0
  3. data/MIT-LICENSE +20 -0
  4. data/README.md +347 -0
  5. data/REVISION +1 -0
  6. data/Rakefile +371 -0
  7. data/VERSION +1 -0
  8. data/VERSION_NAME +1 -0
  9. data/bin/css2sass +7 -0
  10. data/bin/haml +9 -0
  11. data/bin/html2haml +7 -0
  12. data/bin/sass +8 -0
  13. data/extra/haml-mode.el +663 -0
  14. data/extra/sass-mode.el +205 -0
  15. data/extra/update_watch.rb +13 -0
  16. data/init.rb +8 -0
  17. data/lib/haml.rb +40 -0
  18. data/lib/haml/buffer.rb +307 -0
  19. data/lib/haml/engine.rb +301 -0
  20. data/lib/haml/error.rb +22 -0
  21. data/lib/haml/exec.rb +470 -0
  22. data/lib/haml/filters.rb +341 -0
  23. data/lib/haml/helpers.rb +560 -0
  24. data/lib/haml/helpers/action_view_extensions.rb +40 -0
  25. data/lib/haml/helpers/action_view_mods.rb +176 -0
  26. data/lib/haml/herb.rb +96 -0
  27. data/lib/haml/html.rb +308 -0
  28. data/lib/haml/precompiler.rb +997 -0
  29. data/lib/haml/shared.rb +78 -0
  30. data/lib/haml/template.rb +51 -0
  31. data/lib/haml/template/patch.rb +58 -0
  32. data/lib/haml/template/plugin.rb +71 -0
  33. data/lib/haml/util.rb +244 -0
  34. data/lib/haml/version.rb +64 -0
  35. data/lib/sass.rb +24 -0
  36. data/lib/sass/css.rb +423 -0
  37. data/lib/sass/engine.rb +491 -0
  38. data/lib/sass/environment.rb +79 -0
  39. data/lib/sass/error.rb +162 -0
  40. data/lib/sass/files.rb +133 -0
  41. data/lib/sass/plugin.rb +170 -0
  42. data/lib/sass/plugin/merb.rb +57 -0
  43. data/lib/sass/plugin/rails.rb +23 -0
  44. data/lib/sass/repl.rb +58 -0
  45. data/lib/sass/script.rb +55 -0
  46. data/lib/sass/script/bool.rb +17 -0
  47. data/lib/sass/script/color.rb +183 -0
  48. data/lib/sass/script/funcall.rb +50 -0
  49. data/lib/sass/script/functions.rb +199 -0
  50. data/lib/sass/script/lexer.rb +191 -0
  51. data/lib/sass/script/literal.rb +177 -0
  52. data/lib/sass/script/node.rb +14 -0
  53. data/lib/sass/script/number.rb +381 -0
  54. data/lib/sass/script/operation.rb +45 -0
  55. data/lib/sass/script/parser.rb +222 -0
  56. data/lib/sass/script/string.rb +12 -0
  57. data/lib/sass/script/unary_operation.rb +34 -0
  58. data/lib/sass/script/variable.rb +31 -0
  59. data/lib/sass/tree/comment_node.rb +84 -0
  60. data/lib/sass/tree/debug_node.rb +30 -0
  61. data/lib/sass/tree/directive_node.rb +70 -0
  62. data/lib/sass/tree/for_node.rb +48 -0
  63. data/lib/sass/tree/if_node.rb +54 -0
  64. data/lib/sass/tree/import_node.rb +69 -0
  65. data/lib/sass/tree/mixin_def_node.rb +29 -0
  66. data/lib/sass/tree/mixin_node.rb +48 -0
  67. data/lib/sass/tree/node.rb +252 -0
  68. data/lib/sass/tree/prop_node.rb +106 -0
  69. data/lib/sass/tree/root_node.rb +56 -0
  70. data/lib/sass/tree/rule_node.rb +220 -0
  71. data/lib/sass/tree/variable_node.rb +34 -0
  72. data/lib/sass/tree/while_node.rb +31 -0
  73. data/rails/init.rb +1 -0
  74. data/test/benchmark.rb +99 -0
  75. data/test/haml/engine_test.rb +1129 -0
  76. data/test/haml/helper_test.rb +282 -0
  77. data/test/haml/html2haml_test.rb +258 -0
  78. data/test/haml/markaby/standard.mab +52 -0
  79. data/test/haml/mocks/article.rb +6 -0
  80. data/test/haml/results/content_for_layout.xhtml +12 -0
  81. data/test/haml/results/eval_suppressed.xhtml +9 -0
  82. data/test/haml/results/filters.xhtml +62 -0
  83. data/test/haml/results/helpers.xhtml +93 -0
  84. data/test/haml/results/helpful.xhtml +10 -0
  85. data/test/haml/results/just_stuff.xhtml +68 -0
  86. data/test/haml/results/list.xhtml +12 -0
  87. data/test/haml/results/nuke_inner_whitespace.xhtml +40 -0
  88. data/test/haml/results/nuke_outer_whitespace.xhtml +148 -0
  89. data/test/haml/results/original_engine.xhtml +20 -0
  90. data/test/haml/results/partial_layout.xhtml +5 -0
  91. data/test/haml/results/partials.xhtml +21 -0
  92. data/test/haml/results/render_layout.xhtml +3 -0
  93. data/test/haml/results/silent_script.xhtml +74 -0
  94. data/test/haml/results/standard.xhtml +162 -0
  95. data/test/haml/results/tag_parsing.xhtml +23 -0
  96. data/test/haml/results/very_basic.xhtml +5 -0
  97. data/test/haml/results/whitespace_handling.xhtml +89 -0
  98. data/test/haml/rhtml/_av_partial_1.rhtml +12 -0
  99. data/test/haml/rhtml/_av_partial_2.rhtml +8 -0
  100. data/test/haml/rhtml/action_view.rhtml +62 -0
  101. data/test/haml/rhtml/standard.rhtml +54 -0
  102. data/test/haml/spec_test.rb +44 -0
  103. data/test/haml/template_test.rb +217 -0
  104. data/test/haml/templates/_av_partial_1.haml +9 -0
  105. data/test/haml/templates/_av_partial_1_ugly.haml +9 -0
  106. data/test/haml/templates/_av_partial_2.haml +5 -0
  107. data/test/haml/templates/_av_partial_2_ugly.haml +5 -0
  108. data/test/haml/templates/_layout.erb +3 -0
  109. data/test/haml/templates/_layout_for_partial.haml +3 -0
  110. data/test/haml/templates/_partial.haml +8 -0
  111. data/test/haml/templates/_text_area.haml +3 -0
  112. data/test/haml/templates/action_view.haml +47 -0
  113. data/test/haml/templates/action_view_ugly.haml +47 -0
  114. data/test/haml/templates/breakage.haml +8 -0
  115. data/test/haml/templates/content_for_layout.haml +8 -0
  116. data/test/haml/templates/eval_suppressed.haml +11 -0
  117. data/test/haml/templates/filters.haml +66 -0
  118. data/test/haml/templates/helpers.haml +95 -0
  119. data/test/haml/templates/helpful.haml +11 -0
  120. data/test/haml/templates/just_stuff.haml +83 -0
  121. data/test/haml/templates/list.haml +12 -0
  122. data/test/haml/templates/nuke_inner_whitespace.haml +32 -0
  123. data/test/haml/templates/nuke_outer_whitespace.haml +144 -0
  124. data/test/haml/templates/original_engine.haml +17 -0
  125. data/test/haml/templates/partial_layout.haml +3 -0
  126. data/test/haml/templates/partialize.haml +1 -0
  127. data/test/haml/templates/partials.haml +12 -0
  128. data/test/haml/templates/render_layout.haml +2 -0
  129. data/test/haml/templates/silent_script.haml +40 -0
  130. data/test/haml/templates/standard.haml +42 -0
  131. data/test/haml/templates/standard_ugly.haml +42 -0
  132. data/test/haml/templates/tag_parsing.haml +21 -0
  133. data/test/haml/templates/very_basic.haml +4 -0
  134. data/test/haml/templates/whitespace_handling.haml +87 -0
  135. data/test/haml/util_test.rb +92 -0
  136. data/test/linked_rails.rb +12 -0
  137. data/test/sass/css2sass_test.rb +294 -0
  138. data/test/sass/engine_test.rb +956 -0
  139. data/test/sass/functions_test.rb +126 -0
  140. data/test/sass/more_results/more1.css +9 -0
  141. data/test/sass/more_results/more1_with_line_comments.css +26 -0
  142. data/test/sass/more_results/more_import.css +29 -0
  143. data/test/sass/more_templates/_more_partial.sass +2 -0
  144. data/test/sass/more_templates/more1.sass +23 -0
  145. data/test/sass/more_templates/more_import.sass +11 -0
  146. data/test/sass/plugin_test.rb +229 -0
  147. data/test/sass/results/alt.css +4 -0
  148. data/test/sass/results/basic.css +9 -0
  149. data/test/sass/results/compact.css +5 -0
  150. data/test/sass/results/complex.css +87 -0
  151. data/test/sass/results/compressed.css +1 -0
  152. data/test/sass/results/expanded.css +19 -0
  153. data/test/sass/results/import.css +29 -0
  154. data/test/sass/results/line_numbers.css +49 -0
  155. data/test/sass/results/mixins.css +95 -0
  156. data/test/sass/results/multiline.css +24 -0
  157. data/test/sass/results/nested.css +22 -0
  158. data/test/sass/results/parent_ref.css +13 -0
  159. data/test/sass/results/script.css +16 -0
  160. data/test/sass/results/subdir/nested_subdir/nested_subdir.css +1 -0
  161. data/test/sass/results/subdir/subdir.css +3 -0
  162. data/test/sass/results/units.css +11 -0
  163. data/test/sass/script_test.rb +261 -0
  164. data/test/sass/templates/_partial.sass +2 -0
  165. data/test/sass/templates/alt.sass +16 -0
  166. data/test/sass/templates/basic.sass +23 -0
  167. data/test/sass/templates/bork1.sass +2 -0
  168. data/test/sass/templates/bork2.sass +2 -0
  169. data/test/sass/templates/bork3.sass +2 -0
  170. data/test/sass/templates/compact.sass +17 -0
  171. data/test/sass/templates/complex.sass +307 -0
  172. data/test/sass/templates/compressed.sass +15 -0
  173. data/test/sass/templates/expanded.sass +17 -0
  174. data/test/sass/templates/import.sass +11 -0
  175. data/test/sass/templates/importee.sass +19 -0
  176. data/test/sass/templates/line_numbers.sass +13 -0
  177. data/test/sass/templates/mixins.sass +76 -0
  178. data/test/sass/templates/multiline.sass +20 -0
  179. data/test/sass/templates/nested.sass +25 -0
  180. data/test/sass/templates/nested_bork1.sass +2 -0
  181. data/test/sass/templates/nested_bork2.sass +2 -0
  182. data/test/sass/templates/nested_bork3.sass +2 -0
  183. data/test/sass/templates/parent_ref.sass +25 -0
  184. data/test/sass/templates/script.sass +101 -0
  185. data/test/sass/templates/subdir/nested_subdir/_nested_partial.sass +2 -0
  186. data/test/sass/templates/subdir/nested_subdir/nested_subdir.sass +3 -0
  187. data/test/sass/templates/subdir/subdir.sass +6 -0
  188. data/test/sass/templates/units.sass +11 -0
  189. data/test/test_helper.rb +44 -0
  190. metadata +298 -0
@@ -0,0 +1,40 @@
1
+ require 'haml/helpers/action_view_mods'
2
+
3
+ module Haml
4
+ module Helpers
5
+ # This module contains various useful helper methods
6
+ # that either tie into ActionView or the rest of the ActionPack stack,
7
+ # or are only useful in that context.
8
+ # Thus, the methods defined here are only available
9
+ # if ActionView is installed.
10
+ module ActionViewExtensions
11
+ # Returns a value for the "class" attribute
12
+ # unique to this controller/action pair.
13
+ # This can be used to target styles specifically at this action or controller.
14
+ # For example, if the current action were `EntryController#show`,
15
+ #
16
+ # %div{:class => page_class} My Div
17
+ #
18
+ # would become
19
+ #
20
+ # <div class="entry show">My Div</div>
21
+ #
22
+ # Then, in a stylesheet (shown here as {Sass}),
23
+ # you could refer to this specific action:
24
+ #
25
+ # .entry.show
26
+ # font-weight: bold
27
+ #
28
+ # or to all actions in the entry controller:
29
+ #
30
+ # .entry
31
+ # color: #00f
32
+ #
33
+ # @return [String] The class name for the current page
34
+ def page_class
35
+ controller.controller_name + " " + controller.action_name
36
+ end
37
+ alias_method :generate_content_class_names, :page_class
38
+ end
39
+ end
40
+ end
@@ -0,0 +1,176 @@
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,
7
+ # it concats rather than returning a string
8
+ # so we need it to keep thinking it's Haml
9
+ # until it hits the sub-render
10
+ if is_haml? && !(options.is_a?(Hash) && options[:layout] && block_given?)
11
+ return non_haml { render_without_haml(*args, &block) }
12
+ end
13
+ render_without_haml(*args, &block)
14
+ end
15
+ alias_method :render_without_haml, :render
16
+ alias_method :render, :render_with_haml
17
+
18
+ # Rails >2.1
19
+ if Haml::Util.has?(:instance_method, self, :output_buffer)
20
+ def output_buffer_with_haml
21
+ return haml_buffer.buffer if is_haml?
22
+ output_buffer_without_haml
23
+ end
24
+ alias_method :output_buffer_without_haml, :output_buffer
25
+ alias_method :output_buffer, :output_buffer_with_haml
26
+
27
+ def set_output_buffer_with_haml(new)
28
+ if is_haml?
29
+ haml_buffer.buffer = new
30
+ else
31
+ set_output_buffer_without_haml new
32
+ end
33
+ end
34
+ alias_method :set_output_buffer_without_haml, :output_buffer=
35
+ alias_method :output_buffer=, :set_output_buffer_with_haml
36
+ end
37
+ end
38
+
39
+ module Helpers
40
+ # In Rails <=2.1, we've got to override considerable capturing infrastructure.
41
+ # In Rails >2.1, we can make do with only overriding #capture
42
+ # (which no longer behaves differently in helper contexts).
43
+ unless Haml::Util.has?(:instance_method, ActionView::Base, :output_buffer)
44
+ module CaptureHelper
45
+ def capture_with_haml(*args, &block)
46
+ # Rails' #capture helper will just return the value of the block
47
+ # if it's not actually in the template context,
48
+ # as detected by the existance of an _erbout variable.
49
+ # We've got to do the same thing for compatibility.
50
+
51
+ if is_haml? && block_is_haml?(block)
52
+ capture_haml(*args, &block)
53
+ else
54
+ capture_without_haml(*args, &block)
55
+ end
56
+ end
57
+ alias_method :capture_without_haml, :capture
58
+ alias_method :capture, :capture_with_haml
59
+
60
+ def capture_erb_with_buffer_with_haml(buffer, *args, &block)
61
+ if is_haml?
62
+ capture_haml(*args, &block)
63
+ else
64
+ capture_erb_with_buffer_without_haml(buffer, *args, &block)
65
+ end
66
+ end
67
+ alias_method :capture_erb_with_buffer_without_haml, :capture_erb_with_buffer
68
+ alias_method :capture_erb_with_buffer, :capture_erb_with_buffer_with_haml
69
+ end
70
+
71
+ module TextHelper
72
+ def concat_with_haml(string, binding = nil)
73
+ if is_haml?
74
+ haml_buffer.buffer.concat(string)
75
+ else
76
+ concat_without_haml(string, binding)
77
+ end
78
+ end
79
+ alias_method :concat_without_haml, :concat
80
+ alias_method :concat, :concat_with_haml
81
+ end
82
+ else
83
+ module CaptureHelper
84
+ def capture_with_haml(*args, &block)
85
+ if Haml::Helpers.block_is_haml?(block)
86
+ capture_haml(*args, &block)
87
+ else
88
+ capture_without_haml(*args, &block)
89
+ end
90
+ end
91
+ alias_method :capture_without_haml, :capture
92
+ alias_method :capture, :capture_with_haml
93
+ end
94
+ end
95
+
96
+ module TagHelper
97
+ def content_tag_with_haml(name, *args, &block)
98
+ return content_tag_without_haml(name, *args, &block) unless is_haml?
99
+
100
+ preserve = haml_buffer.options[:preserve].include?(name.to_s)
101
+
102
+ if block_given? && block_is_haml?(block) && preserve
103
+ return content_tag_without_haml(name, *args) {preserve(&block)}
104
+ end
105
+
106
+ returning content_tag_without_haml(name, *args, &block) do |content|
107
+ return Haml::Helpers.preserve(content) if preserve && content
108
+ end
109
+ end
110
+
111
+ alias_method :content_tag_without_haml, :content_tag
112
+ alias_method :content_tag, :content_tag_with_haml
113
+ end
114
+
115
+ class InstanceTag
116
+ # Includes TagHelper
117
+
118
+ def haml_buffer
119
+ @template_object.send :haml_buffer
120
+ end
121
+
122
+ def is_haml?
123
+ @template_object.send :is_haml?
124
+ end
125
+
126
+ unless defined?(ActionView::Helpers::ActiveRecordInstanceTag)
127
+ alias_method :content_tag_without_haml, :content_tag
128
+ alias_method :content_tag, :content_tag_with_haml
129
+ end
130
+ end
131
+
132
+ module FormTagHelper
133
+ def form_tag_with_haml(url_for_options = {}, options = {}, *parameters_for_url, &proc)
134
+ if is_haml?
135
+ if block_given?
136
+ oldproc = proc
137
+ proc = haml_bind_proc do |*args|
138
+ concat "\n"
139
+ tab_up
140
+ oldproc.call(*args)
141
+ tab_down
142
+ concat haml_indent
143
+ end
144
+ concat haml_indent
145
+ end
146
+ res = form_tag_without_haml(url_for_options, options, *parameters_for_url, &proc) + "\n"
147
+ concat "\n" if block_given?
148
+ res
149
+ else
150
+ form_tag_without_haml(url_for_options, options, *parameters_for_url, &proc)
151
+ end
152
+ end
153
+ alias_method :form_tag_without_haml, :form_tag
154
+ alias_method :form_tag, :form_tag_with_haml
155
+ end
156
+
157
+ module FormHelper
158
+ def form_for_with_haml(object_name, *args, &proc)
159
+ if block_given? && is_haml?
160
+ oldproc = proc
161
+ proc = haml_bind_proc do |*args|
162
+ tab_up
163
+ oldproc.call(*args)
164
+ tab_down
165
+ concat haml_indent
166
+ end
167
+ concat haml_indent
168
+ end
169
+ form_for_without_haml(object_name, *args, &proc)
170
+ concat "\n" if block_given? && is_haml?
171
+ end
172
+ alias_method :form_for_without_haml, :form_for
173
+ alias_method :form_for, :form_for_with_haml
174
+ end
175
+ end
176
+ end
@@ -0,0 +1,96 @@
1
+ # Modified version of ERB specifically used to help convert ERB templates to Haml
2
+ class ERB
3
+ class HamlCompiler < Compiler # :nodoc:
4
+ class Buffer # :nodoc:
5
+ def initialize(compiler, delimiter)
6
+ @compiler = compiler
7
+ @line = []
8
+ @script = ""
9
+ @delimiter = delimiter
10
+ @compiler.pre_cmd.each do |x|
11
+ push(x)
12
+ end
13
+ end
14
+ attr_reader :script
15
+
16
+ def push(cmd)
17
+ @line << cmd
18
+ end
19
+
20
+ def cr
21
+ @script << (@line.join(@delimiter))
22
+ @line = []
23
+ @script << @delimiter
24
+ end
25
+
26
+ def close
27
+ return unless @line
28
+ @compiler.post_cmd.each do |x|
29
+ push(x)
30
+ end
31
+ @script << (@line.join(@delimiter))
32
+ @line = nil
33
+ end
34
+ end
35
+
36
+ def compile(s, delimiter = '; ')
37
+ out = Buffer.new(self, delimiter)
38
+
39
+ content = ''
40
+ scanner = make_scanner(s)
41
+ scanner.scan do |token|
42
+ if scanner.stag.nil?
43
+ case token
44
+ when PercentLine
45
+ out.push("#{@put_cmd} #{content.dump}") if content.size > 0
46
+ content = ''
47
+ out.push(token.to_s)
48
+ out.cr
49
+ when :cr
50
+ out.cr
51
+ when '<%', '<%=', '<%#'
52
+ scanner.stag = token
53
+ out.push("#{@put_cmd} #{content.dump}") if content.size > 0
54
+ content = ''
55
+ when "\n"
56
+ content << "\n"
57
+ out.push("#{@put_cmd} #{content.dump}")
58
+ out.cr
59
+ content = ''
60
+ when '<%%'
61
+ content << '<%'
62
+ else
63
+ content << token
64
+ end
65
+ else
66
+ case token
67
+ when '%>'
68
+ case scanner.stag
69
+ when '<%'
70
+ if content[-1] == ?\n
71
+ content.chop!
72
+ out.push(content)
73
+ out.cr
74
+ else
75
+ out.push(content)
76
+ end
77
+ when '<%='
78
+ out.push("#{@insert_cmd}((#{content}).to_s)")
79
+ when '<%#'
80
+ # out.push("# #{content.dump}")
81
+ end
82
+ scanner.stag = nil
83
+ content = ''
84
+ when '%%>'
85
+ content << '%>'
86
+ else
87
+ content << token
88
+ end
89
+ end
90
+ end
91
+ out.push("#{@put_cmd} #{content.dump}") if content.size > 0
92
+ out.close
93
+ out.script
94
+ end
95
+ end
96
+ end
@@ -0,0 +1,308 @@
1
+ require File.dirname(__FILE__) + '/../haml'
2
+
3
+ require 'haml/engine'
4
+ require 'rubygems'
5
+ require 'cgi'
6
+ require 'haml/herb'
7
+ require 'parse_tree'
8
+
9
+ module Haml
10
+ class HTML
11
+ # A module containing utility methods that every Hpricot node
12
+ # should have.
13
+ module Node
14
+ # Returns the Haml representation of the given node.
15
+ #
16
+ # @param tabs [Fixnum] The indentation level of the resulting Haml.
17
+ # @option options (see Haml::HTML#initialize)
18
+ def to_haml(tabs, options)
19
+ parse_text(self.to_s, tabs)
20
+ end
21
+
22
+ private
23
+
24
+ def tabulate(tabs)
25
+ ' ' * tabs
26
+ end
27
+
28
+ def parse_text(text, tabs)
29
+ text.strip!
30
+ if text.empty?
31
+ String.new
32
+ else
33
+ lines = text.split("\n")
34
+
35
+ lines.map do |line|
36
+ line.strip!
37
+ "#{tabulate(tabs)}#{'\\' if Haml::Engine::SPECIAL_CHARACTERS.include?(line[0])}#{line}\n"
38
+ end.join
39
+ end
40
+ end
41
+ end
42
+ end
43
+ end
44
+
45
+ # Haml monkeypatches various Hpricot classes
46
+ # to add methods for conversion to Haml.
47
+ module Hpricot
48
+ # @see Hpricot
49
+ module Node
50
+ include Haml::HTML::Node
51
+ end
52
+
53
+ # @see Hpricot
54
+ class BaseEle
55
+ include Haml::HTML::Node
56
+ end
57
+ end
58
+
59
+ require 'hpricot'
60
+
61
+ module Haml
62
+ # Converts HTML documents into Haml templates.
63
+ # Depends on [Hpricot](http://code.whytheluckystiff.net/hpricot/) for HTML parsing.
64
+ #
65
+ # Example usage:
66
+ #
67
+ # Haml::Engine.new("<a href='http://google.com'>Blat</a>").render
68
+ # #=> "%a{:href => 'http://google.com'} Blat"
69
+ class HTML
70
+ # @param template [String, Hpricot::Node] The HTML template to convert
71
+ # @option options :rhtml [Boolean] (false) Whether or not to parse
72
+ # ERB's `<%= %>` and `<% %>` into Haml's `=` and `-`
73
+ # @option options :xhtml [Boolean] (false) Whether or not to parse
74
+ # the HTML strictly as XHTML
75
+ def initialize(template, options = {})
76
+ @options = options
77
+
78
+ if template.is_a? Hpricot::Node
79
+ @template = template
80
+ else
81
+ if template.is_a? IO
82
+ template = template.read
83
+ end
84
+
85
+ Haml::Util.check_encoding(template) {|msg, line| raise Haml::Error.new(msg, line)}
86
+
87
+ if @options[:rhtml]
88
+ template = convert_to_hamlified_markup(template)
89
+ end
90
+
91
+ method = @options[:xhtml] ? Hpricot.method(:XML) : method(:Hpricot)
92
+ @template = method.call(template.gsub('&', '&amp;'))
93
+ end
94
+ end
95
+
96
+ # Processes the document and returns the result as a string
97
+ # containing the Haml template.
98
+ def render
99
+ @template.to_haml(0, @options)
100
+ end
101
+ alias_method :to_haml, :render
102
+
103
+ TEXT_REGEXP = /^(\s*).*$/
104
+
105
+ # @see Hpricot
106
+ class ::Hpricot::Doc
107
+ # @see Haml::HTML::Node#to_haml
108
+ def to_haml(tabs, options)
109
+ (children || []).inject('') {|s, c| s << c.to_haml(0, options)}
110
+ end
111
+ end
112
+
113
+ # @see Hpricot
114
+ class ::Hpricot::XMLDecl
115
+ # @see Haml::HTML::Node#to_haml
116
+ def to_haml(tabs, options)
117
+ "#{tabulate(tabs)}!!! XML\n"
118
+ end
119
+ end
120
+
121
+ # @see Hpricot
122
+ class ::Hpricot::CData
123
+ # @see Haml::HTML::Node#to_haml
124
+ def to_haml(tabs, options)
125
+ "#{tabulate(tabs)}:cdata\n#{parse_text(self.content, tabs + 1)}"
126
+ end
127
+ end
128
+
129
+ # @see Hpricot
130
+ class ::Hpricot::DocType
131
+ # @see Haml::HTML::Node#to_haml
132
+ def to_haml(tabs, options)
133
+ attrs = public_id.scan(/DTD\s+([^\s]+)\s*([^\s]*)\s*([^\s]*)\s*\/\//)[0]
134
+ raise Haml::SyntaxError.new("Invalid doctype") if attrs == nil
135
+
136
+ type, version, strictness = attrs.map { |a| a.downcase }
137
+ if type == "html"
138
+ version = "1.0"
139
+ strictness = "transitional"
140
+ end
141
+
142
+ if version == "1.0" || version.empty?
143
+ version = nil
144
+ end
145
+
146
+ if strictness == 'transitional' || strictness.empty?
147
+ strictness = nil
148
+ end
149
+
150
+ version = " #{version}" if version
151
+ if strictness
152
+ strictness[0] = strictness[0] - 32
153
+ strictness = " #{strictness}"
154
+ end
155
+
156
+ "#{tabulate(tabs)}!!!#{version}#{strictness}\n"
157
+ end
158
+ end
159
+
160
+ # @see Hpricot
161
+ class ::Hpricot::Comment
162
+ # @see Haml::HTML::Node#to_haml
163
+ def to_haml(tabs, options)
164
+ "#{tabulate(tabs)}/\n#{parse_text(self.content, tabs + 1)}"
165
+ end
166
+ end
167
+
168
+ # @see Hpricot
169
+ class ::Hpricot::Elem
170
+ # @see Haml::HTML::Node#to_haml
171
+ def to_haml(tabs, options)
172
+ output = tab_prefix = "#{tabulate(tabs)}"
173
+ if options[:rhtml] && name[0...5] == 'haml:'
174
+ output = (self.children || []).inject("") do |out, child|
175
+ if child.text?
176
+ text = CGI.unescapeHTML(child.inner_text).strip
177
+ text.gsub!(/(^[- ]+)|([- ]+$)/, '')
178
+ next out if text.empty?
179
+ out + tab_prefix + send("haml_tag_#{name[5..-1]}", text, tab_prefix)
180
+ elsif child.name[0...10] == 'haml:block'
181
+ out + child.to_haml(tabs + 1, options)
182
+ elsif child.name[0...5] == 'haml:'
183
+ out + child.to_haml(tabs, options)
184
+ else
185
+ out + child.to_haml(tabs, options)
186
+ end
187
+ end
188
+ return output
189
+ end
190
+
191
+ output += "%#{name}" unless name == 'div' &&
192
+ (static_id?(options) || static_classname?(options))
193
+
194
+ if attributes
195
+ if static_id?(options)
196
+ output += "##{attributes['id']}"
197
+ remove_attribute('id')
198
+ end
199
+ if static_classname?(options)
200
+ attributes['class'].split(' ').each { |c| output += ".#{c}" }
201
+ remove_attribute('class')
202
+ end
203
+ output += haml_attributes(options) if attributes.length > 0
204
+ end
205
+
206
+ (self.children || []).inject(output + "\n") do |output, child|
207
+ output + child.to_haml(tabs + 1, options)
208
+ end
209
+ end
210
+
211
+ private
212
+
213
+ def dynamic_attributes
214
+ @dynamic_attributes ||= begin
215
+ Haml::Util.map_hash(attributes) do |name, value|
216
+ next if value.empty?
217
+ full_match = nil
218
+ ruby_value = value.gsub(%r{<haml:loud>\s*(.+?)\s*</haml:loud>}) do
219
+ full_match = $`.empty? && $'.empty?
220
+ full_match ? $1: "\#{#{$1}}"
221
+ end
222
+ next if ruby_value == value
223
+ [name, full_match ? ruby_value : %("#{ruby_value}")]
224
+ end
225
+ end
226
+ end
227
+
228
+ def haml_tag_loud(text, tab_prefix = '')
229
+ if text =~ /\n/
230
+ lines = text.strip.split(/\n+/)
231
+ pad_size = lines.map { |line| line.length }.max + 1
232
+ out = lines.map { |line| tab_prefix + " " + line.ljust(pad_size) + '|' }.join("\n")
233
+ out[0...4] = "= "
234
+ out + "\n"
235
+ else
236
+ "= #{text.gsub(/\n\s*/, ' ').strip}\n"
237
+ end
238
+ end
239
+
240
+ def haml_tag_silent(text, tab_prefix = '')
241
+ text.strip.split("\n").map { |line| "- #{line.strip}" }.join("\n#{tab_prefix}") + "\n"
242
+ end
243
+
244
+ def haml_tag_block(text, tab_prefix = '')
245
+ "#{text.strip}\n"
246
+ end
247
+
248
+ def static_attribute?(name, options)
249
+ attributes[name] and !dynamic_attribute?(name, options)
250
+ end
251
+
252
+ def dynamic_attribute?(name, options)
253
+ options[:rhtml] and dynamic_attributes.key?(name)
254
+ end
255
+
256
+ def static_id?(options)
257
+ static_attribute?('id', options)
258
+ end
259
+
260
+ def static_classname?(options)
261
+ static_attribute?('class', options)
262
+ end
263
+
264
+ # Returns a string representation of an attributes hash
265
+ # that's prettier than that produced by Hash#inspect
266
+ def haml_attributes(options)
267
+ attrs = attributes.map do |name, value|
268
+ value = dynamic_attribute?(name, options) ? dynamic_attributes[name] : value.inspect
269
+ name = name.index(/\W/) ? name.inspect : ":#{name}"
270
+ "#{name} => #{value}"
271
+ end
272
+ "{ #{attrs.join(', ')} }"
273
+ end
274
+ end
275
+
276
+ private
277
+
278
+ TOKEN_DELIMITER = '__oHg5SJYRHA0__'
279
+
280
+ def convert_to_hamlified_markup(string)
281
+ output = ''
282
+ compiler = ERB::HamlCompiler.new(nil)
283
+ compiler.insert_cmd = compiler.put_cmd = '_erbout.concat'
284
+
285
+ lines = compiler.compile(string, TOKEN_DELIMITER).split(TOKEN_DELIMITER)
286
+ lines.each do |code|
287
+ code.gsub!(/(^[- ]+)|([- ]+$)/, '')
288
+ output << if code[0...15] == '_erbout.concat '
289
+ code[0...15] = ''
290
+ eval(code)
291
+ elsif code =~ /^_erbout\.concat\(\((.*)\)\.to_s\)$/m
292
+ "<haml:loud>#{$1}</haml:loud>"
293
+ elsif code == 'end'
294
+ "</haml:block></haml:silent>"
295
+ else
296
+ begin
297
+ ParseTree.translate(code)
298
+ "<haml:silent>#{code}</haml:silent>"
299
+ rescue Exception
300
+ "<haml:silent>#{code}<haml:block>"
301
+ end
302
+ end
303
+ end
304
+ output
305
+ end
306
+
307
+ end
308
+ end