return nodes.reject {|x| x[:node] == :whitespace }
end
+ # Return a new list of nodes where consecutive whitespace nodes have been
+ # replaced with a single whitespace node, and if the whitespace contains
+ # one or more newlines, everything before the final newline is removed.
+ def simplify_whitespace(nodes)
+ nodes.slice_when do |a,b|
+ a[:node] != :whitespace or b[:node] != :whitespace
+ end.map do |x|
+ if x[0][:node] == :whitespace
+ combined = x.map{|y| y[:raw]}.join.sub(/.*\n/m, "\n")
+ x[0].merge({ raw: combined})
+ else
+ x[0]
+ end
+ end
+ end
+
def is_media_dark_block(x)
return false unless x[:node] == :simple_block
x[:prelude].index {|y| is_media_dark_block(y)}
end
+ def is_supports_block(x)
+ true if x[:node] == :at_rule and x[:name] == "supports"
+ end
+
# Remove (prefers-color-scheme: dark) conditions from a media query.
# If the resulting query is empty, returns the query's block alone.
# Otherwise, returns the modified query.
end
end
+ def process(tree, params)
+ last_visited = {}
+ darknodes = []
+
+ tree.delete_if do |x|
+ sep = { node: :whitespace, raw: "\n" }
+ if last_visited[:node] == :whitespace
+ # Try to maintain indentation
+ sep[:raw] += last_visited[:raw].sub(/[^\n]*\z|.*\n/m, "")
+ end
+ last_visited = x
+
+ if is_supports_block(x)
+ # Re-parse the block as a list of rules
+ s = Crass::Parser.stringify(x[:block])
+ x[:block] = Crass::Parser.parse_rules(s, params)
+
+ block = process(x[:block], params)
+ unless block.empty?
+ block << { node: :whitespace, raw: "\n" }
+ darknodes << [sep, x.merge({ block: block })]
+ end
+
+ Crass::Parser.stringify(x[:block]).strip.empty?
+ elsif is_media_dark(x)
+ if params[:alternate]
+ x = prune_media_dark(x)
+ end
+ darknodes << [sep, x]
+ end
+ end
+
+ # Combine consecutive equivalent media queries into a single query
+ result = darknodes.slice_when do |a,b|
+ !equiv_query(a[1], b[1])
+ end.each.map do |x|
+ case x[0][1]
+ when Hash
+ g = x.map{ |sep, node| node[:block] }.flatten
+ [ x[0][0], x[0][1].merge({ block: simplify_whitespace(g) }) ]
+ else
+ x
+ end
+ end.flatten
+
+ simplify_whitespace(result)
+ end
+
def run(content, params = {})
params = {
preserve_comments: true,
tree = Crass.parse(content, params)
- darknodes = []
- tree.delete_if { |x| darknodes << x if is_media_dark(x) }
-
- # Combine consecutive equivalent media queries into a single query
- darknodes = darknodes.slice_when{|a,b| !equiv_query(a, b)}.each.map \
- do |x|
- combined = x[0].merge({block: x.map{|x| x[:block]}.flatten})
- end
-
- # In alternate mode, remove prefers-color-scheme queries.
- if params[:alternate]
- darknodes.map!{|x| prune_media_dark(x)}
+ prologue = tree.take_while do |x|
+ x[:node] == :comment or x[:node] == :whitespace
end
+ tree.slice!(0, prologue.length)
- darkcss = ""
- darknodes.each do |x|
- darkcss += "#{Crass::Parser.stringify(x).rstrip}\n"
- end
- darkcss.sub!(/^\n*/, "")
-
- if params[:alternate]
- prologue = tree.take_while do |x|
- x[:node] == :comment or x[:node] == :whitespace
- end
-
- "#{Crass::Parser.stringify(prologue).rstrip}\n\n#{darkcss}"
- else
- output = "#{Crass::Parser.stringify(tree).rstrip}\n"
- output += "\n#{darkcss}" unless darkcss.empty?
- output
+ output = "#{Crass::Parser.stringify(prologue).rstrip}\n"
+ darknodes = process(tree, params)
+ unless params[:alternate]
+ tree = simplify_whitespace(tree)
+ output += "#{Crass::Parser.stringify(tree).rstrip}\n"
end
+ output += "#{Crass::Parser.stringify(darknodes).rstrip}\n"
end
end