Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP

Loading…

Awesome media #6

Closed
wants to merge 16 commits into from

2 participants

@chriseppstein

This branch implements nested @media directives.

chriseppstein added some commits
@chriseppstein chriseppstein fix some test running issues in 1.9.2 dbfbdd0
@chriseppstein chriseppstein fix 1.9.2 issue in the staleness checker. b94af29
@chriseppstein chriseppstein Fix some numeric rounding issues in ruby 1.9.2 29cf7a6
@chriseppstein chriseppstein Merge branch 'sass-only'
* sass-only:
  Fix some numeric rounding issues in ruby 1.9.2
  fix 1.9.2 issue in the staleness checker.
  fix some test running issues in 1.9.2
9819c42
@chriseppstein chriseppstein Add failing test for @media bubbling. 68cf28e
@chriseppstein chriseppstein Restructure a sass tree for all the nodes that bubble up b540a68
@chriseppstein chriseppstein bubble media directives 35b8a7b
@chriseppstein chriseppstein Merge nested media directives during the restructuring phase. 7a45356
@chriseppstein chriseppstein a test for media directives around properties. ae162ce
@chriseppstein chriseppstein A test case to ensure that parent references work across @media blocks. d63a616
@chriseppstein chriseppstein Make media a first class node. e8de202
@chriseppstein chriseppstein Some better code documentation. 22ed3f3
@chriseppstein chriseppstein remove vestigial code. 278a35f
@chriseppstein chriseppstein Merge branch 'stable'
* stable:
  [Sass] Proper fix for GH-275 that doesn't break other tests. Closes GH-281.
  [Haml] Fix :encoding under 1.9.2.
  [Sass] Make rgba respect precision.

Conflicts:
	doc-src/HAML_CHANGELOG.md
	lib/haml/buffer.rb
	lib/haml/engine.rb
	lib/sass/script/color.rb
	lib/sass/script/number.rb
	test/haml/engine_test.rb
85aa757
@chriseppstein chriseppstein Merge branch 'master' into awesome_media
* master: (23 commits)
  [Sass] Proper fix for GH-275 that doesn't break other tests. Closes GH-281.
  Revert "Disable rubyforge releasing until we get a project set up."
  Disable rubyforge releasing until we get a project set up.
  Watch for updates on port 3124.
  [Sass] Support --unix-newlines on Unix.
  [Sass] Properly detect SCSS with -c.
  Fix a typo.
  Clean up a few tests.
  Track the original filename of a sass file even within imports.
  We don't need to assume anything about cached root nodes anymore.
  Propagate Util.dump and Util.load through to child nodes and importers.
  [Emacs] Allow indentation beneath for loops.
  [Sass] Make color names csae-insensitive.
  [Sass] Add a changelog entry about aab0567.
  [Sass] Add an #invert function.
  [Sass] Clean up a little.
  Fix bug in compressed output of SCSS selectors with newlines.
  [Sass] Get rid of Compiler#forbid_update?.
  Allow objects that are not sass trees to be placed into the sass cache.
  Fixed one last reference to Sass::Files.
  ...

Conflicts:
	test/sass/engine_test.rb
4ac1362
@chriseppstein chriseppstein document the @media behavior in the changlog and reference. 9a97871
@nex3
Owner

Merge branch 'media-bubble'

Conflicts:
doc-src/SASS_CHANGELOG.md

Closed by 30371ea

@chriseppstein chriseppstein referenced this pull request from a commit in chriseppstein/sass
@nex3 nex3 Merge branch 'media-bubble'
Conflicts:
	doc-src/SASS_CHANGELOG.md

Closes gh-6
30371ea
This issue was closed.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Commits on Sep 18, 2010
  1. @chriseppstein
  2. @chriseppstein
  3. @chriseppstein
Commits on Sep 22, 2010
  1. @chriseppstein

    Merge branch 'sass-only'

    chriseppstein authored
    * sass-only:
      Fix some numeric rounding issues in ruby 1.9.2
      fix 1.9.2 issue in the staleness checker.
      fix some test running issues in 1.9.2
  2. @chriseppstein
  3. @chriseppstein
Commits on Sep 23, 2010
  1. @chriseppstein

    bubble media directives

    chriseppstein authored
  2. @chriseppstein
  3. @chriseppstein
  4. @chriseppstein
  5. @chriseppstein
  6. @chriseppstein
  7. @chriseppstein

    remove vestigial code.

    chriseppstein authored
Commits on Sep 25, 2010
  1. @chriseppstein

    Merge branch 'stable'

    chriseppstein authored
    * stable:
      [Sass] Proper fix for GH-275 that doesn't break other tests. Closes GH-281.
      [Haml] Fix :encoding under 1.9.2.
      [Sass] Make rgba respect precision.
    
    Conflicts:
    	doc-src/HAML_CHANGELOG.md
    	lib/haml/buffer.rb
    	lib/haml/engine.rb
    	lib/sass/script/color.rb
    	lib/sass/script/number.rb
    	test/haml/engine_test.rb
  2. @chriseppstein

    Merge branch 'master' into awesome_media

    chriseppstein authored
    * master: (23 commits)
      [Sass] Proper fix for GH-275 that doesn't break other tests. Closes GH-281.
      Revert "Disable rubyforge releasing until we get a project set up."
      Disable rubyforge releasing until we get a project set up.
      Watch for updates on port 3124.
      [Sass] Support --unix-newlines on Unix.
      [Sass] Properly detect SCSS with -c.
      Fix a typo.
      Clean up a few tests.
      Track the original filename of a sass file even within imports.
      We don't need to assume anything about cached root nodes anymore.
      Propagate Util.dump and Util.load through to child nodes and importers.
      [Emacs] Allow indentation beneath for loops.
      [Sass] Make color names csae-insensitive.
      [Sass] Add a changelog entry about aab0567.
      [Sass] Add an #invert function.
      [Sass] Clean up a little.
      Fix bug in compressed output of SCSS selectors with newlines.
      [Sass] Get rid of Compiler#forbid_update?.
      Allow objects that are not sass trees to be placed into the sass cache.
      Fixed one last reference to Sass::Files.
      ...
    
    Conflicts:
    	test/sass/engine_test.rb
Commits on Oct 1, 2010
  1. @chriseppstein
This page is out of date. Refresh to see the latest.
View
1  Rakefile
@@ -12,6 +12,7 @@ require 'rake/testtask'
Rake::TestTask.new do |t|
t.libs << 'lib'
+ t.libs << 'test'
test_files = FileList[scope('test/**/*_test.rb')]
test_files.exclude(scope('test/rails/*'))
test_files.exclude(scope('test/plugins/*'))
View
38 doc-src/SASS_CHANGELOG.md
@@ -5,7 +5,43 @@
## 3.2.0 (Unreleased)
+*Because people will ask*: Yes. All the features in this release work in both syntaxes.
+
* Add an {Sass::Script::Functions#invert `invert` function} that takes the inverse of colors.
+* Support for **nested @media directives**. `@media` directives can now be placed in a nested
+ context -- This means they can be used within selector blocks and other `@media` blocks.
+ Sass will then bubble these at-rules up to the top level where they must be in CSS.
+ Example:
+
+ nav {
+ li {
+ float: left;
+ @media screen and (max-width: 500px) {
+ clear: left; } } }
+
+ Generates:
+
+ nav li { float: left; }
+
+ @media screen and (min-width: 500px) {
+ nav li { clear: left; }
+ }
+
+ Similarly, Sass will combine nested `@media` directives for your styling pleasure:
+
+ @media screen {
+ color: red;
+ @media (min-width: 500px) { color: blue; }
+ }
+
+ Generates:
+
+ @media screen { color: red; }
+ @media (screen) and (min-width: 500px) { color: blue; }
+
+ Similarly, selectors nested within `@media` blocks are placed within their full selector
+ context, the parent-reference selector `&` will work as expected, and @media blocks can
+ be safely used within mixins.
### Backwards Incompatibilities -- Must Read!
@@ -20,6 +56,8 @@
## 3.0.19 (Unreleased)
+* Make the alpha value for `rgba` colors respect {Sass::Script::Number::PRECISION}.
+
* Remove all newlines in selectors in `:compressed` mode.
* Make color names case-insensitive.
View
60 doc-src/SASS_REFERENCE.md
@@ -492,6 +492,66 @@ is compiled to:
font-size: 30em;
font-weight: bold; }
+### Nested `@media`
+
+`@media` directives may also be nested at any depth within selector blocks and other
+`@media` blocks. The Sass compiler will bubble these blocks up to the top level where
+they are required to be in CSS. Example:
+
+ nav {
+ li {
+ float: left;
+ @media screen and (max-width: 500px) {
+ clear: left;
+ }
+ }
+ }
+
+Generates:
+
+ nav li { float: left; }
+
+ @media screen and (min-width: 500px) {
+ nav li { clear: left; }
+ }
+
+`@media` blocks can be nested and their queries will be merged:
+
+ @media screen {
+ @media (max-width: 500px) {
+ @media (min-pixel-ratio: 2) {
+ color: red;
+ }
+ }
+ }
+
+Generates:
+
+ @media (screen) and ((max-width: 500px) and (min-pixel-ratio: 2)) {
+ color: red;
+ }
+
+Selector nesting and parent references (`&`) work across nested `@media` blocks too:
+
+ nav {
+ li { float: left; }
+ &.side { background: blue; }
+ @media screen and (max-width: 500px) {
+ li { float: none; }
+ &.side { background: red; }
+ }
+ }
+
+Generates:
+
+ nav li { float: left; }
+ nav.side { background: blue; }
+ @media screen and (max-width: 500px) {
+ nav li { float: none; }
+ nav.side { background: red; }
+ }
+
+
## Comments: `/* */` and `//` {#comments}
Sass supports standard multiline CSS comments with `/* */`,
View
3  lib/sass/engine.rb
@@ -7,6 +7,7 @@
require 'sass/tree/comment_node'
require 'sass/tree/prop_node'
require 'sass/tree/directive_node'
+require 'sass/tree/media_node'
require 'sass/tree/variable_node'
require 'sass/tree/mixin_def_node'
require 'sass/tree/mixin_node'
@@ -628,6 +629,8 @@ def parse_directive(parent, line, root)
:line => @line + 1) unless line.children.empty?
offset = line.offset + line.text.index(value).to_i
Tree::WarnNode.new(parse_script(value, :offset => offset))
+ elsif directive == "media"
+ Tree::MediaNode.new(line.text)
else
Tree::DirectiveNode.new(line.text)
end
View
5 lib/sass/plugin/staleness_checker.rb
@@ -90,7 +90,10 @@ def dependencies_stale?(uri, importer, css_mtime)
return true
end
end
- timestamps[css_mtime] = dependencies(uri, importer).any?(&dependency_updated?(css_mtime))
+ checker = dependency_updated?(css_mtime)
+ timestamps[css_mtime] = dependencies(uri, importer).any? do |dep|
+ checker.call(*dep)
+ end
rescue Sass::SyntaxError
# If there's an error finding dependencies, default to recompiling.
true
View
2  lib/sass/script/color.rb
@@ -401,7 +401,7 @@ def smallest
end
def rgba_str
- "rgba(#{rgb.join(', ')}, #{alpha % 1 == 0.0 ? alpha.to_i : alpha})"
+ "rgba(#{rgb.join(', ')}, #{Number.round(alpha)})"
end
def hex_str
View
33 lib/sass/script/number.rb
@@ -10,6 +10,12 @@ module Sass::Script
# Numbers can also have more complex units, such as `1px*em/in`.
# These cannot be inputted directly in Sass code at the moment.
class Number < Literal
+ # The precision with which numbers will be printed to CSS files.
+ # For example, if this is `1000.0`,
+ # `3.1415926` will be printed as `3.142`.
+ # @api public
+ PRECISION = 1000.0
+
# The Ruby value of the number.
#
# @return [Numeric]
@@ -35,12 +41,6 @@ class Number < Literal
# @return [Boolean, nil]
attr_accessor :original
- # The precision with which numbers will be printed to CSS files.
- # For example, if this is `1000.0`,
- # `3.1415926` will be printed as `3.142`.
- # @api public
- PRECISION = 1000.0
-
# @param value [Numeric] The value of the number
# @param numerator_units [Array<String>] See \{#numerator\_units}
# @param denominator_units [Array<String>] See \{#denominator\_units}
@@ -248,15 +248,7 @@ def to_s
#
# @return [String] The representation
def inspect(opts = {})
- value =
- if self.value.is_a?(Float) && (self.value.infinite? || self.value.nan?)
- self.value
- elsif int?
- self.value.to_i
- else
- (self.value * PRECISION).round / PRECISION
- end
- "#{value}#{unit_str}"
+ "#{self.class.round(self.value)}#{unit_str}"
end
alias_method :to_sass, :inspect
@@ -334,6 +326,17 @@ def unit_str
private
+ # @private
+ def self.round(num)
+ if num.is_a?(Float) && (num.infinite? || num.nan?)
+ num
+ elsif num % 1 == 0.0
+ num.to_i
+ else
+ (num * PRECISION).round / PRECISION
+ end
+ end
+
def operate(other, operation)
this = self
if [:+, :-, :<=, :<, :>, :>=].include?(operation)
View
2  lib/sass/scss/parser.rb
@@ -240,7 +240,7 @@ def use_css_import?; false; end
def media_directive
val = str {media_query_list}.strip
- block(node(Sass::Tree::DirectiveNode.new("@media #{val}")), :directive)
+ block(node(Sass::Tree::MediaNode.new("@media #{val}")), :directive)
end
# http://www.w3.org/TR/css3-mediaqueries/#syntax
View
8 lib/sass/tree/directive_node.rb
@@ -20,6 +20,14 @@ def initialize(value)
super()
end
+ def name
+ value.split(/\s/, 2).first
+ end
+
+ def rest
+ value.split(/\s+/, 2).last
+ end
+
protected
# @see Node#to_src
View
27 lib/sass/tree/media_node.rb
@@ -0,0 +1,27 @@
+module Sass::Tree
+ class MediaNode < DirectiveNode
+
+ def bubbles?(parent)
+ !parent.is_a?(MediaNode)
+ end
+
+ def merges?(parent)
+ parent.is_a?(MediaNode)
+ end
+
+ def query
+ rest.strip
+ end
+
+ def merge_with(node)
+ unless node.is_a?(MediaNode)
+ raise ArgumentError, "Cannot merge with #{node.inspect}"
+ end
+ n = MediaNode.new("@media (#{self.query}) and (#{node.query})")
+ n.children = node.children
+ n.options = options
+ n
+ end
+
+ end
+end
View
76 lib/sass/tree/node.rb
@@ -269,6 +269,82 @@ def _after_load
@children = Sass::Util.load(@children)
end
+ # Restructures a static Sass tree (e.g. the output of \{#perform})
+ # into another static Sass tree. This allows a single node in the
+ # tree to rewrite itself as an array of nodes that should replace it
+ # in its parent's child array.
+ #
+ # This step is used to bubble nodes up the tree and to merge
+ # nodes with their parent in cases where CSS does not understand them
+ # in a nested context.
+ #
+ # \{#restructure} generally shouldn't be overridden directly;
+ # instead, override \{#bubbles?}, \{#merges?}.
+ #
+ # @return [Array<Tree::Node>] The resulting tree of static nodes
+ # @see Sass::Tree
+ # @see Sass::Tree::RootNode#restructure
+ def restructure
+ # First we restructure the children and replace them with their returned array of nodes
+ new_children = children.map {|c| c.restructure}.flatten
+ unless new_children.any?{|c| c.bubbles?(self) || c.merges?(self)}
+ # No more restructuring to do just return.
+ self.children = new_children
+ return [self]
+ end
+ # break the children into sets of nodes separated by nodes that need to be restructured.
+ # The parent node will be duplicated/replaced for each set.
+ child_groups = [[]]
+ new_children.each do |child|
+ if child.bubbles?(self) || child.merges?(self)
+ child_groups << [child]
+ child_groups << []
+ else
+ child_groups.last << child
+ end
+ end
+ # The above approach can generate empty sets -- dump them
+ child_groups.reject!{|group| group.empty?}
+
+ # perform the restructuring by bubbling and merging those
+ # nodes which require it.
+ replacements = child_groups.map do |children|
+ node = self.dup
+ if children.size == 1 && children.first.bubbles?(self)
+ # swap the child and parent in the tree
+ node, child = children.first, node
+ child.children = node.children
+ node.children = [child]
+ elsif children.size == 1 && children.first.merges?(self)
+ # merge the child and parent in the tree
+ node.children = []
+ node = node.merge_with(children.first)
+ else
+ node.children = children
+ end
+ node
+ end
+ replacements
+ end
+
+ # Whether this node should bubble up to the next level
+ def bubbles?(parent)
+ false
+ end
+
+ # Whether this node should bubble up to the next level
+ def merges?(parent)
+ false
+ end
+
+ # Merges this node with another, returning a new node
+ #
+ # Any node that returns true for \{#merges?} should
+ # implement this method.
+ def merge_with(parent)
+ Sass::Util.abstract(self)
+ end
+
protected
# Computes the CSS corresponding to this particular Sass node.
View
12 lib/sass/tree/root_node.rb
@@ -26,11 +26,21 @@ def to_s(*args)
# @see #perform
# @see #to_s
def render
- result, extends = perform(Environment.new).cssize
+ result = self.perform(Environment.new)
+ result = result.restructure
+ result, extends = result.cssize
result = result.do_extend(extends) unless extends.empty?
result.to_s
end
+ # The root node is always on the top and it needn't be restructured
+ # so it does not return an array.
+ def restructure
+ node = self.dup
+ node.children = children.map {|c| c.restructure}.flatten
+ return node
+ end
+
# @see Node#perform
def perform(environment)
environment.options = @options if environment.options.nil? || environment.options.empty?
View
2  lib/sass/tree/rule_node.rb
@@ -134,7 +134,7 @@ def _to_s(tabs)
per_rule_indent, total_indent = [:nested, :expanded].include?(style) ? [rule_indent, ''] : ['', rule_indent]
total_rule = total_indent + resolved_rules.members.
- map {|seq| seq.to_a.join.gsub("\n", style == :compressed ? " " : "\n")}.
+ map {|seq| seq.to_a.join.gsub(/([^,])\n/m, style == :compressed ? '\1 ' : "\\1\n")}.
join(rule_separator).split("\n").map do |line|
per_rule_indent + line.strip
end.join(line_separator)
View
109 test/sass/engine_test.rb
@@ -2211,6 +2211,115 @@ def test_utf32be_bom
end
end
+ def test_media_directives_bubble_up_to_the_top_level
+ assert_equal <<CSS, render(<<SASS)
+.outside {
+ color: red; }
+
+@media print {
+ .outside {
+ color: black; }
+ .outside .inside {
+ border: 1px solid black; } }
+
+.outside {
+ background: blue; }
+ .outside .middle {
+ display: block; }
+CSS
+.outside
+ color: red
+ @media print
+ color: black
+ .inside
+ border: 1px solid black
+ background: blue
+ .middle
+ display: block
+SASS
+ end
+
+ def test_nested_media
+ sass_str = <<SASS
+.outside
+ color: red
+ @media print
+ color: black
+ @media nested
+ .inside
+ border: 1px solid black
+ background: blue
+ .middle
+ display: block
+SASS
+ css_str = <<CSS
+.outside {
+ color: red; }
+
+@media print {
+ .outside {
+ color: black; } }
+
+@media (print) and (nested) {
+ .outside .inside {
+ border: 1px solid black; } }
+
+.outside {
+ background: blue; }
+ .outside .middle {
+ display: block; }
+CSS
+ assert_equal css_str, render(sass_str)
+ end
+
+ def test_nested_media_around_properties
+ sass_str = <<SASS
+.outside
+ color: red
+ @media print
+ color: black
+ .inside
+ @media nested
+ border: 1px solid black
+ background: blue
+ .middle
+ display: block
+SASS
+ css_str = <<CSS
+.outside {
+ color: red; }
+
+@media print {
+ .outside {
+ color: black; } }
+
+@media (print) and (nested) {
+ .outside .inside {
+ border: 1px solid black; } }
+
+.outside {
+ background: blue; }
+ .outside .middle {
+ display: block; }
+CSS
+ assert_equal css_str, render(sass_str)
+ end
+
+ def test_media_with_parent_references
+ sass_str = <<SASS
+.outside
+ @media print
+ &.inside
+ border: 1px solid black
+SASS
+ css_str = <<CSS
+@media print {
+ .outside.inside {
+ border: 1px solid black; } }
+CSS
+ assert_equal css_str, render(sass_str)
+ end
+
def test_original_filename_set
importer = MockImporter.new
importer.add_import("imported", "div{color:red}")
View
2  test/sass/functions_test.rb
@@ -1,6 +1,6 @@
#!/usr/bin/env ruby
require 'test/unit'
-require File.dirname(__FILE__) + '/../test_helper'
+require 'test_helper'
require 'sass/script'
module Sass::Script::Functions::UserFunctions
View
4 test/sass/script_test.rb
@@ -79,6 +79,10 @@ def test_rgba_number_math
assert_equal "rgba(100, 100, 100, 0.75)", resolve("rgba(50, 50, 50, 0.75) * 2")
end
+ def test_rgba_rounding
+ assert_equal "rgba(10, 1, 0, 0.123)", resolve("rgba(10.0, 1.23456789, 0.0, 0.1234567)")
+ end
+
def test_compressed_colors
assert_equal "#123456", resolve("#123456", :style => :compressed)
assert_equal "rgba(1, 2, 3, 0.5)", resolve("rgba(1, 2, 3, 0.5)", :style => :compressed)
View
128 test/sass/scss/scss_test.rb
@@ -1023,6 +1023,134 @@ def test_no_interpolation_in_unrecognized_directives
SCSS
end
+ # XXX TODO: test several medias in the same nesting level and at different nesting levels
+ # XXX TODO: media nesting
+ def test_media_directives_bubble_up_to_the_top_level
+ assert_equal <<CSS, render(<<SCSS)
+.outside {
+ color: red; }
+
+@media print {
+ .outside {
+ color: black; }
+ .outside .inside {
+ border: 1px solid black; } }
+
+.outside {
+ background: blue; }
+ .outside .middle {
+ display: block; }
+CSS
+.outside {
+ color: red;
+ @media print {
+ color: black;
+ .inside {
+ border: 1px solid black;
+ }
+ }
+ background: blue;
+ .middle {
+ display: block;
+ }
+}
+SCSS
+ end
+
+ def test_nested_media
+ scss_str = <<SCSS
+.outside {
+ color: red;
+ @media print {
+ color: black;
+ @media nested {
+ .inside {
+ border: 1px solid black;
+ }
+ }
+ }
+ background: blue;
+ .middle {
+ display: block;
+ }
+}
+SCSS
+ css_str = <<CSS
+.outside {
+ color: red; }
+
+@media print {
+ .outside {
+ color: black; } }
+
+@media (print) and (nested) {
+ .outside .inside {
+ border: 1px solid black; } }
+
+.outside {
+ background: blue; }
+ .outside .middle {
+ display: block; }
+CSS
+ assert_equal css_str, render(scss_str)
+ end
+
+ def test_nested_media_around_properties
+ scss_str = <<SCSS
+.outside {
+ color: red;
+ @media print {
+ color: black;
+ .inside {
+ @media nested {
+ border: 1px solid black;
+ }
+ }
+ }
+ background: blue;
+ .middle {
+ display: block;
+ }
+}
+SCSS
+ css_str = <<CSS
+.outside {
+ color: red; }
+
+@media print {
+ .outside {
+ color: black; } }
+
+@media (print) and (nested) {
+ .outside .inside {
+ border: 1px solid black; } }
+
+.outside {
+ background: blue; }
+ .outside .middle {
+ display: block; }
+CSS
+ assert_equal css_str, render(scss_str)
+ end
+
+ def test_media_with_parent_references
+ scss_str = <<SCSS
+.outside {
+ @media print {
+ &.inside {
+ border: 1px solid black;
+ }
+ }
+}
+SCSS
+ css_str = <<CSS
+@media print {
+ .outside.inside {
+ border: 1px solid black; } }
+CSS
+ assert_equal css_str, render(scss_str)
+ end
+
# Regression
def test_weird_added_space
Something went wrong with that request. Please try again.