diff --git a/middleman-more/features/minify_css.feature b/middleman-more/features/minify_css.feature index 3695ad87..80b3f8ce 100644 --- a/middleman-more/features/minify_css.feature +++ b/middleman-more/features/minify_css.feature @@ -5,13 +5,15 @@ Feature: Minify CSS Given "minify_css" feature is "disabled" And the Server is running at "minify-css-app" When I go to "/stylesheets/site.css" - Then I should see "55" lines + Then I should see "60" lines + And I should see "only screen and (device-width" Scenario: Rendering external css with the feature enabled Given "minify_css" feature is "enabled" And the Server is running at "minify-css-app" When I go to "/stylesheets/site.css" Then I should see "1" lines + And I should see "only screen and (device-width" Scenario: Rendering external css with passthrough compressor Given the Server is running at "passthrough-app" diff --git a/middleman-more/fixtures/minify-css-app/source/stylesheets/site.css.sass b/middleman-more/fixtures/minify-css-app/source/stylesheets/site.css.sass index d143e05e..0cf9e7da 100755 --- a/middleman-more/fixtures/minify-css-app/source/stylesheets/site.css.sass +++ b/middleman-more/fixtures/minify-css-app/source/stylesheets/site.css.sass @@ -1 +1,5 @@ -@import "compass/reset" \ No newline at end of file +@import "compass/reset" + +@media handheld, only screen and (device-width: 768px) + body + display: block \ No newline at end of file diff --git a/middleman-more/lib/middleman-more/extensions/minify_css.rb b/middleman-more/lib/middleman-more/extensions/minify_css.rb index a7a8c0b9..f90c8848 100644 --- a/middleman-more/lib/middleman-more/extensions/minify_css.rb +++ b/middleman-more/lib/middleman-more/extensions/minify_css.rb @@ -12,8 +12,8 @@ module Middleman::Extensions # Tell Sprockets to use the built in CSSMin app.after_configuration do if !css_compressor - require "middleman-more/extensions/minify_css/cssmin" - set :css_compressor, ::CSSMin + require "middleman-more/extensions/minify_css/rainpress" + set :css_compressor, ::Rainpress end end end diff --git a/middleman-more/lib/middleman-more/extensions/minify_css/cssmin.rb b/middleman-more/lib/middleman-more/extensions/minify_css/cssmin.rb deleted file mode 100644 index 8a84885b..00000000 --- a/middleman-more/lib/middleman-more/extensions/minify_css/cssmin.rb +++ /dev/null @@ -1,60 +0,0 @@ -#-- -# Copyright (c) 2008 Ryan Grove -# All rights reserved. -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions are met: -# -# * Redistributions of source code must retain the above copyright notice, -# this list of conditions and the following disclaimer. -# * Redistributions in binary form must reproduce the above copyright notice, -# this list of conditions and the following disclaimer in the documentation -# and/or other materials provided with the distribution. -# * Neither the name of this project nor the names of its contributors may be -# used to endorse or promote products derived from this software without -# specific prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" -# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE -# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL -# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR -# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER -# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, -# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -#++ - -# Minify CSS -module CSSMin - - # Compress a CSS string - # @param [String] input - # @return [String] - def self.compress(input) - css = input.is_a?(IO) ? input.read : input.dup.to_s - css.gsub!(/\/\*[\s\S]*?\*\//, '') - css.gsub!(/\s+/, ' ') - css.gsub!(/"\\"\}\\""/, '___BMH___') - css.gsub!(/(?:^|\})[^\{:]+\s+:+[^\{]*\{/) do |match| - match.gsub(':', '___PSEUDOCLASSCOLON___') - end - css.gsub!(/\s+([!\{\};:>+\(\)\],])/, '\1') - css.gsub!('___PSEUDOCLASSCOLON___', ':') - css.gsub!(/([!\{\}:;>+\(\[,])\s+/, '\1') - css.gsub!(/([^;\}])\}/, '\1;}') - css.gsub!(/([\s:])([+-]?0)(?:%|em|ex|px|in|cm|mm|pt|pc)/i, '\1\2') - css.gsub!(/:(?:0 )+0;/, ':0;') - css.gsub!('background-position:0;', 'background-position:0 0;') - css.gsub!(/(:|\s)0+\.(\d+)/, '\1.\2') - css.gsub!(/rgb\s*\(\s*([0-9,\s]+)\s*\)/) do |match| - '#' << $1.scan(/\d+/).map{|n| n.to_i.to_s(16).rjust(2, '0') }.join - end - css.gsub!(/([^"'=\s])\s*#([0-9a-f])\2([0-9a-f])\3([0-9a-f])\4/i, '\1 #\2\3\4') - css.gsub!(/[^\}]+\{;\}\n/, '') - css.gsub!('___BMH___', '"\"}\""') - css.gsub!(/;;+/, ';') - css.strip - end -end \ No newline at end of file diff --git a/middleman-more/lib/middleman-more/extensions/minify_css/rainpress.rb b/middleman-more/lib/middleman-more/extensions/minify_css/rainpress.rb new file mode 100644 index 00000000..20790696 --- /dev/null +++ b/middleman-more/lib/middleman-more/extensions/minify_css/rainpress.rb @@ -0,0 +1,168 @@ +# == Information +# +# This is the main class of Rainpress, create an instance of it to compress +# your CSS-styles. +# +# Author:: Uwe L. Korn +# +# Options: +# +# * :comments - if set to false, comments will not be removed +# * :newlines - if set to false, newlines will not be removed +# * :spaces - if set to false, spaces will not be removed +# * :colors - if set to false, colors will not be modified +# * :misc - if set to false, miscellaneous compression parts will be skipped +class Rainpress + # Quick-compress the styles. + # This eliminates the need to create an instance of the class + def self.compress(style, options = {}) + self.new(style, options).compress! + end + + def initialize(style, opts = {}) + @style = style + @opts = { + :comments => true, + :newlines => true, + :spaces => true, + :colors => true, + :misc => true + } + @opts.merge! opts + end + + # Run the compressions and return the newly compressed text + def compress! + remove_comments! if @opts[:comments] + remove_newlines! if @opts[:newlines] + remove_spaces! if @opts[:spaces] + shorten_colors! if @opts[:colors] + do_misc! if @opts[:misc] + @style + end + + # Remove all comments out of the CSS-Document + # + # Only /* text */ comments are supported. + # Attention: If you are doing css hacks for IE using the comment tricks, + # they will be removed using this function. Please consider for IE css style + # corrections the usage of conditionals comments in your (X)HTML document. + def remove_comments! + input = @style + @style = '' + + while input.length > 0 do + pos = input.index("/*"); + + # No more comments + if pos == nil + @style += input + input = ''; + else # Comment beginning at pos + @style += input[0..(pos-1)] if pos > 0 # only append text if there is some + input = input[(pos+2)..-1] + # Comment ending at pos + pos = input.index("*/") + input = input[(pos+2)..-1] + end + end + end + + # Remove all newline characters + # + # We take care of Windows(\r\n), Unix(\n) and Mac(\r) newlines. + def remove_newlines! + @style.gsub! /\n|\r/, '' + end + + # Remove unneeded spaces + # + # 1. Turn mutiple spaces into a single + # 2. Remove spaces around ;:{}, + # 3. Remove tabs + def remove_spaces! + @style.gsub! /\s*(\s|;|:|\}|\{|,)\s*/, '\1' + @style.gsub! "\t", '' + end + + # Replace color values with their shorter equivalent + # + # 1. Turn rgb(,,)-colors into #-values + # 2. Shorten #AABBCC down to #ABC + # 3. Replace names with their shorter hex-equivalent + # * white -> #fff + # * black -> #000 + # 4. Replace #-values with their shorter name + # * #f00 -> red + def shorten_colors! + # rgb(50,101,152) to #326598 + @style.gsub! /rgb\s*\(\s*([0-9,\s]+)\s*\)/ do |match| + out = '#' + $1.split(',').each do |num| + out += '0' if num.to_i < 16 + out += num.to_i.to_s(16) # convert to hex + end + out + end + # Convert #AABBCC to #ABC, keep if preceed by a '=' + @style.gsub! /([^\"'=\s])(\s*)#([\da-f])\3([\da-f])\4([\da-f])\5/i, '\1#\3\4\5' + + # At the moment we assume that colours only appear before ';' or '}' and + # after a ':', if there could be an occurence of a color before or after + # an other character, submit either a bug report or, better, a patch that + # enables Rainpress to take care of this. + + # shorten several names to numbers + ## shorten white -> #fff + @style.gsub! /:\s*white\s*(;|\})/, ':#fff\1' + + ## shorten black -> #000 + @style.gsub! /:\s*black\s*(;|\})/, ':#000\1' + + # shotern several numbers to names + ## shorten #f00 or #ff0000 -> red + @style.gsub! /:\s*#f{1,2}0{2,4}(;|\})/i, ':red\1' + end + + # Do miscellaneous compression methods on the style. + def do_misc! + # Replace 0(pt,px,em,%) with 0 but only when preceded by : or a white-space + @style.gsub! /([\s:]+)(0)(px|em|%|in|cm|mm|pc|pt|ex)/i, '\1\2' + + # Replace :0 0 0 0(;|}) with :0(;|}) + @style.gsub! /:0 0 0 0(;|\})/, ':0\1' + + # Replace :0 0 0(;|}) with :0(;|}) + @style.gsub! /:0 0 0(;|\})/, ':0\1' + + # Replace :0 0(;|}) with :0(;|}) + @style.gsub! /:0 0(;|\})/, ':0\1' + + # Replace background-position:0; with background-position:0 0; + @style.gsub! 'background-position:0;', 'background-position:0 0;' + + # Replace 0.6 to .6, but only when preceded by : or a white-space + @style.gsub! /[:\s]0+\.(\d+)/ do |match| + match.sub '0', '' # only first '0' !! + end + + # Replace multiple ';' with a single ';' + @style.gsub! /[;]+/, ';' + + # Replace ;} with } + @style.gsub! ';}', '}' + + # Replace font-weight:normal; with 400 + @style.gsub! /font-weight[\s]*:[\s]*normal[\s]*(;|\})/i,'font-weight:400\1' + @style.gsub! /font[\s]*:[\s]*normal[\s;\}]*/ do |match| + match.sub 'normal', '400' + end + + # Replace font-weight:bold; with 700 + @style.gsub! /font-weight[\s]*:[\s]*bold[\s]*(;|\})/,'font-weight:700\1' + @style.gsub! /font[\s]*:[\s]*bold[\s;\}]*/ do |match| + match.sub 'bold', '700' + end + end + +end \ No newline at end of file