sass 3.2.0.alpha.247 → 3.2.0.alpha.258
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.
- 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
|