sass 3.2.0.alpha.247 → 3.2.0.alpha.258
Sign up to get free protection for your applications and to get access to all the features.
- data/REVISION +1 -1
- data/VERSION +1 -1
- data/lib/sass/css.rb +22 -5
- data/lib/sass/script/functions.rb +34 -28
- data/lib/sass/script/operation.rb +1 -1
- data/lib/sass/scss/parser.rb +68 -29
- data/lib/sass/selector.rb +9 -1
- data/lib/sass/selector/sequence.rb +24 -14
- data/lib/sass/selector/simple_sequence.rb +25 -7
- data/lib/sass/tree/visitors/to_css.rb +2 -1
- data/lib/sass/util.rb +2 -2
- data/test/sass/conversion_test.rb +29 -1
- data/test/sass/css2sass_test.rb +26 -0
- data/test/sass/engine_test.rb +1 -1
- data/test/sass/extend_test.rb +66 -0
- data/test/sass/functions_test.rb +14 -0
- data/test/sass/script_test.rb +10 -0
- data/test/sass/scss/css_test.rb +124 -70
- data/test/sass/scss/scss_test.rb +48 -0
- metadata +5 -5
data/REVISION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
|
1
|
+
77bf74d8fe5dc1ee1301f5e06676ae8581ddfd73
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
3.2.0.alpha.
|
1
|
+
3.2.0.alpha.258
|
data/lib/sass/css.rb
CHANGED
@@ -82,6 +82,7 @@ module Sass
|
|
82
82
|
nest_seqs root
|
83
83
|
parent_ref_rules root
|
84
84
|
flatten_rules root
|
85
|
+
bubble_subject root
|
85
86
|
fold_commas root
|
86
87
|
dump_selectors root
|
87
88
|
root
|
@@ -212,14 +213,16 @@ module Sass
|
|
212
213
|
firsts, rest = [sseq.members.first], sseq.members[1..-1]
|
213
214
|
firsts.push rest.shift if firsts.first.is_a?(Sass::Selector::Parent)
|
214
215
|
|
215
|
-
|
216
|
+
last_simple_subject = rest.empty? && sseq.subject?
|
217
|
+
if current_rule.nil? || first_sseq(current_rule).members != firsts ||
|
218
|
+
!!first_sseq(current_rule).subject? != !!last_simple_subject
|
216
219
|
current_rule = Tree::RuleNode.new([])
|
217
|
-
current_rule.parsed_rules = make_sseq(*firsts)
|
220
|
+
current_rule.parsed_rules = make_sseq(last_simple_subject, *firsts)
|
218
221
|
end
|
219
222
|
|
220
223
|
unless rest.empty?
|
221
224
|
rest.unshift Sass::Selector::Parent.new
|
222
|
-
child.parsed_rules = make_sseq(*rest)
|
225
|
+
child.parsed_rules = make_sseq(sseq.subject?, *rest)
|
223
226
|
current_rule << child
|
224
227
|
else
|
225
228
|
current_rule.children += child.children
|
@@ -287,6 +290,19 @@ module Sass
|
|
287
290
|
flatten_rules(rule)
|
288
291
|
end
|
289
292
|
|
293
|
+
def bubble_subject(root)
|
294
|
+
root.children.each do |child|
|
295
|
+
bubble_subject(child) if child.is_a?(Tree::RuleNode) || child.is_a?(Tree::DirectiveNode)
|
296
|
+
next unless child.is_a?(Tree::RuleNode)
|
297
|
+
next unless child.children.all? do |c|
|
298
|
+
next unless c.is_a?(Tree::RuleNode)
|
299
|
+
first_simple_sel(c).is_a?(Sass::Selector::Parent) && first_sseq(c).subject?
|
300
|
+
end
|
301
|
+
first_sseq(child).subject = true
|
302
|
+
child.children.each {|c| first_sseq(c).subject = false}
|
303
|
+
end
|
304
|
+
end
|
305
|
+
|
290
306
|
# Transform
|
291
307
|
#
|
292
308
|
# foo
|
@@ -355,10 +371,11 @@ module Sass
|
|
355
371
|
# {Sass::Selector::Sequence} which in turn contains only a single
|
356
372
|
# {Sass::Selector::SimpleSequence}.
|
357
373
|
#
|
374
|
+
# @param subject [Boolean] Whether this is a subject selector
|
358
375
|
# @param sseqs [Array<Sass::Selector::Sequence, String>]
|
359
376
|
# @return [Sass::Selector::CommaSequence]
|
360
|
-
def make_sseq(*sseqs)
|
361
|
-
make_seq(Sass::Selector::SimpleSequence.new(sseqs))
|
377
|
+
def make_sseq(subject, *sseqs)
|
378
|
+
make_seq(Sass::Selector::SimpleSequence.new(sseqs, subject))
|
362
379
|
end
|
363
380
|
|
364
381
|
# Return the first {Sass::Selector::Sequence} in a {Sass::Tree::RuleNode}.
|
@@ -144,6 +144,9 @@ module Sass::Script
|
|
144
144
|
# \{#join join($list1, $list2, \[$separator\])}
|
145
145
|
# : Joins together two lists into one.
|
146
146
|
#
|
147
|
+
# \{#append append($list1, $val, \[$separator\])}
|
148
|
+
# : Appends a single value onto the end of a list.
|
149
|
+
#
|
147
150
|
# ## Introspection Functions
|
148
151
|
#
|
149
152
|
# \{#type_of type-of($value)}
|
@@ -582,7 +585,10 @@ module Sass::Script
|
|
582
585
|
return Sass::Script::String.new("alpha(#{args.map {|a| a.to_s}.join(", ")})")
|
583
586
|
end
|
584
587
|
|
585
|
-
|
588
|
+
raise ArgumentError.new("wrong number of arguments (#{args.size} for 1)") if args.size != 1
|
589
|
+
|
590
|
+
assert_type args.first, :Color
|
591
|
+
Sass::Script::Number.new(args.first.alpha)
|
586
592
|
end
|
587
593
|
declare :alpha, [:color]
|
588
594
|
|
@@ -595,6 +601,7 @@ module Sass::Script
|
|
595
601
|
# @see #transparentize
|
596
602
|
# @raise [ArgumentError] If `color` isn't a color
|
597
603
|
def opacity(color)
|
604
|
+
return Sass::Script::String.new("opacity(#{color})") if color.is_a?(Sass::Script::Number)
|
598
605
|
assert_type color, :Color
|
599
606
|
Sass::Script::Number.new(color.alpha)
|
600
607
|
end
|
@@ -685,16 +692,21 @@ module Sass::Script
|
|
685
692
|
# @example
|
686
693
|
# saturate(hsl(120, 30%, 90%), 20%) => hsl(120, 50%, 90%)
|
687
694
|
# saturate(#855, 20%) => #9e3f3f
|
688
|
-
# @
|
689
|
-
#
|
690
|
-
#
|
691
|
-
#
|
692
|
-
#
|
693
|
-
#
|
694
|
-
|
695
|
+
# @overload saturate(color, amount)
|
696
|
+
# @param color [Color]
|
697
|
+
# @param amount [Number]
|
698
|
+
# @return [Color]
|
699
|
+
# @see #desaturate
|
700
|
+
# @raise [ArgumentError] If `color` isn't a color,
|
701
|
+
# or `number` isn't a number between 0% and 100%
|
702
|
+
def saturate(color, amount = nil)
|
703
|
+
# Support the filter effects definition of saturate.
|
704
|
+
# https://dvcs.w3.org/hg/FXTF/raw-file/tip/filters/index.html
|
705
|
+
return Sass::Script::String.new("saturate(#{color})") if amount.nil?
|
695
706
|
_adjust(color, amount, :saturation, 0..100, :+, "%")
|
696
707
|
end
|
697
708
|
declare :saturate, [:color, :amount]
|
709
|
+
declare :saturate, [:amount]
|
698
710
|
|
699
711
|
# Makes a color less saturated.
|
700
712
|
# Takes a color and an amount between 0% and 100%,
|
@@ -995,21 +1007,13 @@ module Sass::Script
|
|
995
1007
|
declare :mix, [:color_1, :color_2]
|
996
1008
|
declare :mix, [:color_1, :color_2, :weight]
|
997
1009
|
|
998
|
-
#
|
999
|
-
#
|
1000
|
-
# This is identical to `desaturate(color, 100%)`.
|
1001
|
-
#
|
1002
|
-
# @param color [Color]
|
1003
|
-
# @return [Color]
|
1004
|
-
# @raise [ArgumentError] if `color` isn't a color
|
1005
|
-
# @see #desaturate
|
1006
|
-
# @overload grayscale(number)
|
1007
|
-
# Returns an unquoted string `grayscale(number)`, as though the function
|
1008
|
-
# were not defined. This is for the `grayscale` function used in
|
1009
|
-
# `-webkit-filter`.
|
1010
|
+
# Converts a color to grayscale.
|
1011
|
+
# This is identical to `desaturate(color, 100%)`.
|
1010
1012
|
#
|
1011
|
-
#
|
1012
|
-
#
|
1013
|
+
# @param color [Color]
|
1014
|
+
# @return [Color]
|
1015
|
+
# @raise [ArgumentError] if `color` isn't a color
|
1016
|
+
# @see #desaturate
|
1013
1017
|
def grayscale(color)
|
1014
1018
|
return Sass::Script::String.new("grayscale(#{color})") if color.is_a?(Sass::Script::Number)
|
1015
1019
|
desaturate color, Number.new(100)
|
@@ -1035,6 +1039,8 @@ module Sass::Script
|
|
1035
1039
|
# @return [Color]
|
1036
1040
|
# @raise [ArgumentError] if `color` isn't a color
|
1037
1041
|
def invert(color)
|
1042
|
+
return Sass::Script::String.new("invert(#{color})") if color.is_a?(Sass::Script::Number)
|
1043
|
+
|
1038
1044
|
assert_type color, :Color
|
1039
1045
|
color.with(
|
1040
1046
|
:red => (255 - color.red),
|
@@ -1328,14 +1334,14 @@ module Sass::Script
|
|
1328
1334
|
# append(10px 20px, 30px) => 10px 20px 30px
|
1329
1335
|
# append((blue, red), green) => blue, red, green
|
1330
1336
|
# append(10px 20px, 30px 40px) => 10px 20px (30px 40px)
|
1331
|
-
#
|
1332
|
-
#
|
1333
|
-
# @overload
|
1334
|
-
# @param
|
1335
|
-
# @param
|
1337
|
+
# append(10px, 20px, comma) => 10px, 20px
|
1338
|
+
# append((blue, red), green, space) => blue red green
|
1339
|
+
# @overload append(list, val, separator: auto)
|
1340
|
+
# @param list [Literal] The list to add the value to
|
1341
|
+
# @param val [Literal] The value to add to the end of the list
|
1336
1342
|
# @param separator [String] How the list separator (comma or space) should be determined.
|
1337
1343
|
# If this is `comma` or `space`, that is always the separator;
|
1338
|
-
# if this is `auto` (the default), the separator is
|
1344
|
+
# if this is `auto` (the default), the separator is the same as that used by the list.
|
1339
1345
|
def append(list, val, separator = Sass::Script::String.new("auto"))
|
1340
1346
|
assert_type separator, :String
|
1341
1347
|
unless %w[auto space comma].include?(separator.value)
|
@@ -82,7 +82,7 @@ module Sass::Script
|
|
82
82
|
|
83
83
|
literal2 = @operand2.perform(environment)
|
84
84
|
|
85
|
-
if literal1.is_a?(Null) || literal2.is_a?(Null)
|
85
|
+
if (literal1.is_a?(Null) || literal2.is_a?(Null)) && @operator != :eq && @operator != :neq
|
86
86
|
raise Sass::SyntaxError.new("Invalid null operation: \"#{literal1.inspect} #{@operator} #{literal2.inspect}\".")
|
87
87
|
end
|
88
88
|
|
data/lib/sass/scss/parser.rb
CHANGED
@@ -541,7 +541,7 @@ module Sass
|
|
541
541
|
end
|
542
542
|
|
543
543
|
# This is a nasty hack, and the only place in the parser
|
544
|
-
# that requires backtracking.
|
544
|
+
# that requires a large amount of backtracking.
|
545
545
|
# The reason is that we can't figure out if certain strings
|
546
546
|
# are declarations or rulesets with fixed finite lookahead.
|
547
547
|
# For example, "foo:bar baz baz baz..." could be either a property
|
@@ -631,19 +631,31 @@ module Sass
|
|
631
631
|
end
|
632
632
|
|
633
633
|
def combinator
|
634
|
-
tok(PLUS) || tok(GREATER) || tok(TILDE)
|
634
|
+
tok(PLUS) || tok(GREATER) || tok(TILDE) || reference_combinator
|
635
|
+
end
|
636
|
+
|
637
|
+
def reference_combinator
|
638
|
+
return unless tok(/\//)
|
639
|
+
res = ['/']
|
640
|
+
ns, name = expr!(:qualified_name)
|
641
|
+
res << ns << '|' if ns
|
642
|
+
res << name << tok!(/\//)
|
643
|
+
res = res.flatten
|
644
|
+
res = res.join '' if res.all? {|e| e.is_a?(String)}
|
645
|
+
res
|
635
646
|
end
|
636
647
|
|
637
648
|
def simple_selector_sequence
|
638
|
-
#
|
649
|
+
# Returning expr by default allows for stuff like
|
650
|
+
# http://www.w3.org/TR/css3-animations/#keyframes-
|
639
651
|
return expr(!:allow_var) unless e = element_name || id_selector ||
|
640
|
-
class_selector || placeholder_selector || attrib ||
|
641
|
-
|
652
|
+
class_selector || placeholder_selector || attrib || pseudo ||
|
653
|
+
parent_selector || interpolation_selector
|
642
654
|
res = [e]
|
643
655
|
|
644
656
|
# The tok(/\*/) allows the "E*" hack
|
645
657
|
while v = id_selector || class_selector || placeholder_selector || attrib ||
|
646
|
-
|
658
|
+
pseudo || interpolation_selector ||
|
647
659
|
(tok(/\*/) && Selector::Universal.new(nil))
|
648
660
|
res << v
|
649
661
|
end
|
@@ -664,7 +676,7 @@ module Sass
|
|
664
676
|
end
|
665
677
|
end
|
666
678
|
|
667
|
-
Selector::SimpleSequence.new(res)
|
679
|
+
Selector::SimpleSequence.new(res, tok(/!/))
|
668
680
|
end
|
669
681
|
|
670
682
|
def parent_selector
|
@@ -691,12 +703,8 @@ module Sass
|
|
691
703
|
end
|
692
704
|
|
693
705
|
def element_name
|
694
|
-
|
695
|
-
|
696
|
-
@expected = "element name or *"
|
697
|
-
ns = name
|
698
|
-
name = interp_ident || tok!(/\*/)
|
699
|
-
end
|
706
|
+
ns, name = qualified_name(:allow_star_name)
|
707
|
+
return unless ns || name
|
700
708
|
|
701
709
|
if name == '*'
|
702
710
|
Selector::Universal.new(merge(ns))
|
@@ -705,6 +713,15 @@ module Sass
|
|
705
713
|
end
|
706
714
|
end
|
707
715
|
|
716
|
+
def qualified_name(allow_star_name=false)
|
717
|
+
return unless name = interp_ident || tok(/\*/) || (tok?(/\|/) && "")
|
718
|
+
return nil, name unless tok(/\|/)
|
719
|
+
|
720
|
+
return name, expr!(:interp_ident) unless allow_star_name
|
721
|
+
@expected = "identifier or *"
|
722
|
+
return name, interp_ident || tok!(/\*/)
|
723
|
+
end
|
724
|
+
|
708
725
|
def interpolation_selector
|
709
726
|
return unless script = interpolation
|
710
727
|
Selector::Interpolation.new(script)
|
@@ -727,9 +744,10 @@ module Sass
|
|
727
744
|
val = interp_ident || expr!(:interp_string)
|
728
745
|
ss
|
729
746
|
end
|
747
|
+
flags = interp_ident || interp_string
|
730
748
|
tok!(/\]/)
|
731
749
|
|
732
|
-
Selector::Attribute.new(merge(name), merge(ns), op, merge(val))
|
750
|
+
Selector::Attribute.new(merge(name), merge(ns), op, merge(val), merge(flags))
|
733
751
|
end
|
734
752
|
|
735
753
|
def attrib_name!
|
@@ -756,32 +774,53 @@ module Sass
|
|
756
774
|
name = expr!(:interp_ident)
|
757
775
|
if tok(/\(/)
|
758
776
|
ss
|
759
|
-
arg = expr!(:
|
777
|
+
arg = expr!(:pseudo_arg)
|
778
|
+
while tok(/,/)
|
779
|
+
arg << ',' << str{ss}
|
780
|
+
arg.concat expr!(:pseudo_arg)
|
781
|
+
end
|
760
782
|
tok!(/\)/)
|
761
783
|
end
|
762
784
|
Selector::Pseudo.new(s == ':' ? :class : :element, merge(name), merge(arg))
|
763
785
|
end
|
764
786
|
|
787
|
+
def pseudo_arg
|
788
|
+
# In the CSS spec, every pseudo-class/element either takes a pseudo
|
789
|
+
# expression or a selector comma sequence as an argument. However, we
|
790
|
+
# don't want to have to know which takes which, so we handle both at
|
791
|
+
# once.
|
792
|
+
#
|
793
|
+
# However, there are some ambiguities between the two. For instance, "n"
|
794
|
+
# could start a pseudo expression like "n+1", or it could start a
|
795
|
+
# selector like "n|m". In order to handle this, we must regrettably
|
796
|
+
# backtrack.
|
797
|
+
expr, sel = nil
|
798
|
+
pseudo_err = catch_error do
|
799
|
+
expr = pseudo_expr
|
800
|
+
next if tok?(/[,)]/)
|
801
|
+
expr = nil
|
802
|
+
expected '")"'
|
803
|
+
end
|
804
|
+
|
805
|
+
return expr if expr
|
806
|
+
sel_err = catch_error {sel = selector}
|
807
|
+
return sel if sel
|
808
|
+
rethrow pseudo_err if pseudo_err
|
809
|
+
rethrow sel_err if sel_err
|
810
|
+
return
|
811
|
+
end
|
812
|
+
|
765
813
|
def pseudo_expr
|
766
|
-
return unless e = tok(PLUS) || tok(
|
814
|
+
return unless e = tok(PLUS) || tok(/[-*]/) || tok(NUMBER) ||
|
767
815
|
interp_string || tok(IDENT) || interpolation
|
768
816
|
res = [e, str{ss}]
|
769
|
-
while e = tok(PLUS) || tok(
|
817
|
+
while e = tok(PLUS) || tok(/[-*]/) || tok(NUMBER) ||
|
770
818
|
interp_string || tok(IDENT) || interpolation
|
771
819
|
res << e << str{ss}
|
772
820
|
end
|
773
821
|
res
|
774
822
|
end
|
775
823
|
|
776
|
-
def negation
|
777
|
-
return unless name = tok(NOT) || tok(ANY)
|
778
|
-
ss
|
779
|
-
@expected = "selector"
|
780
|
-
sel = selector_comma_sequence
|
781
|
-
tok!(/\)/)
|
782
|
-
Selector::SelectorPseudoClass.new(name[1...-1], sel)
|
783
|
-
end
|
784
|
-
|
785
824
|
def declaration
|
786
825
|
# This allows the "*prop: val", ":prop: val", and ".prop: val" hacks
|
787
826
|
if s = tok(/[:\*\.]|\#(?!\{)/)
|
@@ -980,12 +1019,12 @@ MESSAGE
|
|
980
1019
|
EXPR_NAMES = {
|
981
1020
|
:media_query => "media query (e.g. print, screen, print and screen)",
|
982
1021
|
:media_expr => "media expression (e.g. (min-device-width: 800px))",
|
983
|
-
:
|
1022
|
+
:pseudo_arg => "expression (e.g. fr, 2n+1)",
|
984
1023
|
:interp_ident => "identifier",
|
985
1024
|
:interp_name => "identifier",
|
1025
|
+
:qualified_name => "identifier",
|
986
1026
|
:expr => "expression (e.g. 1px, bold)",
|
987
1027
|
:_selector => "selector",
|
988
|
-
:selector_comma_sequence => "selector",
|
989
1028
|
:simple_selector_sequence => "selector",
|
990
1029
|
:import_arg => "file to import (string or url())",
|
991
1030
|
:moz_document_function => "matching function (e.g. url-prefix(), domain())",
|
@@ -1041,7 +1080,7 @@ MESSAGE
|
|
1041
1080
|
pos = @scanner.pos
|
1042
1081
|
line = @line
|
1043
1082
|
expected = @expected
|
1044
|
-
if catch(:_sass_parser_error
|
1083
|
+
if catch(:_sass_parser_error) {yield; false}
|
1045
1084
|
@scanner.pos = pos
|
1046
1085
|
@line = line
|
1047
1086
|
@expected = expected
|
data/lib/sass/selector.rb
CHANGED
@@ -309,15 +309,22 @@ module Sass
|
|
309
309
|
# @return [Array<String, Sass::Script::Node>]
|
310
310
|
attr_reader :value
|
311
311
|
|
312
|
+
# Flags for the attribute selector (e.g. `i`).
|
313
|
+
#
|
314
|
+
# @return [Array<String, Sass::Script::Node>]
|
315
|
+
attr_reader :flags
|
316
|
+
|
312
317
|
# @param name [Array<String, Sass::Script::Node>] The attribute name
|
313
318
|
# @param namespace [Array<String, Sass::Script::Node>, nil] See \{#namespace}
|
314
319
|
# @param operator [String] The matching operator, e.g. `"="` or `"^="`
|
315
320
|
# @param value [Array<String, Sass::Script::Node>] See \{#value}
|
316
|
-
|
321
|
+
# @param value [Array<String, Sass::Script::Node>] See \{#flags}
|
322
|
+
def initialize(name, namespace, operator, value, flags)
|
317
323
|
@name = name
|
318
324
|
@namespace = namespace
|
319
325
|
@operator = operator
|
320
326
|
@value = value
|
327
|
+
@flags = flags
|
321
328
|
end
|
322
329
|
|
323
330
|
# @see Selector#to_a
|
@@ -326,6 +333,7 @@ module Sass
|
|
326
333
|
res.concat(@namespace) << "|" if @namespace
|
327
334
|
res.concat @name
|
328
335
|
(res << @operator).concat @value if @value
|
336
|
+
(res << " ").concat @flags if @flags
|
329
337
|
res << "]"
|
330
338
|
end
|
331
339
|
|
@@ -24,17 +24,16 @@ module Sass
|
|
24
24
|
filename
|
25
25
|
end
|
26
26
|
|
27
|
-
# The array of {SimpleSequence simple selector sequences}, operators, and
|
28
|
-
# The operators are strings such as `"+"` and `">"`
|
29
|
-
#
|
30
|
-
#
|
31
|
-
#
|
32
|
-
# but they do affect formatting.
|
27
|
+
# The array of {SimpleSequence simple selector sequences}, operators, and
|
28
|
+
# newlines. The operators are strings such as `"+"` and `">"` representing
|
29
|
+
# the corresponding CSS operators, or interpolated SassScript. Newlines
|
30
|
+
# are also newline strings; these aren't semantically relevant, but they
|
31
|
+
# do affect formatting.
|
33
32
|
#
|
34
|
-
# @return [Array<SimpleSequence, String
|
33
|
+
# @return [Array<SimpleSequence, String|Array<Sass::Tree::Node, String>>]
|
35
34
|
attr_reader :members
|
36
35
|
|
37
|
-
# @param seqs_and_ops [Array<SimpleSequence, String
|
36
|
+
# @param seqs_and_ops [Array<SimpleSequence, String|Array<Sass::Tree::Node, String>>] See \{#members}
|
38
37
|
def initialize(seqs_and_ops)
|
39
38
|
@members = seqs_and_ops
|
40
39
|
end
|
@@ -54,7 +53,7 @@ module Sass
|
|
54
53
|
end
|
55
54
|
members = []
|
56
55
|
members << nl if nl
|
57
|
-
members << SimpleSequence.new([Parent.new])
|
56
|
+
members << SimpleSequence.new([Parent.new], false)
|
58
57
|
members += @members
|
59
58
|
end
|
60
59
|
|
@@ -198,7 +197,7 @@ module Sass
|
|
198
197
|
diff += fin.map {|sel| sel.is_a?(Array) ? sel : [sel]}
|
199
198
|
diff.reject! {|c| c.empty?}
|
200
199
|
|
201
|
-
Sass::Util.paths(diff).map {|p| p.flatten}
|
200
|
+
Sass::Util.paths(diff).map {|p| p.flatten}.reject {|p| path_has_two_subjects?(p)}
|
202
201
|
end
|
203
202
|
|
204
203
|
# Extracts initial selector combinators (`"+"`, `">"`, `"~"`, and `"\n"`)
|
@@ -272,7 +271,7 @@ module Sass
|
|
272
271
|
elsif sel2.superselector?(sel1)
|
273
272
|
res.unshift sel1, '~'
|
274
273
|
else
|
275
|
-
merged = sel1.unify(sel2.members)
|
274
|
+
merged = sel1.unify(sel2.members, sel2.subject?)
|
276
275
|
res.unshift [
|
277
276
|
[sel1, '~', sel2, '~'],
|
278
277
|
[sel2, '~', sel1, '~'],
|
@@ -289,7 +288,7 @@ module Sass
|
|
289
288
|
if tilde_sel.superselector?(plus_sel)
|
290
289
|
res.unshift plus_sel, '+'
|
291
290
|
else
|
292
|
-
merged = plus_sel.unify(tilde_sel.members)
|
291
|
+
merged = plus_sel.unify(tilde_sel.members, tilde_sel.subject?)
|
293
292
|
res.unshift [
|
294
293
|
[tilde_sel, '~', plus_sel, '+'],
|
295
294
|
([merged, '+'] if merged)
|
@@ -302,7 +301,7 @@ module Sass
|
|
302
301
|
res.unshift sel1, op1
|
303
302
|
seq2.push sel2, op2
|
304
303
|
elsif op1 == op2
|
305
|
-
return unless merged = sel1.unify(sel2.members)
|
304
|
+
return unless merged = sel1.unify(sel2.members, sel2.subject?)
|
306
305
|
res.unshift merged, op1
|
307
306
|
else
|
308
307
|
# Unknown selector combinators can't be unified
|
@@ -422,7 +421,7 @@ module Sass
|
|
422
421
|
# @param seq2 [Array<SimpleSequence or String>]
|
423
422
|
# @return [Boolean]
|
424
423
|
def parent_superselector?(seq1, seq2)
|
425
|
-
base = Sass::Selector::SimpleSequence.new([Sass::Selector::Placeholder.new('<temp>')])
|
424
|
+
base = Sass::Selector::SimpleSequence.new([Sass::Selector::Placeholder.new('<temp>')], false)
|
426
425
|
_superselector?(seq1 + [base], seq2 + [base])
|
427
426
|
end
|
428
427
|
|
@@ -467,6 +466,17 @@ module Sass
|
|
467
466
|
|
468
467
|
private
|
469
468
|
|
469
|
+
def path_has_two_subjects?(path)
|
470
|
+
subject = false
|
471
|
+
path.each do |sseq_or_op|
|
472
|
+
next unless sseq_or_op.is_a?(SimpleSequence)
|
473
|
+
next unless sseq_or_op.subject?
|
474
|
+
return true if subject
|
475
|
+
subject = true
|
476
|
+
end
|
477
|
+
false
|
478
|
+
end
|
479
|
+
|
470
480
|
def _sources(seq)
|
471
481
|
s = Set.new
|
472
482
|
seq.map {|sseq_or_op| s.merge sseq_or_op.sources if sseq_or_op.is_a?(SimpleSequence)}
|
@@ -26,6 +26,9 @@ module Sass
|
|
26
26
|
# @return {Set<Sequence>}
|
27
27
|
attr_accessor :sources
|
28
28
|
|
29
|
+
# @see \{#subject?}
|
30
|
+
attr_writer :subject
|
31
|
+
|
29
32
|
# Returns the element or universal selector in this sequence,
|
30
33
|
# if it exists.
|
31
34
|
#
|
@@ -41,10 +44,21 @@ module Sass
|
|
41
44
|
@rest ||= Set.new(base ? members[1..-1] : members)
|
42
45
|
end
|
43
46
|
|
47
|
+
# Whether or not this compound selector is the subject of the parent
|
48
|
+
# selector; that is, whether it is prepended with `$` and represents the
|
49
|
+
# actual element that will be selected.
|
50
|
+
#
|
51
|
+
# @return [Boolean]
|
52
|
+
def subject?
|
53
|
+
@subject
|
54
|
+
end
|
55
|
+
|
44
56
|
# @param selectors [Array<Simple>] See \{#members}
|
57
|
+
# @param subject [Boolean] See \{#subject?}
|
45
58
|
# @param sources [Set<Sequence>]
|
46
|
-
def initialize(selectors, sources = Set.new)
|
59
|
+
def initialize(selectors, subject, sources = Set.new)
|
47
60
|
@members = selectors
|
61
|
+
@subject = subject
|
48
62
|
@sources = sources
|
49
63
|
end
|
50
64
|
|
@@ -66,7 +80,7 @@ module Sass
|
|
66
80
|
end
|
67
81
|
|
68
82
|
super_seq.members[0...-1] +
|
69
|
-
[SimpleSequence.new(super_seq.members.last.members + @members[1..-1])]
|
83
|
+
[SimpleSequence.new(super_seq.members.last.members + @members[1..-1], subject?)]
|
70
84
|
end
|
71
85
|
|
72
86
|
# Non-destrucively extends this selector with the extensions specified in a hash
|
@@ -88,7 +102,7 @@ module Sass
|
|
88
102
|
# seq is A, sels is B, and self is C
|
89
103
|
|
90
104
|
self_without_sel = self.members - sels
|
91
|
-
next unless unified = seq.members.last.unify(self_without_sel)
|
105
|
+
next unless unified = seq.members.last.unify(self_without_sel, subject?)
|
92
106
|
next if group.map {|e, _| check_directives_match!(e, parent_directives)}.none?
|
93
107
|
new_seq = Sequence.new(seq.members[0...-1] + [unified])
|
94
108
|
new_seq.add_sources!(sources + [seq])
|
@@ -103,6 +117,7 @@ module Sass
|
|
103
117
|
# that matches both this selector and the input selector.
|
104
118
|
#
|
105
119
|
# @param sels [Array<Simple>] A {SimpleSequence}'s {SimpleSequence#members members array}
|
120
|
+
# @param subject [Boolean] Whether the {SimpleSequence} being merged is a subject.
|
106
121
|
# @return [SimpleSequence, nil] A {SimpleSequence} matching both `sels` and this selector,
|
107
122
|
# or `nil` if this is impossible (e.g. unifying `#foo` and `#bar`)
|
108
123
|
# @raise [Sass::SyntaxError] If this selector cannot be unified.
|
@@ -111,12 +126,12 @@ module Sass
|
|
111
126
|
# Since these selectors should be resolved
|
112
127
|
# by the time extension and unification happen,
|
113
128
|
# this exception will only ever be raised as a result of programmer error
|
114
|
-
def unify(sels)
|
129
|
+
def unify(sels, other_subject)
|
115
130
|
return unless sseq = members.inject(sels) do |sseq, sel|
|
116
131
|
return unless sseq
|
117
132
|
sel.unify(sseq)
|
118
133
|
end
|
119
|
-
SimpleSequence.new(sseq)
|
134
|
+
SimpleSequence.new(sseq, other_subject || subject?)
|
120
135
|
end
|
121
136
|
|
122
137
|
# Returns whether or not this selector matches all elements
|
@@ -133,7 +148,9 @@ module Sass
|
|
133
148
|
|
134
149
|
# @see Simple#to_a
|
135
150
|
def to_a
|
136
|
-
@members.map {|sel| sel.to_a}.flatten
|
151
|
+
res = @members.map {|sel| sel.to_a}.flatten
|
152
|
+
res << '!' if subject?
|
153
|
+
res
|
137
154
|
end
|
138
155
|
|
139
156
|
# Returns a string representation of the sequence.
|
@@ -178,7 +195,8 @@ WARNING
|
|
178
195
|
end
|
179
196
|
|
180
197
|
def _eql?(other)
|
181
|
-
other.base.eql?(self.base) && Sass::Util.set_eql?(other.rest, self.rest)
|
198
|
+
other.base.eql?(self.base) && Sass::Util.set_eql?(other.rest, self.rest) &&
|
199
|
+
other.subject? == self.subject?
|
182
200
|
end
|
183
201
|
end
|
184
202
|
end
|
@@ -214,7 +214,8 @@ class Sass::Tree::Visitors::ToCss < Sass::Tree::Visitors::Base
|
|
214
214
|
rule.resolved_rules = Sass::Selector::CommaSequence.new(
|
215
215
|
[Sass::Selector::Sequence.new(
|
216
216
|
[Sass::Selector::SimpleSequence.new(
|
217
|
-
[Sass::Selector::Element.new(k.to_s.gsub(/[^\w-]/, "\\\\\\0"), nil)]
|
217
|
+
[Sass::Selector::Element.new(k.to_s.gsub(/[^\w-]/, "\\\\\\0"), nil)],
|
218
|
+
false)
|
218
219
|
])
|
219
220
|
])
|
220
221
|
prop = Sass::Tree::PropNode.new([""], Sass::Script::String.new(''), :new)
|
data/lib/sass/util.rb
CHANGED
@@ -462,9 +462,9 @@ module Sass
|
|
462
462
|
# Like `Dir.glob`, but works with backslash-separated paths on Windows.
|
463
463
|
#
|
464
464
|
# @param path [String]
|
465
|
-
def glob(path)
|
465
|
+
def glob(path, &block)
|
466
466
|
path = path.gsub('\\', '/') if windows?
|
467
|
-
Dir.glob(path)
|
467
|
+
Dir.glob(path, &block)
|
468
468
|
end
|
469
469
|
|
470
470
|
## Cross-Ruby-Version Compatibility
|
@@ -74,10 +74,17 @@ SCSS
|
|
74
74
|
assert_renders <<SASS, <<SCSS
|
75
75
|
foo \#{$bar + "baz"}.bip
|
76
76
|
baz: bang
|
77
|
+
|
78
|
+
foo /\#{$bar + "baz"}/ .bip
|
79
|
+
baz: bang
|
77
80
|
SASS
|
78
81
|
foo \#{$bar + "baz"}.bip {
|
79
82
|
baz: bang;
|
80
83
|
}
|
84
|
+
|
85
|
+
foo /\#{$bar + "baz"}/ .bip {
|
86
|
+
baz: bang;
|
87
|
+
}
|
81
88
|
SCSS
|
82
89
|
end
|
83
90
|
|
@@ -1403,6 +1410,28 @@ SASS
|
|
1403
1410
|
SCSS
|
1404
1411
|
end
|
1405
1412
|
|
1413
|
+
def test_reference_selector
|
1414
|
+
assert_renders(<<SASS, <<SCSS)
|
1415
|
+
foo /bar|baz/ bang
|
1416
|
+
a: b
|
1417
|
+
SASS
|
1418
|
+
foo /bar|baz/ bang {
|
1419
|
+
a: b;
|
1420
|
+
}
|
1421
|
+
SCSS
|
1422
|
+
end
|
1423
|
+
|
1424
|
+
def test_subject
|
1425
|
+
assert_renders(<<SASS, <<SCSS)
|
1426
|
+
foo bar! baz
|
1427
|
+
a: b
|
1428
|
+
SASS
|
1429
|
+
foo bar! baz {
|
1430
|
+
a: b;
|
1431
|
+
}
|
1432
|
+
SCSS
|
1433
|
+
end
|
1434
|
+
|
1406
1435
|
def test_placeholder_interoplation_conversion
|
1407
1436
|
assert_renders(<<SASS, <<SCSS)
|
1408
1437
|
$foo: foo
|
@@ -1523,7 +1552,6 @@ foo bar {
|
|
1523
1552
|
SCSS
|
1524
1553
|
end
|
1525
1554
|
|
1526
|
-
|
1527
1555
|
## Regression Tests
|
1528
1556
|
|
1529
1557
|
def test_empty_lists
|
data/test/sass/css2sass_test.rb
CHANGED
@@ -237,6 +237,32 @@ SASS
|
|
237
237
|
CSS
|
238
238
|
end
|
239
239
|
|
240
|
+
def test_subject
|
241
|
+
assert_equal(<<SASS, css2sass(<<CSS))
|
242
|
+
.foo
|
243
|
+
.bar!
|
244
|
+
.baz
|
245
|
+
a: b
|
246
|
+
.bip
|
247
|
+
c: d
|
248
|
+
.bar .bonk
|
249
|
+
e: f
|
250
|
+
|
251
|
+
.flip!
|
252
|
+
&.bar
|
253
|
+
a: b
|
254
|
+
&.baz
|
255
|
+
c: d
|
256
|
+
SASS
|
257
|
+
.foo .bar! .baz {a: b;}
|
258
|
+
.foo .bar! .bip {c: d;}
|
259
|
+
.foo .bar .bonk {e: f;}
|
260
|
+
|
261
|
+
.flip.bar! {a: b;}
|
262
|
+
.flip.baz! {c: d;}
|
263
|
+
CSS
|
264
|
+
end
|
265
|
+
|
240
266
|
# Regressions
|
241
267
|
|
242
268
|
def test_nesting_within_media
|
data/test/sass/engine_test.rb
CHANGED
@@ -2716,7 +2716,7 @@ SASS
|
|
2716
2716
|
end
|
2717
2717
|
|
2718
2718
|
def test_comment_like_selector
|
2719
|
-
assert_raise_message(Sass::SyntaxError, 'Invalid CSS after "": expected
|
2719
|
+
assert_raise_message(Sass::SyntaxError, 'Invalid CSS after "/": expected identifier, was " foo"') {render(<<SASS)}
|
2720
2720
|
/ foo
|
2721
2721
|
a: b
|
2722
2722
|
SASS
|
data/test/sass/extend_test.rb
CHANGED
@@ -414,6 +414,12 @@ SCSS
|
|
414
414
|
assert_extends 'a ~ b c .c1', 'b c .c2 {@extend .c1}', 'a ~ b c .c1, a ~ b c .c2'
|
415
415
|
end
|
416
416
|
|
417
|
+
def test_nested_extender_doesnt_find_common_selectors_around_reference_selector
|
418
|
+
assert_extends 'a /for/ b c .c1', 'a c .c2 {@extend .c1}', 'a /for/ b c .c1, a /for/ b a c .c2, a a /for/ b c .c2'
|
419
|
+
assert_extends 'a /for/ b c .c1', 'a b .c2 {@extend .c1}', 'a /for/ b c .c1, a a /for/ b c .c2'
|
420
|
+
assert_extends 'a /for/ b c .c1', 'b c .c2 {@extend .c1}', 'a /for/ b c .c1, a /for/ b c .c2'
|
421
|
+
end
|
422
|
+
|
417
423
|
def test_nested_extender_with_early_child_selectors_doesnt_subseq_them
|
418
424
|
assert_extends('.bip > .bap .foo', '.grip > .bap .bar {@extend .foo}',
|
419
425
|
'.bip > .bap .foo, .bip > .bap .grip > .bap .bar, .grip > .bap .bip > .bap .bar')
|
@@ -443,7 +449,9 @@ CSS
|
|
443
449
|
> .baz {@extend .bar}
|
444
450
|
}
|
445
451
|
SCSS
|
452
|
+
end
|
446
453
|
|
454
|
+
def test_nested_extender_with_early_child_selectors_doesnt_subseq_them
|
447
455
|
assert_equal <<CSS, render(<<SCSS)
|
448
456
|
.foo .bar, .foo .bip > .baz {
|
449
457
|
a: b; }
|
@@ -1008,6 +1016,64 @@ CSS
|
|
1008
1016
|
SCSS
|
1009
1017
|
end
|
1010
1018
|
|
1019
|
+
def test_extend_with_subject_transfers_subject_to_extender
|
1020
|
+
assert_equal(<<CSS, render(<<SCSS))
|
1021
|
+
foo bar! baz, foo .bip .bap! baz, .bip foo .bap! baz {
|
1022
|
+
a: b; }
|
1023
|
+
CSS
|
1024
|
+
foo bar! baz {a: b}
|
1025
|
+
.bip .bap {@extend bar}
|
1026
|
+
SCSS
|
1027
|
+
|
1028
|
+
assert_equal(<<CSS, render(<<SCSS))
|
1029
|
+
foo.x bar.y! baz.z, foo.x .bip bar.bap! baz.z, .bip foo.x bar.bap! baz.z {
|
1030
|
+
a: b; }
|
1031
|
+
CSS
|
1032
|
+
foo.x bar.y! baz.z {a: b}
|
1033
|
+
.bip .bap {@extend .y}
|
1034
|
+
SCSS
|
1035
|
+
end
|
1036
|
+
|
1037
|
+
def test_extend_with_subject_retains_subject_on_target
|
1038
|
+
assert_equal(<<CSS, render(<<SCSS))
|
1039
|
+
.foo! .bar, .foo! .bip .bap, .bip .foo! .bap {
|
1040
|
+
a: b; }
|
1041
|
+
CSS
|
1042
|
+
.foo! .bar {a: b}
|
1043
|
+
.bip .bap {@extend .bar}
|
1044
|
+
SCSS
|
1045
|
+
end
|
1046
|
+
|
1047
|
+
def test_extend_with_subject_transfers_subject_to_target
|
1048
|
+
assert_equal(<<CSS, render(<<SCSS))
|
1049
|
+
a.foo .bar, .bip a.bap! .bar {
|
1050
|
+
a: b; }
|
1051
|
+
CSS
|
1052
|
+
a.foo .bar {a: b}
|
1053
|
+
.bip .bap! {@extend .foo}
|
1054
|
+
SCSS
|
1055
|
+
end
|
1056
|
+
|
1057
|
+
def test_extend_with_subject_retains_subject_on_extender
|
1058
|
+
assert_equal(<<CSS, render(<<SCSS))
|
1059
|
+
.foo .bar, .foo .bip! .bap, .bip! .foo .bap {
|
1060
|
+
a: b; }
|
1061
|
+
CSS
|
1062
|
+
.foo .bar {a: b}
|
1063
|
+
.bip! .bap {@extend .bar}
|
1064
|
+
SCSS
|
1065
|
+
end
|
1066
|
+
|
1067
|
+
def test_extend_with_subject_fails_with_conflicting_subject
|
1068
|
+
assert_equal(<<CSS, render(<<SCSS))
|
1069
|
+
x! .bar {
|
1070
|
+
a: b; }
|
1071
|
+
CSS
|
1072
|
+
x! .bar {a: b}
|
1073
|
+
y! .bap {@extend .bar}
|
1074
|
+
SCSS
|
1075
|
+
end
|
1076
|
+
|
1011
1077
|
# Regression Tests
|
1012
1078
|
|
1013
1079
|
def test_newline_near_combinator
|
data/test/sass/functions_test.rb
CHANGED
@@ -336,6 +336,18 @@ class SassFunctionTest < Test::Unit::TestCase
|
|
336
336
|
assert_error_message("12 is not a color for `alpha'", "alpha(12)")
|
337
337
|
end
|
338
338
|
|
339
|
+
def test_opacity
|
340
|
+
assert_equal("1", evaluate("opacity(#123456)"))
|
341
|
+
assert_equal("0.34", evaluate("opacity(rgba(0, 1, 2, 0.34))"))
|
342
|
+
assert_equal("0", evaluate("opacity(hsla(0, 1, 2, 0))"))
|
343
|
+
assert_equal("0", evaluate("opacity($color: hsla(0, 1, 2, 0))"))
|
344
|
+
assert_equal("opacity(20%)", evaluate("opacity(20%)"))
|
345
|
+
end
|
346
|
+
|
347
|
+
def test_opacity_exception
|
348
|
+
assert_error_message("\"foo\" is not a color for `opacity'", "opacity(foo)")
|
349
|
+
end
|
350
|
+
|
339
351
|
def test_opacify
|
340
352
|
assert_equal("rgba(0, 0, 0, 0.75)", evaluate("opacify(rgba(0, 0, 0, 0.5), 0.25)"))
|
341
353
|
assert_equal("rgba(0, 0, 0, 0.3)", evaluate("opacify(rgba(0, 0, 0, 0.2), 0.1)"))
|
@@ -435,6 +447,7 @@ class SassFunctionTest < Test::Unit::TestCase
|
|
435
447
|
assert_equal("#88aa88", evaluate("saturate(#8a8, 0%)"))
|
436
448
|
assert_equal("rgba(158, 63, 63, 0.5)", evaluate("saturate(rgba(136, 85, 85, 0.5), 20%)"))
|
437
449
|
assert_equal("rgba(158, 63, 63, 0.5)", evaluate("saturate($color: rgba(136, 85, 85, 0.5), $amount: 20%)"))
|
450
|
+
assert_equal("saturate(50%)", evaluate("saturate(50%)"))
|
438
451
|
end
|
439
452
|
|
440
453
|
def test_saturate_tests_bounds
|
@@ -828,6 +841,7 @@ class SassFunctionTest < Test::Unit::TestCase
|
|
828
841
|
def test_invert
|
829
842
|
assert_equal("#112233", evaluate("invert(#edc)"))
|
830
843
|
assert_equal("rgba(245, 235, 225, 0.5)", evaluate("invert(rgba(10, 20, 30, 0.5))"))
|
844
|
+
assert_equal("invert(20%)", evaluate("invert(20%)"))
|
831
845
|
end
|
832
846
|
|
833
847
|
def test_invert_tests_types
|
data/test/sass/script_test.rb
CHANGED
@@ -385,6 +385,16 @@ SASS
|
|
385
385
|
assert_equal "false", resolve('(1 (2 3)) == (1 2 3)')
|
386
386
|
assert_equal "false", resolve('((1, 2) (3, 4)) == (1, 2 3, 4)')
|
387
387
|
assert_equal "false", resolve('(1 2 3) == (1, 2, 3)')
|
388
|
+
|
389
|
+
assert_equal "true", resolve('null == null')
|
390
|
+
assert_equal "false", resolve('"null" == null')
|
391
|
+
assert_equal "false", resolve('0 == null')
|
392
|
+
assert_equal "false", resolve('() == null')
|
393
|
+
|
394
|
+
assert_equal "false", resolve('null != null')
|
395
|
+
assert_equal "true", resolve('"null" != null')
|
396
|
+
assert_equal "true", resolve('0 != null')
|
397
|
+
assert_equal "true", resolve('() != null')
|
388
398
|
end
|
389
399
|
|
390
400
|
def test_operation_precedence
|
data/test/sass/scss/css_test.rb
CHANGED
@@ -650,91 +650,140 @@ SCSS
|
|
650
650
|
|
651
651
|
## Selectors
|
652
652
|
|
653
|
-
# Taken from http://
|
653
|
+
# Taken from http://dev.w3.org/csswg/selectors4/#overview
|
654
654
|
def test_summarized_selectors
|
655
655
|
assert_selector_parses('*')
|
656
656
|
assert_selector_parses('E')
|
657
|
+
assert_selector_parses('E:not(s)')
|
658
|
+
assert_selector_parses('E:not(s1, s2)')
|
659
|
+
assert_selector_parses('E:matches(s1, s2)')
|
660
|
+
assert_selector_parses('E.warning')
|
661
|
+
assert_selector_parses('E#myid')
|
657
662
|
assert_selector_parses('E[foo]')
|
658
663
|
assert_selector_parses('E[foo="bar"]')
|
664
|
+
assert_selector_parses('E[foo="bar" i]')
|
659
665
|
assert_selector_parses('E[foo~="bar"]')
|
660
666
|
assert_selector_parses('E[foo^="bar"]')
|
661
667
|
assert_selector_parses('E[foo$="bar"]')
|
662
668
|
assert_selector_parses('E[foo*="bar"]')
|
663
669
|
assert_selector_parses('E[foo|="en"]')
|
664
|
-
assert_selector_parses('E:
|
665
|
-
assert_selector_parses('E:
|
666
|
-
assert_selector_parses('E:
|
667
|
-
assert_selector_parses('E:
|
668
|
-
assert_selector_parses('E:nth-last-of-type(n)')
|
669
|
-
assert_selector_parses('E:first-child')
|
670
|
-
assert_selector_parses('E:last-child')
|
671
|
-
assert_selector_parses('E:first-of-type')
|
672
|
-
assert_selector_parses('E:last-of-type')
|
673
|
-
assert_selector_parses('E:only-child')
|
674
|
-
assert_selector_parses('E:only-of-type')
|
675
|
-
assert_selector_parses('E:empty')
|
670
|
+
assert_selector_parses('E:dir(ltr)')
|
671
|
+
assert_selector_parses('E:lang(fr)')
|
672
|
+
assert_selector_parses('E:lang(zh, *-hant)')
|
673
|
+
assert_selector_parses('E:any-link')
|
676
674
|
assert_selector_parses('E:link')
|
677
675
|
assert_selector_parses('E:visited')
|
676
|
+
assert_selector_parses('E:local-link')
|
677
|
+
assert_selector_parses('E:local-link(0)')
|
678
|
+
assert_selector_parses('E:target')
|
679
|
+
assert_selector_parses('E:scope')
|
680
|
+
assert_selector_parses('E:current')
|
681
|
+
assert_selector_parses('E:current(s)')
|
682
|
+
assert_selector_parses('E:past')
|
683
|
+
assert_selector_parses('E:future')
|
678
684
|
assert_selector_parses('E:active')
|
679
685
|
assert_selector_parses('E:hover')
|
680
686
|
assert_selector_parses('E:focus')
|
681
|
-
assert_selector_parses('E:target')
|
682
|
-
assert_selector_parses('E:lang(fr)')
|
683
687
|
assert_selector_parses('E:enabled')
|
684
688
|
assert_selector_parses('E:disabled')
|
685
689
|
assert_selector_parses('E:checked')
|
686
|
-
assert_selector_parses('E
|
687
|
-
assert_selector_parses('E
|
688
|
-
assert_selector_parses('E
|
689
|
-
assert_selector_parses('E
|
690
|
-
assert_selector_parses('E
|
691
|
-
assert_selector_parses('E
|
692
|
-
assert_selector_parses('E:
|
690
|
+
assert_selector_parses('E:indeterminate')
|
691
|
+
assert_selector_parses('E:default')
|
692
|
+
assert_selector_parses('E:in-range')
|
693
|
+
assert_selector_parses('E:out-of-range')
|
694
|
+
assert_selector_parses('E:required')
|
695
|
+
assert_selector_parses('E:optional')
|
696
|
+
assert_selector_parses('E:read-only')
|
697
|
+
assert_selector_parses('E:read-write')
|
698
|
+
assert_selector_parses('E:root')
|
699
|
+
assert_selector_parses('E:empty')
|
700
|
+
assert_selector_parses('E:first-child')
|
701
|
+
assert_selector_parses('E:nth-child(n)')
|
702
|
+
assert_selector_parses('E:last-child')
|
703
|
+
assert_selector_parses('E:nth-last-child(n)')
|
704
|
+
assert_selector_parses('E:only-child')
|
705
|
+
assert_selector_parses('E:first-of-type')
|
706
|
+
assert_selector_parses('E:nth-of-type(n)')
|
707
|
+
assert_selector_parses('E:last-of-type')
|
708
|
+
assert_selector_parses('E:nth-last-of-type(n)')
|
709
|
+
assert_selector_parses('E:only-of-type')
|
710
|
+
assert_selector_parses('E:nth-match(n of selector)')
|
711
|
+
assert_selector_parses('E:nth-last-match(n of selector)')
|
712
|
+
assert_selector_parses('E:column(selector)')
|
713
|
+
assert_selector_parses('E:nth-column(n)')
|
714
|
+
assert_selector_parses('E:nth-last-column(n)')
|
693
715
|
assert_selector_parses('E F')
|
694
716
|
assert_selector_parses('E > F')
|
695
717
|
assert_selector_parses('E + F')
|
696
718
|
assert_selector_parses('E ~ F')
|
719
|
+
assert_selector_parses('E /foo/ F')
|
720
|
+
assert_selector_parses('E! > F')
|
721
|
+
|
722
|
+
assert_selector_parses('E /ns|foo/ F')
|
723
|
+
assert_selector_parses('E /*|foo/ F')
|
697
724
|
end
|
698
725
|
|
699
|
-
# Taken from http://
|
700
|
-
#
|
701
|
-
def
|
726
|
+
# Taken from http://dev.w3.org/csswg/selectors4/#overview, but without element
|
727
|
+
# names.
|
728
|
+
def test_summarized_selectors
|
729
|
+
assert_selector_parses(':not(s)')
|
730
|
+
assert_selector_parses(':not(s1, s2)')
|
731
|
+
assert_selector_parses(':matches(s1, s2)')
|
732
|
+
assert_selector_parses('.warning')
|
733
|
+
assert_selector_parses('#myid')
|
702
734
|
assert_selector_parses('[foo]')
|
703
735
|
assert_selector_parses('[foo="bar"]')
|
736
|
+
assert_selector_parses('[foo="bar" i]')
|
704
737
|
assert_selector_parses('[foo~="bar"]')
|
705
738
|
assert_selector_parses('[foo^="bar"]')
|
706
739
|
assert_selector_parses('[foo$="bar"]')
|
707
740
|
assert_selector_parses('[foo*="bar"]')
|
708
741
|
assert_selector_parses('[foo|="en"]')
|
709
|
-
assert_selector_parses(':
|
710
|
-
assert_selector_parses(':
|
711
|
-
assert_selector_parses(':
|
712
|
-
assert_selector_parses(':
|
713
|
-
assert_selector_parses(':nth-last-of-type(n)')
|
714
|
-
assert_selector_parses(':first-child')
|
715
|
-
assert_selector_parses(':last-child')
|
716
|
-
assert_selector_parses(':first-of-type')
|
717
|
-
assert_selector_parses(':last-of-type')
|
718
|
-
assert_selector_parses(':only-child')
|
719
|
-
assert_selector_parses(':only-of-type')
|
720
|
-
assert_selector_parses(':empty')
|
742
|
+
assert_selector_parses(':dir(ltr)')
|
743
|
+
assert_selector_parses(':lang(fr)')
|
744
|
+
assert_selector_parses(':lang(zh, *-hant)')
|
745
|
+
assert_selector_parses(':any-link')
|
721
746
|
assert_selector_parses(':link')
|
722
747
|
assert_selector_parses(':visited')
|
748
|
+
assert_selector_parses(':local-link')
|
749
|
+
assert_selector_parses(':local-link(0)')
|
750
|
+
assert_selector_parses(':target')
|
751
|
+
assert_selector_parses(':scope')
|
752
|
+
assert_selector_parses(':current')
|
753
|
+
assert_selector_parses(':current(s)')
|
754
|
+
assert_selector_parses(':past')
|
755
|
+
assert_selector_parses(':future')
|
723
756
|
assert_selector_parses(':active')
|
724
757
|
assert_selector_parses(':hover')
|
725
758
|
assert_selector_parses(':focus')
|
726
|
-
assert_selector_parses(':target')
|
727
|
-
assert_selector_parses(':lang(fr)')
|
728
759
|
assert_selector_parses(':enabled')
|
729
760
|
assert_selector_parses(':disabled')
|
730
761
|
assert_selector_parses(':checked')
|
731
|
-
assert_selector_parses('
|
732
|
-
assert_selector_parses('
|
733
|
-
assert_selector_parses('
|
734
|
-
assert_selector_parses('
|
735
|
-
assert_selector_parses('
|
736
|
-
assert_selector_parses('
|
737
|
-
assert_selector_parses(':
|
762
|
+
assert_selector_parses(':indeterminate')
|
763
|
+
assert_selector_parses(':default')
|
764
|
+
assert_selector_parses(':in-range')
|
765
|
+
assert_selector_parses(':out-of-range')
|
766
|
+
assert_selector_parses(':required')
|
767
|
+
assert_selector_parses(':optional')
|
768
|
+
assert_selector_parses(':read-only')
|
769
|
+
assert_selector_parses(':read-write')
|
770
|
+
assert_selector_parses(':root')
|
771
|
+
assert_selector_parses(':empty')
|
772
|
+
assert_selector_parses(':first-child')
|
773
|
+
assert_selector_parses(':nth-child(n)')
|
774
|
+
assert_selector_parses(':last-child')
|
775
|
+
assert_selector_parses(':nth-last-child(n)')
|
776
|
+
assert_selector_parses(':only-child')
|
777
|
+
assert_selector_parses(':first-of-type')
|
778
|
+
assert_selector_parses(':nth-of-type(n)')
|
779
|
+
assert_selector_parses(':last-of-type')
|
780
|
+
assert_selector_parses(':nth-last-of-type(n)')
|
781
|
+
assert_selector_parses(':only-of-type')
|
782
|
+
assert_selector_parses(':nth-match(n of selector)')
|
783
|
+
assert_selector_parses(':nth-last-match(n of selector)')
|
784
|
+
assert_selector_parses(':column(selector)')
|
785
|
+
assert_selector_parses(':nth-column(n)')
|
786
|
+
assert_selector_parses(':nth-last-column(n)')
|
738
787
|
end
|
739
788
|
|
740
789
|
def test_attribute_selectors_with_identifiers
|
@@ -771,36 +820,41 @@ CSS
|
|
771
820
|
SCSS
|
772
821
|
end
|
773
822
|
|
774
|
-
def
|
775
|
-
|
776
|
-
|
823
|
+
def test_selectors_containing_selectors
|
824
|
+
assert_selector_can_contain_selectors(':not(<sel>)')
|
825
|
+
assert_selector_can_contain_selectors(':current(<sel>)')
|
826
|
+
assert_selector_can_contain_selectors(':nth-match(n of <sel>)')
|
827
|
+
assert_selector_can_contain_selectors(':nth-last-match(n of <sel>)')
|
828
|
+
assert_selector_can_contain_selectors(':column(<sel>)')
|
829
|
+
assert_selector_can_contain_selectors(':-moz-any(<sel>)')
|
830
|
+
end
|
831
|
+
|
832
|
+
def assert_selector_can_contain_selectors(sel)
|
833
|
+
try = lambda {|subsel| assert_selector_parses(sel.gsub('<sel>', subsel))}
|
834
|
+
|
835
|
+
try['foo|bar']
|
836
|
+
try['*|bar']
|
777
837
|
|
778
|
-
|
779
|
-
|
838
|
+
try['foo|*']
|
839
|
+
try['*|*']
|
780
840
|
|
781
|
-
|
782
|
-
|
841
|
+
try['#blah']
|
842
|
+
try['.blah']
|
783
843
|
|
784
|
-
|
785
|
-
|
786
|
-
|
844
|
+
try['[foo]']
|
845
|
+
try['[foo^="bar"]']
|
846
|
+
try['[baz|foo~="bar"]']
|
787
847
|
|
788
|
-
|
789
|
-
|
848
|
+
try[':hover']
|
849
|
+
try[':nth-child(2n + 3)']
|
790
850
|
|
791
|
-
|
792
|
-
|
793
|
-
assert_selector_parses(':not(a#foo.bar)')
|
794
|
-
assert_selector_parses(':not(#foo .bar > baz)')
|
795
|
-
assert_selector_parses(':not(h1, h2, h3)')
|
796
|
-
end
|
851
|
+
try['h1, h2, h3']
|
852
|
+
try['#foo, bar, [baz]']
|
797
853
|
|
798
|
-
|
799
|
-
|
800
|
-
|
801
|
-
|
802
|
-
assert_selector_parses(':-webkit-any(foo bar, .baz > .bang)')
|
803
|
-
assert_selector_parses(':any(foo bar, .baz > .bang)')
|
854
|
+
# Not technically allowed for most selectors, but what the heck
|
855
|
+
try[':not(#foo)']
|
856
|
+
try['a#foo.bar']
|
857
|
+
try['#foo .bar > baz']
|
804
858
|
end
|
805
859
|
|
806
860
|
def test_namespaced_selectors
|
data/test/sass/scss/scss_test.rb
CHANGED
@@ -517,6 +517,22 @@ foo {
|
|
517
517
|
SCSS
|
518
518
|
end
|
519
519
|
|
520
|
+
def test_parent_selector_with_subject
|
521
|
+
assert_equal <<CSS, render(<<SCSS)
|
522
|
+
bar foo.baz! .bip {
|
523
|
+
a: b; }
|
524
|
+
|
525
|
+
bar foo bar.baz! .bip {
|
526
|
+
c: d; }
|
527
|
+
CSS
|
528
|
+
foo {
|
529
|
+
bar &.baz! .bip {a: b}}
|
530
|
+
|
531
|
+
foo bar {
|
532
|
+
bar &.baz! .bip {c: d}}
|
533
|
+
SCSS
|
534
|
+
end
|
535
|
+
|
520
536
|
## Namespace Properties
|
521
537
|
|
522
538
|
def test_namespace_properties
|
@@ -871,6 +887,29 @@ div { -foo-\#{$a}-\#{$b}-foo: foo }
|
|
871
887
|
SCSS
|
872
888
|
end
|
873
889
|
|
890
|
+
def test_selector_interpolation_in_reference_combinator
|
891
|
+
assert_equal <<CSS, render(<<SCSS)
|
892
|
+
.foo /a/ .bar /b|c/ .baz {
|
893
|
+
a: b; }
|
894
|
+
CSS
|
895
|
+
$a: a;
|
896
|
+
$b: b;
|
897
|
+
$c: c;
|
898
|
+
.foo /\#{$a}/ .bar /\#{$b}|\#{$c}/ .baz {a: b}
|
899
|
+
SCSS
|
900
|
+
end
|
901
|
+
|
902
|
+
def test_parent_selector_with_parent_and_subject
|
903
|
+
assert_equal <<CSS, render(<<SCSS)
|
904
|
+
bar foo.baz! .bip {
|
905
|
+
c: d; }
|
906
|
+
CSS
|
907
|
+
$subject: "!";
|
908
|
+
foo {
|
909
|
+
bar &.baz\#{$subject} .bip {c: d}}
|
910
|
+
SCSS
|
911
|
+
end
|
912
|
+
|
874
913
|
def test_basic_prop_name_interpolation
|
875
914
|
assert_equal <<CSS, render(<<SCSS)
|
876
915
|
foo {
|
@@ -1243,6 +1282,15 @@ SCSS
|
|
1243
1282
|
|
1244
1283
|
# Regression
|
1245
1284
|
|
1285
|
+
def test_reference_combinator_with_parent_ref
|
1286
|
+
assert_equal <<CSS, render(<<SCSS)
|
1287
|
+
a /foo/ b {
|
1288
|
+
c: d; }
|
1289
|
+
CSS
|
1290
|
+
a {& /foo/ b {c: d}}
|
1291
|
+
SCSS
|
1292
|
+
end
|
1293
|
+
|
1246
1294
|
def test_prop_name_interpolation_after_hyphen
|
1247
1295
|
assert_equal <<CSS, render(<<SCSS)
|
1248
1296
|
a {
|
metadata
CHANGED
@@ -1,15 +1,15 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: sass
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
hash:
|
4
|
+
hash: 592302361
|
5
5
|
prerelease: 6
|
6
6
|
segments:
|
7
7
|
- 3
|
8
8
|
- 2
|
9
9
|
- 0
|
10
10
|
- alpha
|
11
|
-
-
|
12
|
-
version: 3.2.0.alpha.
|
11
|
+
- 258
|
12
|
+
version: 3.2.0.alpha.258
|
13
13
|
platform: ruby
|
14
14
|
authors:
|
15
15
|
- Nathan Weizenbaum
|
@@ -19,7 +19,7 @@ autorequire:
|
|
19
19
|
bindir: bin
|
20
20
|
cert_chain: []
|
21
21
|
|
22
|
-
date: 2012-06-
|
22
|
+
date: 2012-06-29 00:00:00 -04:00
|
23
23
|
default_executable:
|
24
24
|
dependencies:
|
25
25
|
- !ruby/object:Gem::Dependency
|
@@ -154,7 +154,6 @@ files:
|
|
154
154
|
- lib/sass/tree/content_node.rb
|
155
155
|
- lib/sass/tree/css_import_node.rb
|
156
156
|
- lib/sass/tree/variable_node.rb
|
157
|
-
- lib/sass/tree/warn_node.rb
|
158
157
|
- lib/sass/tree/visitors/base.rb
|
159
158
|
- lib/sass/tree/visitors/check_nesting.rb
|
160
159
|
- lib/sass/tree/visitors/convert.rb
|
@@ -164,6 +163,7 @@ files:
|
|
164
163
|
- lib/sass/tree/visitors/perform.rb
|
165
164
|
- lib/sass/tree/visitors/set_options.rb
|
166
165
|
- lib/sass/tree/visitors/to_css.rb
|
166
|
+
- lib/sass/tree/warn_node.rb
|
167
167
|
- lib/sass/tree/while_node.rb
|
168
168
|
- lib/sass/tree/supports_node.rb
|
169
169
|
- lib/sass/tree/trace_node.rb
|