# =========================================================================== # Project: Abbot - SproutCore Build Tools # Copyright: ©2009 Apple Inc. # portions copyright @2006-2009 Sprout Systems, Inc. # and contributors # =========================================================================== # module and class name have been modified # # == MIT License # See http://code.google.com/p/rainpress/ # # == About # # Rainpress is a compressor for CSS. It's written in ruby, but should not be # limited to ruby projects. # # Rainpress does not apply common compression algorithms like gzip, it removes # unnecessary characters and replaces some attributes with a shorter equivalent # name. # # == Links # # * {Rainpress Website}[http://rainpress.xhochy.com/] # * {SVN repository}[http://code.google.com/p/rainpress/source] # * {Bugtracker}[https://bugs.launchpad.net/rainpress/] # * {Wiki}[http://code.google.com/p/rainpress/w/list] # * {Translations}[https://translations.launchpad.net/rainpress/] # * {XhochY Weblog (for Announcements about Rainpress)}[http://xhochy.org/en/] # * {Mailinglist}[http://groups.google.com/group/xy-oss-projects-discussion] # * {Continous Integration Builds and Tests}[http://cruisecontrol-rb.xhochy.com/builds/rainpress] # * {Freshmeat Record}[http://freshmeat.net/projects/rainpress] module SC::Helpers # == Information # # This is the main class of Rainpress, create an instance of it to compress # your CSS-styles. # # == Simple Usage # # packer = SproutCore::CSSPacker.new # compressed_style = packer.compress(style) class CSSPacker # Use always this functions if you want to compress your CSS-style # # Options: # # * :preserveComments - if set to true, comments will not be # removed # * :preserveNewline - if set to true, newlines will not be removed # * :preserveSpaces - if set to true, spaces will not be removed # * :preserveColors - if set to true, colors will not be modified # * :skipMisc - if set to true, miscellaneous compression parts # will be skipped def compress(style, options = {}) # remove comments style = remove_comments(style) unless options[:preserveComments] # remove newlines style = remove_newlines(style) unless options[:preserveNewlines] # remove unneeded spaces style = remove_spaces(style) unless options[:preserveSpaces] # replace colours with shorter names style = shorten_colors(style) unless options[:preserveColors] # make all other things style = do_misc(style) unless options[:skipMisc] style end # Remove all comments out of the CSS-Document def remove_comments(script) input = script script = '' while input.length > 0 do pos = input.index("/*"); # No more comments if pos == nil script += input input = ''; else # Comment beginning at pos script += 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 # return script end # Remove all newline characters def remove_newlines(script) script.gsub(/\n|\r/,'') end # 1. Turn mutiple spaces into a single # 2. Remove spaces around ;:{}, # 3. Remove tabs def remove_spaces(script) script = script.gsub(/(\s(\s)+)/, ' ') script = script.gsub(/\s*;\s*/,';') script = script.gsub(/\s*:\s*/,':') script = script.gsub(/\s*\{\s*/,'{') script = script.gsub(/\s*\}\s*/,'}') script = script.gsub(/\s*,\s*/,',') script.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(style) # rgb(50,101,152) to #326598 style = style.gsub(/rgb\s*\(\s*([0-9,\s]+)\s*\)/) do |match| out = '#' $1.split(',').each do |num| if num.to_i < 16 out += '0' end out += num.to_i.to_s(16) # convert to hex end out end # #AABBCC to #ABC, keep if preceed by a '=' style = style.gsub(/([^\"'=\s])(\s*)#([0-9a-fA-F])([0-9a-fA-F])([0-9a-fA-F])([0-9a-fA-F])([0-9a-fA-F])([0-9a-fA-F])/) do |match| out = match if ($3.downcase == $4.downcase) and ($5.downcase == $6.downcase) and ($7.downcase == $8.downcase) out = $1 + '#' + $3.downcase + $5.downcase + $7.downcase end out end # shorten several names to numbers style = style.gsub(/:[\s]*white[\s]*;/, ':#fff;') style = style.gsub(/:[\s]*white[\s]*\}/, ':#fff}') style = style.gsub(/:[\s]*black[\s]*;/, ':#000;') style = style.gsub(/:[\s]*black[\s]*\}/, ':#000}') # shotern several numbers to names style = style.gsub(/:[\s]*#([fF]00|[fF]{2}0000);/, ':red;') style = style.gsub(/:[\s]*#([fF]00|[fF]{2}0000)\}/, ':red}') style end # Do miscellaneous compression methods on the style def do_misc(script) # Replace 0(pt,px,em,%) with 0 but only when preceded by : or a white-space script = script.gsub(/([\s:]+)(0)(px|em|%|in|cm|mm|pc|pt|ex)/) do |match| match.gsub(/(px|em|%|in|cm|mm|pc|pt|ex)/,'') end # Replace 0 0 0 0; with 0. script = script.gsub(':0 0 0 0;', ':0;') script = script.gsub(':0 0 0 0}', ':0}') script = script.gsub(':0 0 0;', ':0;') script = script.gsub(':0 0 0}', ':0}') script = script.gsub(':0 0}', ':0}') script = script.gsub(':0 0;', ':0;') # Replace background-position:0; with background-position:0 0; script = script.gsub('background-position:0;', 'background-position:0 0;'); # Replace 0.6 to .6, but only when preceded by : or a white-space script = script.gsub(/[:\s]0+\.(\d+)/) do |match| match.sub('0', '') # only first '0' !! end # Replace ;;;; with ; script = script.gsub(/[;]+/, ';') # Replace ;} with } script = script.gsub(';}', '}') # Replace background-color: with background: script = script.gsub('background-color:', 'background:') # Replace font-weight:normal; with 400, bold with 700 script = script.gsub(/font-weight[\s]*:[\s]*normal[\s]*;/,'font-weight:400;') script = script.gsub(/font-weight[\s]*:[\s]*normal[\s]*\}/,'font-weight:400}') script = script.gsub(/font[\s]*:[\s]*normal[\s;\}]*/) do |match| match.sub('normal', '400') end script = script.gsub(/font-weight[\s]*:[\s]*bold[\s]*;/,'font-weight:700;') script = script.gsub(/font-weight[\s]*:[\s]*bold[\s]*\}/,'font-weight:700}') script = script.gsub(/font[\s]*:[\s]*bold[\s;\}]*/) do |match| match.sub('bold', '700') end script end end end