haml 1.5.2 → 1.7.0
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.
- data/MIT-LICENSE +1 -1
- data/Rakefile +1 -0
- data/VERSION +1 -1
- data/bin/css2sass +7 -0
- data/bin/html2haml +0 -82
- data/lib/haml.rb +43 -6
- data/lib/haml/buffer.rb +81 -72
- data/lib/haml/engine.rb +240 -110
- data/lib/haml/exec.rb +120 -5
- data/lib/haml/helpers.rb +88 -3
- data/lib/haml/helpers/action_view_extensions.rb +45 -0
- data/lib/haml/helpers/action_view_mods.rb +30 -17
- data/lib/haml/html.rb +173 -0
- data/lib/haml/template.rb +1 -26
- data/lib/haml/util.rb +18 -0
- data/lib/sass.rb +181 -3
- data/lib/sass/constant.rb +38 -9
- data/lib/sass/constant/color.rb +25 -1
- data/lib/sass/constant/literal.rb +10 -8
- data/lib/sass/css.rb +197 -0
- data/lib/sass/engine.rb +239 -68
- data/lib/sass/error.rb +2 -2
- data/lib/sass/plugin.rb +11 -3
- data/lib/sass/tree/attr_node.rb +25 -17
- data/lib/sass/tree/comment_node.rb +14 -0
- data/lib/sass/tree/node.rb +18 -1
- data/lib/sass/tree/rule_node.rb +17 -5
- data/lib/sass/tree/value_node.rb +4 -0
- data/test/haml/engine_test.rb +42 -25
- data/test/haml/helper_test.rb +28 -3
- data/test/haml/results/eval_suppressed.xhtml +6 -0
- data/test/haml/results/helpers.xhtml +26 -2
- data/test/haml/results/helpful.xhtml +2 -0
- data/test/haml/results/just_stuff.xhtml +17 -2
- data/test/haml/results/standard.xhtml +1 -1
- data/test/haml/results/whitespace_handling.xhtml +1 -11
- data/test/haml/rhtml/standard.rhtml +1 -1
- data/test/haml/template_test.rb +7 -2
- data/test/haml/templates/eval_suppressed.haml +7 -2
- data/test/haml/templates/helpers.haml +16 -1
- data/test/haml/templates/helpful.haml +2 -0
- data/test/haml/templates/just_stuff.haml +23 -4
- data/test/haml/templates/standard.haml +3 -3
- data/test/haml/templates/whitespace_handling.haml +0 -50
- data/test/sass/engine_test.rb +35 -10
- data/test/sass/plugin_test.rb +10 -6
- data/test/sass/results/alt.css +4 -0
- data/test/sass/results/complex.css +4 -3
- data/test/sass/results/constants.css +3 -3
- data/test/sass/results/import.css +27 -0
- data/test/sass/results/nested.css +7 -0
- data/test/sass/results/parent_ref.css +13 -0
- data/test/sass/results/subdir/nested_subdir/nested_subdir.css +1 -0
- data/test/sass/results/subdir/subdir.css +1 -0
- data/test/sass/templates/alt.sass +16 -0
- data/test/sass/templates/bork2.sass +2 -0
- data/test/sass/templates/complex.sass +19 -1
- data/test/sass/templates/constants.sass +8 -0
- data/test/sass/templates/import.sass +8 -0
- data/test/sass/templates/importee.sass +10 -0
- data/test/sass/templates/nested.sass +8 -0
- data/test/sass/templates/parent_ref.sass +25 -0
- data/test/sass/templates/subdir/nested_subdir/nested_subdir.sass +3 -0
- data/test/sass/templates/subdir/subdir.sass +6 -0
- metadata +95 -75
- data/test/haml/results/semantic.cache +0 -15
data/lib/haml/template.rb
CHANGED
@@ -40,7 +40,6 @@ module Haml
|
|
40
40
|
# to render its templates.
|
41
41
|
def initialize(view)
|
42
42
|
@view = view
|
43
|
-
@@precompiled_templates ||= {}
|
44
43
|
end
|
45
44
|
|
46
45
|
# Renders the file at the location <tt>template</tt>,
|
@@ -60,13 +59,7 @@ module Haml
|
|
60
59
|
engine = Haml::Engine.new(template, options)
|
61
60
|
else
|
62
61
|
options[:filename] ||= template
|
63
|
-
|
64
|
-
options[:precompiled] ||= @precompiled
|
65
|
-
engine = Haml::Engine.new("", options)
|
66
|
-
else
|
67
|
-
engine = Haml::Engine.new(File.read(template), options)
|
68
|
-
set_precompiled(template, engine.precompiled)
|
69
|
-
end
|
62
|
+
engine = Haml::Engine.new(File.read(template), options)
|
70
63
|
end
|
71
64
|
|
72
65
|
yield_proc = @view.instance_eval do
|
@@ -76,24 +69,6 @@ module Haml
|
|
76
69
|
engine.to_html(@view) { |*args| yield_proc.call(*args) }
|
77
70
|
|
78
71
|
end
|
79
|
-
|
80
|
-
private
|
81
|
-
|
82
|
-
# Gets the cached, precompiled version of the template at location <tt>filename</tt>
|
83
|
-
# as a string.
|
84
|
-
def get_precompiled(filename)
|
85
|
-
# Do we have it on file? Is it new enough?
|
86
|
-
if (precompiled, precompiled_on = @@precompiled_templates[filename]) &&
|
87
|
-
(precompiled_on == File.mtime(filename).to_i)
|
88
|
-
precompiled
|
89
|
-
end
|
90
|
-
end
|
91
|
-
|
92
|
-
# Sets the cached, precompiled version of the template at location <tt>filename</tt>
|
93
|
-
# to <tt>precompiled</tt>.
|
94
|
-
def set_precompiled(filename, precompiled)
|
95
|
-
@@precompiled_templates[filename] = [precompiled, File.mtime(filename).to_i]
|
96
|
-
end
|
97
72
|
end
|
98
73
|
end
|
99
74
|
|
data/lib/haml/util.rb
ADDED
@@ -0,0 +1,18 @@
|
|
1
|
+
# This file contains various useful bits of code
|
2
|
+
# that are shared between Haml and Sass.
|
3
|
+
|
4
|
+
class Hash # :nodoc:
|
5
|
+
# Same as Hash#merge!,
|
6
|
+
# but recursively merges sub-hashes
|
7
|
+
def rec_merge!(other)
|
8
|
+
other.each do |key, value|
|
9
|
+
myval = self[key]
|
10
|
+
if value.is_a?(Hash) && myval.is_a?(Hash)
|
11
|
+
myval.rec_merge!(value)
|
12
|
+
else
|
13
|
+
self[key] = value
|
14
|
+
end
|
15
|
+
end
|
16
|
+
self
|
17
|
+
end
|
18
|
+
end
|
data/lib/sass.rb
CHANGED
@@ -77,12 +77,27 @@ $LOAD_PATH << dir unless $LOAD_PATH.include?(dir)
|
|
77
77
|
#
|
78
78
|
# === Attributes
|
79
79
|
#
|
80
|
-
#
|
80
|
+
# There are two different ways to write CSS attrbibutes.
|
81
|
+
# The first is very similar to the how you're used to writing them:
|
82
|
+
# with a colon between the name and the value.
|
83
|
+
# However, Sass attributes don't have semicolons at the end;
|
84
|
+
# each attribute is on its own line, so they aren't necessary.
|
85
|
+
# For example:
|
86
|
+
#
|
87
|
+
# #main p
|
88
|
+
# color: #00ff00
|
89
|
+
# width: 97%
|
90
|
+
#
|
91
|
+
# is compiled to:
|
92
|
+
#
|
93
|
+
# #main p {
|
94
|
+
# color: #00ff00;
|
95
|
+
# width: 97% }
|
96
|
+
#
|
97
|
+
# The second syntax for attributes is slightly different.
|
81
98
|
# The colon is at the beginning of the attribute,
|
82
99
|
# rather than between the name and the value,
|
83
100
|
# so it's easier to tell what elements are attributes just by glancing at them.
|
84
|
-
# Attributes also don't have semicolons at the end;
|
85
|
-
# each attribute is on its own line, so they aren't necessary.
|
86
101
|
# For example:
|
87
102
|
#
|
88
103
|
# #main p
|
@@ -142,6 +157,57 @@ $LOAD_PATH << dir unless $LOAD_PATH.include?(dir)
|
|
142
157
|
# #main pre {
|
143
158
|
# font-size: 3em; }
|
144
159
|
#
|
160
|
+
# === Referencing Parent Rules
|
161
|
+
#
|
162
|
+
# In addition to the default behavior of inserting the parent selector
|
163
|
+
# as a CSS parent of the current selector
|
164
|
+
# (e.g. above, "#main" is the parent of "p"),
|
165
|
+
# you can have more fine-grained control over what's done with the parent selector
|
166
|
+
# by using the ampersand character "&" in your selectors.
|
167
|
+
#
|
168
|
+
# The ampersand is automatically replaced by the parent selector,
|
169
|
+
# instead of having it prepended.
|
170
|
+
# This allows you to cleanly create pseudo-attributes:
|
171
|
+
#
|
172
|
+
# a
|
173
|
+
# :font-weight bold
|
174
|
+
# :text-decoration none
|
175
|
+
# &:hover
|
176
|
+
# :text-decoration underline
|
177
|
+
# &:visited
|
178
|
+
# :font-weight normal
|
179
|
+
#
|
180
|
+
# Which would become:
|
181
|
+
#
|
182
|
+
# a {
|
183
|
+
# font-weight: bold;
|
184
|
+
# text-decoration: none; }
|
185
|
+
# a:hover {
|
186
|
+
# text-decoration: underline; }
|
187
|
+
# a:visited {
|
188
|
+
# font-weight: normal; }
|
189
|
+
#
|
190
|
+
# It also allows you to add selectors at the base of the hierarchy,
|
191
|
+
# which can be useuful for targeting certain styles to certain browsers:
|
192
|
+
#
|
193
|
+
# #main
|
194
|
+
# :width 90%
|
195
|
+
# #sidebar
|
196
|
+
# :float left
|
197
|
+
# :margin-left 20%
|
198
|
+
# .ie6 &
|
199
|
+
# :margin-left 40%
|
200
|
+
#
|
201
|
+
# Which would become:
|
202
|
+
#
|
203
|
+
# #main {
|
204
|
+
# width: 90%; }
|
205
|
+
# #main #sidebar {
|
206
|
+
# float: left;
|
207
|
+
# margin-left: 20%; }
|
208
|
+
# .ie6 #main #sidebar {
|
209
|
+
# margin-left: 40%; }
|
210
|
+
#
|
145
211
|
# === Attribute Namespaces
|
146
212
|
#
|
147
213
|
# CSS has quite a few attributes that are in "namespaces;"
|
@@ -232,6 +298,10 @@ $LOAD_PATH << dir unless $LOAD_PATH.include?(dir)
|
|
232
298
|
#
|
233
299
|
# === Colors
|
234
300
|
#
|
301
|
+
# Colors may be written as three- or six-digit hex numbers prefixed
|
302
|
+
# by a pound sign (#), or as HTML4 color names. For example,
|
303
|
+
# "#ff0", "#ffff00" and "yellow" all refer to the same color.
|
304
|
+
#
|
235
305
|
# Not only can arithmetic be done between colors and other colors,
|
236
306
|
# but it can be done between colors and normal numbers.
|
237
307
|
# In this case, the operation is done piecewise one each of the
|
@@ -301,6 +371,108 @@ $LOAD_PATH << dir unless $LOAD_PATH.include?(dir)
|
|
301
371
|
# #main h6 {
|
302
372
|
# font: italic small-caps bold 1.1em sans-serif; }
|
303
373
|
#
|
374
|
+
# == Directives
|
375
|
+
#
|
376
|
+
# Directives allow the author to directly issue instructions to the Sass compiler.
|
377
|
+
# They're prefixed with an at sign, "<tt>@</tt>",
|
378
|
+
# followed by the name of the directive,
|
379
|
+
# a space, and any arguments to it -
|
380
|
+
# just like CSS directives.
|
381
|
+
# For example:
|
382
|
+
#
|
383
|
+
# @import red.sass
|
384
|
+
#
|
385
|
+
# === Import
|
386
|
+
#
|
387
|
+
# Currently, the only directive is the "import" directive.
|
388
|
+
# It works in a very similar way to the CSS import directive,
|
389
|
+
# and sometimes compiles to a literal CSS "@import".
|
390
|
+
#
|
391
|
+
# Sass can import either other Sass files or plain CSS files.
|
392
|
+
# If it imports a Sass file,
|
393
|
+
# not only are the rules from that file included,
|
394
|
+
# but all constants in that file are made available in the current file.
|
395
|
+
#
|
396
|
+
# Sass looks for other Sass files in the working directory,
|
397
|
+
# and the Sass file directory under Rails.
|
398
|
+
# Additional search directories may be specified
|
399
|
+
# using the :load_paths option (see below).
|
400
|
+
#
|
401
|
+
# Sass can also import plain CSS files.
|
402
|
+
# In this case, it doesn't literally include the content of the files;
|
403
|
+
# rather, it uses the built-in CSS "@import" directive to tell the client program
|
404
|
+
# to import the files.
|
405
|
+
#
|
406
|
+
# The import directive can take either a full filename
|
407
|
+
# or a filename without an extension.
|
408
|
+
# If an extension isn't provided,
|
409
|
+
# Sass will try to find a Sass file with the given basename in the load paths,
|
410
|
+
# and, failing that, will assume a relevant CSS file will be available.
|
411
|
+
#
|
412
|
+
# For example,
|
413
|
+
#
|
414
|
+
# @import foo.sass
|
415
|
+
#
|
416
|
+
# would compile to
|
417
|
+
#
|
418
|
+
# .foo
|
419
|
+
# :color #f00
|
420
|
+
#
|
421
|
+
# whereas
|
422
|
+
#
|
423
|
+
# @import foo.css
|
424
|
+
#
|
425
|
+
# would compile to
|
426
|
+
#
|
427
|
+
# @import foo.css
|
428
|
+
#
|
429
|
+
# Finally,
|
430
|
+
#
|
431
|
+
# @import foo
|
432
|
+
#
|
433
|
+
# might compile to either,
|
434
|
+
# depending on whether a file called "foo.sass" existed.
|
435
|
+
#
|
436
|
+
# == Comments
|
437
|
+
#
|
438
|
+
# === Silent Comments
|
439
|
+
#
|
440
|
+
# It's simple to add "silent" comments,
|
441
|
+
# which don't output anything to the CSS document,
|
442
|
+
# to a Sass document.
|
443
|
+
# Simply use the familiar C-style notation for a one-line comment, "//",
|
444
|
+
# at the normal indentation level and all text following it won't be output.
|
445
|
+
# For example:
|
446
|
+
#
|
447
|
+
# // A very awesome rule.
|
448
|
+
# #awesome.rule
|
449
|
+
# // An equally awesome attribute.
|
450
|
+
# :awesomeness very
|
451
|
+
#
|
452
|
+
# becomes
|
453
|
+
#
|
454
|
+
# #awesome.rule {
|
455
|
+
# awesomeness: very; }
|
456
|
+
#
|
457
|
+
# === Loud Comments
|
458
|
+
#
|
459
|
+
# "Loud" comments are just as easy as silent ones.
|
460
|
+
# These comments output to the document as CSS comments,
|
461
|
+
# and thus use the same opening sequence: "/*".
|
462
|
+
# For example:
|
463
|
+
#
|
464
|
+
# /* A very awesome rule.
|
465
|
+
# #awesome.rule
|
466
|
+
# /* An equally awesome attribute.
|
467
|
+
# :awesomeness very
|
468
|
+
#
|
469
|
+
# becomes
|
470
|
+
#
|
471
|
+
# /* A very awesome rule. */
|
472
|
+
# #awesome.rule {
|
473
|
+
# /* An equally awesome attribute. */
|
474
|
+
# awesomeness: very; }
|
475
|
+
#
|
304
476
|
# == Output Style
|
305
477
|
#
|
306
478
|
# Although the default CSS style that Sass outputs is very nice,
|
@@ -412,6 +584,12 @@ $LOAD_PATH << dir unless $LOAD_PATH.include?(dir)
|
|
412
584
|
# [<tt>:filename</tt>] The filename of the file being rendered.
|
413
585
|
# This is used solely for reporting errors,
|
414
586
|
# and is automatically set when using Rails.
|
587
|
+
#
|
588
|
+
# [<tt>:load_paths</tt>] An array of filesystem paths which should be searched
|
589
|
+
# for Sass templates imported with the "@import" directive.
|
590
|
+
# This defaults to the working directory and, in Rails,
|
591
|
+
# whatever <tt>:template_location</tt> is
|
592
|
+
# (by default <tt>RAILS_ROOT + "/public/stylesheets/sass"</tt>).
|
415
593
|
#
|
416
594
|
module Sass; end
|
417
595
|
|
data/lib/sass/constant.rb
CHANGED
@@ -76,9 +76,11 @@ module Sass
|
|
76
76
|
|
77
77
|
last = to_return[-1]
|
78
78
|
|
79
|
+
# Do we need to open or close a string literal?
|
79
80
|
if byte == STRING_CHAR
|
80
81
|
is_string = !is_string
|
81
82
|
|
83
|
+
# Adjacent strings should be concatenated
|
82
84
|
if is_string && last && (!last.is_a?(Symbol) || last == :close)
|
83
85
|
to_return << :concat
|
84
86
|
end
|
@@ -89,6 +91,7 @@ module Sass
|
|
89
91
|
|
90
92
|
unless is_string
|
91
93
|
|
94
|
+
# Are we looking at whitespace?
|
92
95
|
if WHITESPACE.include?(byte)
|
93
96
|
str = reset_str.call
|
94
97
|
next
|
@@ -96,13 +99,21 @@ module Sass
|
|
96
99
|
|
97
100
|
symbol = SYMBOLS[byte]
|
98
101
|
|
102
|
+
# Adjacent values without an operator should be concatenated
|
99
103
|
if (symbol.nil? || symbol == :open) &&
|
100
104
|
last && (!last.is_a?(Symbol) || last == :close)
|
101
|
-
# Two values connected without an operator
|
102
105
|
to_return << :concat
|
103
106
|
end
|
104
107
|
|
105
|
-
|
108
|
+
# Time for a unary minus!
|
109
|
+
if negative_okay && symbol == :minus
|
110
|
+
negative_okay = true
|
111
|
+
to_return << :neg
|
112
|
+
next
|
113
|
+
end
|
114
|
+
|
115
|
+
# Are we looking at an operator?
|
116
|
+
if symbol && (str.empty? || symbol != :mod)
|
106
117
|
str = reset_str.call
|
107
118
|
negative_okay = true
|
108
119
|
to_return << symbol
|
@@ -132,12 +143,25 @@ module Sass
|
|
132
143
|
beginning = i
|
133
144
|
token = value[i]
|
134
145
|
|
135
|
-
while i < value_len && token != :close
|
146
|
+
while i < value_len && token != :close
|
136
147
|
if token == :open
|
137
148
|
to_return.push(*value[beginning...i])
|
138
149
|
sub, i = parenthesize_helper(i + 1, value, value_len)
|
139
150
|
beginning = i
|
140
151
|
to_return << sub
|
152
|
+
elsif token == :neg
|
153
|
+
if value[i + 1].nil?
|
154
|
+
raise Sass::SyntaxError("Unterminated unary minus.")
|
155
|
+
elsif value[i + 1] == :open
|
156
|
+
to_return.push(*value[beginning...i])
|
157
|
+
sub, i = parenthesize_helper(i + 2, value, value_len)
|
158
|
+
beginning = i
|
159
|
+
to_return << [:neg, sub]
|
160
|
+
else
|
161
|
+
to_return.push(*value[beginning...i])
|
162
|
+
to_return << [:neg, value[i + 1]]
|
163
|
+
beginning = i = i + 2
|
164
|
+
end
|
141
165
|
else
|
142
166
|
i += 1
|
143
167
|
end
|
@@ -154,17 +178,22 @@ module Sass
|
|
154
178
|
#++
|
155
179
|
def operationalize(value, constants)
|
156
180
|
value = [value] unless value.is_a?(Array)
|
157
|
-
|
158
|
-
if length == 1
|
181
|
+
if value.length == 1
|
159
182
|
value = value[0]
|
160
|
-
if value.is_a?
|
183
|
+
if value.is_a? Array
|
184
|
+
operationalize(value, constants)
|
185
|
+
elsif value.is_a? Operation
|
161
186
|
value
|
162
187
|
else
|
163
188
|
Literal.parse(insert_constant(value, constants))
|
164
189
|
end
|
165
|
-
elsif length == 2
|
166
|
-
|
167
|
-
|
190
|
+
elsif value.length == 2
|
191
|
+
if value[0] == :neg
|
192
|
+
Operation.new(Sass::Constant::Number.new('0'), operationalize(value[1], constants), :minus)
|
193
|
+
else
|
194
|
+
raise SyntaxError.new("Constant arithmetic error")
|
195
|
+
end
|
196
|
+
elsif value.length == 3
|
168
197
|
Operation.new(operationalize(value[0], constants), operationalize(value[2], constants), value[1])
|
169
198
|
else
|
170
199
|
if SECOND_ORDER.include?(value[1]) && FIRST_ORDER.include?(value[3])
|
data/lib/sass/constant/color.rb
CHANGED
@@ -2,11 +2,35 @@ require 'sass/constant/literal'
|
|
2
2
|
|
3
3
|
module Sass::Constant # :nodoc:
|
4
4
|
class Color < Literal # :nodoc:
|
5
|
+
|
6
|
+
HTML4_COLORS = {
|
7
|
+
'black' => 0x000000,
|
8
|
+
'silver' => 0xc0c0c0,
|
9
|
+
'gray' => 0x808080,
|
10
|
+
'white' => 0xffffff,
|
11
|
+
'maroon' => 0x800000,
|
12
|
+
'red' => 0xff0000,
|
13
|
+
'purple' => 0x800080,
|
14
|
+
'fuchsia' => 0xff00ff,
|
15
|
+
'green' => 0x008000,
|
16
|
+
'lime' => 0x00ff00,
|
17
|
+
'olive' => 0x808000,
|
18
|
+
'yellow' => 0xffff00,
|
19
|
+
'navy' => 0x000080,
|
20
|
+
'blue' => 0x0000ff,
|
21
|
+
'teal' => 0x008080,
|
22
|
+
'aqua' => 0x00ffff
|
23
|
+
}
|
5
24
|
|
6
25
|
REGEXP = /\##{"([0-9a-fA-F]{1,2})" * 3}/
|
7
26
|
|
8
27
|
def parse(value)
|
9
|
-
|
28
|
+
if (value =~ REGEXP)
|
29
|
+
@value = value.scan(REGEXP)[0].map { |num| num.ljust(2, num).to_i(16) }
|
30
|
+
else
|
31
|
+
color = HTML4_COLORS[value.downcase]
|
32
|
+
@value = (0..2).map{ |n| color >> (n << 3) & 0xff }.reverse
|
33
|
+
end
|
10
34
|
end
|
11
35
|
|
12
36
|
def plus(other)
|
@@ -7,19 +7,21 @@ require 'sass/constant/color'
|
|
7
7
|
|
8
8
|
class Sass::Constant::Literal # :nodoc:
|
9
9
|
# The regular expression matching numbers.
|
10
|
-
NUMBER = /^(
|
10
|
+
NUMBER = /^(-?\d*?\.?)(\d+)([^\d\s]*)$/
|
11
|
+
|
12
|
+
html_color_matcher = Sass::Constant::Color::HTML4_COLORS.keys.join '|'
|
11
13
|
|
12
14
|
# The regular expression matching colors.
|
13
|
-
COLOR = /^\#(
|
15
|
+
COLOR = /^\# (?: [\da-f]{3} | [\da-f]{6} ) | #{html_color_matcher}/ix
|
14
16
|
|
15
17
|
def self.parse(value)
|
16
18
|
case value
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
19
|
+
when NUMBER
|
20
|
+
Sass::Constant::Number.new(value)
|
21
|
+
when COLOR
|
22
|
+
Sass::Constant::Color.new(value)
|
23
|
+
else
|
24
|
+
Sass::Constant::String.new(value)
|
23
25
|
end
|
24
26
|
end
|
25
27
|
|