css_press 0.3.0

Sign up to get free protection for your applications and to get access to all the features.
data/.gitignore ADDED
@@ -0,0 +1,5 @@
1
+ *.gem
2
+ .bundle
3
+ Gemfile.lock
4
+ pkg/*
5
+ /.project
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source "http://rubygems.org"
2
+
3
+ # Specify your gem's dependencies in css_press.gemspec
4
+ gemspec
data/Rakefile ADDED
@@ -0,0 +1,10 @@
1
+ require "bundler/gem_tasks"
2
+
3
+ [:build, :install, :release].each do |task_name|
4
+ Rake::Task[task_name].prerequisites << :spec
5
+ end
6
+
7
+ require "rspec/core/rake_task"
8
+ RSpec::Core::RakeTask.new
9
+
10
+ task :default => :spec
data/Readme.md ADDED
@@ -0,0 +1,14 @@
1
+ # css_press
2
+
3
+ ## Main goals & principles
4
+
5
+ - Compress css, but without support of any kind comment hacks. There are better ways to do crossbrowser styling. For example, [IE conditional comments](http://paulirish.com/2008/conditional-stylesheets-vs-css-hacks-answer-neither/).
6
+ - Compression done with the help of real css parser ([csspool](https://github.com/tenderlove/csspool)). Not Regexps.
7
+ - Pure ruby.
8
+
9
+ ## Alternatives
10
+
11
+ - [rainpres](https://github.com/sprsquish/rainpress). Seems to be dead ☠.
12
+ - [css-compressor](https://github.com/codenothing/css-compressor). The best of the best. P☣P :(
13
+ - [yuicompressor](https://github.com/yui/yuicompressor). Java
14
+ - [ruby-yui-compressor](https://github.com/sstephenson/ruby-yui-compressor). Ruby wrapper for yuicompressor
data/css_press.gemspec ADDED
@@ -0,0 +1,25 @@
1
+ # -*- encoding: utf-8 -*-
2
+ $:.push File.expand_path("../lib", __FILE__)
3
+ require "css_press/version"
4
+
5
+ Gem::Specification.new do |s|
6
+ s.name = "css_press"
7
+ s.version = CssPress::VERSION
8
+ s.authors = ["stereobooster"]
9
+ s.email = ["stereobooster@gmail.com"]
10
+ s.homepage = ""
11
+ s.summary = %q{Compress CSS}
12
+ s.description = %q{Ruby gem for compressing CSS}
13
+
14
+ s.rubyforge_project = "css_press"
15
+
16
+ s.files = `git ls-files`.split("\n")
17
+ s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
18
+ s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
19
+ s.require_paths = ["lib"]
20
+
21
+ s.add_dependency "csspool-st", "3.1.2"
22
+
23
+ s.add_development_dependency "rake"
24
+ s.add_development_dependency "rspec"
25
+ end
@@ -0,0 +1,11 @@
1
+ {
2
+ "808080":"gray",
3
+ "008000":"green",
4
+ "800000":"maroon",
5
+ "000080":"navy",
6
+ "808000":"olive",
7
+ "ff0000":"red",
8
+ "c0c0c0":"silver",
9
+ "008080":"teal",
10
+ "f00":"red"
11
+ }
@@ -0,0 +1,34 @@
1
+ {
2
+ "f0ffff":"azure",
3
+ "f5f5dc":"beige",
4
+ "ffe4c4":"bisque",
5
+ "a52a2a":"brown",
6
+ "ff7f50":"coral",
7
+ "ffd700":"gold",
8
+ "808080":"gray",
9
+ "008000":"green",
10
+ "4b0082":"indigo",
11
+ "fffff0":"ivory",
12
+ "f0e68c":"khaki",
13
+ "faf0e6":"linen",
14
+ "800000":"maroon",
15
+ "000080":"navy",
16
+ "808000":"olive",
17
+ "ffa500":"orange",
18
+ "da70d6":"orchid",
19
+ "cd853f":"peru",
20
+ "ffc0cb":"pink",
21
+ "dda0dd":"plum",
22
+ "800080":"purple",
23
+ "ff0000":"red",
24
+ "fa8072":"salmon",
25
+ "a0522d":"sienna",
26
+ "c0c0c0":"silver",
27
+ "fffafa":"snow",
28
+ "d2b48c":"tan",
29
+ "008080":"teal",
30
+ "ff6347":"tomato",
31
+ "ee82ee":"violet",
32
+ "f5deb3":"wheat",
33
+ "f00":"red"
34
+ }
@@ -0,0 +1,122 @@
1
+ {
2
+ "aliceblue":"f0f8ff",
3
+ "antiquewhite":"faebd7",
4
+ "aquamarine":"7fffd4",
5
+ "bisque":"ffe4c4",
6
+ "black":"000",
7
+ "blanchedalmond":"ffebcd",
8
+ "blueviolet":"8a2be2",
9
+ "burlywood":"deb887",
10
+ "cadetblue":"5f9ea0",
11
+ "chartreuse":"7fff00",
12
+ "chocolate":"d2691e",
13
+ "coral":"ff7f50",
14
+ "cornflowerblue":"6495ed",
15
+ "cornsilk":"fff8dc",
16
+ "crimson":"dc143c",
17
+ "cyan":"0ff",
18
+ "darkblue":"00008b",
19
+ "darkcyan":"008b8b",
20
+ "darkgoldenrod":"b8860b",
21
+ "darkgray":"a9a9a9",
22
+ "darkgreen":"006400",
23
+ "darkkhaki":"bdb76b",
24
+ "darkmagenta":"8b008b",
25
+ "darkolivegreen":"556b2f",
26
+ "darkorange":"ff8c00",
27
+ "darkorchid":"9932cc",
28
+ "darkred":"8b0000",
29
+ "darksalmon":"e9967a",
30
+ "darkseagreen":"8fbc8f",
31
+ "darkslateblue":"483d8b",
32
+ "darkslategray":"2f4f4f",
33
+ "darkturquoise":"00ced1",
34
+ "darkviolet":"9400d3",
35
+ "deeppink":"ff1493",
36
+ "deepskyblue":"00bfff",
37
+ "dimgray":"696969",
38
+ "dodgerblue":"1e90ff",
39
+ "firebrick":"b22222",
40
+ "floralwhite":"fffaf0",
41
+ "forestgreen":"228b22",
42
+ "fuchsia":"f0f",
43
+ "gainsboro":"dcdcdc",
44
+ "ghostwhite":"f8f8ff",
45
+ "goldenrod":"daa520",
46
+ "green":"008000",
47
+ "greenyellow":"adff2f",
48
+ "honeydew":"f0fff0",
49
+ "hotpink":"ff69b4",
50
+ "indianred ":"cd5c5c",
51
+ "indigo ":"4b0082",
52
+ "lavender":"e6e6fa",
53
+ "lavenderblush":"fff0f5",
54
+ "lawngreen":"7cfc00",
55
+ "lemonchiffon":"fffacd",
56
+ "lightblue":"add8e6",
57
+ "lightcoral":"f08080",
58
+ "lightcyan":"e0ffff",
59
+ "lightgoldenrodyellow":"fafad2",
60
+ "lightgrey":"d3d3d3",
61
+ "lightgreen":"90ee90",
62
+ "lightpink":"ffb6c1",
63
+ "lightsalmon":"ffa07a",
64
+ "lightseagreen":"20b2aa",
65
+ "lightskyblue":"87cefa",
66
+ "lightslategray":"789",
67
+ "lightsteelblue":"b0c4de",
68
+ "lightyellow":"ffffe0",
69
+ "lime":"0f0",
70
+ "limegreen":"32cd32",
71
+ "magenta":"f0f",
72
+ "maroon":"800000",
73
+ "mediumaquamarine":"66cdaa",
74
+ "mediumblue":"0000cd",
75
+ "mediumorchid":"ba55d3",
76
+ "mediumpurple":"9370d8",
77
+ "mediumseagreen":"3cb371",
78
+ "mediumslateblue":"7b68ee",
79
+ "mediumspringgreen":"00fa9a",
80
+ "mediumturquoise":"48d1cc",
81
+ "mediumvioletred":"c71585",
82
+ "midnightblue":"191970",
83
+ "mintcream":"f5fffa",
84
+ "mistyrose":"ffe4e1",
85
+ "moccasin":"ffe4b5",
86
+ "navajowhite":"ffdead",
87
+ "oldlace":"fdf5e6",
88
+ "olivedrab":"6b8e23",
89
+ "orange":"ffa500",
90
+ "orangered":"ff4500",
91
+ "orchid":"da70d6",
92
+ "palegoldenrod":"eee8aa",
93
+ "palegreen":"98fb98",
94
+ "paleturquoise":"afeeee",
95
+ "palevioletred":"d87093",
96
+ "papayawhip":"ffefd5",
97
+ "peachpuff":"ffdab9",
98
+ "powderblue":"b0e0e6",
99
+ "purple":"800080",
100
+ "rosybrown":"bc8f8f",
101
+ "royalblue":"4169e1",
102
+ "saddlebrown":"8b4513",
103
+ "salmon":"fa8072",
104
+ "sandybrown":"f4a460",
105
+ "seagreen":"2e8b57",
106
+ "seashell":"fff5ee",
107
+ "sienna":"a0522d",
108
+ "silver":"c0c0c0",
109
+ "skyblue":"87ceeb",
110
+ "slateblue":"6a5acd",
111
+ "slategray":"708090",
112
+ "springgreen":"00ff7f",
113
+ "steelblue":"4682b4",
114
+ "thistle":"d8bfd8",
115
+ "tomato":"ff6347",
116
+ "turquoise":"40e0d0",
117
+ "violet":"ee82ee",
118
+ "white":"fff",
119
+ "whitesmoke":"f5f5f5",
120
+ "yellow":"ff0",
121
+ "yellowgreen":"9acd32"
122
+ }
data/lib/css_press.rb ADDED
@@ -0,0 +1,11 @@
1
+ require "css_press/version"
2
+ require "css_press/css"
3
+ require "css_press/min_css"
4
+ require "css_press/node"
5
+ require "css_press/color"
6
+
7
+ module CssPress
8
+ def self.press(text, options = {})
9
+ CssPress::Css.new(options).press text
10
+ end
11
+ end
@@ -0,0 +1,69 @@
1
+ require 'json'
2
+
3
+ module CSSPool
4
+ class Color
5
+ class << self
6
+
7
+ def load name
8
+ data = File.read(File.expand_path("../../colors/#{name}.json", __FILE__))
9
+ JSON.parse(data)
10
+ end
11
+
12
+ [:hex_to_short, :hex_to_safe, :long_to_hex].each do |name|
13
+ define_method name do |val|
14
+ @colors ||= {}
15
+ @colors[name] ||= load name.to_s
16
+ @colors[name][val]
17
+ end
18
+ end
19
+
20
+ def color_val value, result, hash
21
+ val = result.nil? ? value : result
22
+ if hash && val.length == 6 && val[0] == val[1] && val[2] == val[3] && val[4] == val[5] then
23
+ val = result = val[0] + val[2] + val[4]
24
+ end
25
+ if !result.nil? then
26
+ result = hash ? "##{val}" : val
27
+ end
28
+ result
29
+ end
30
+
31
+ def min_hex value
32
+ val = value.downcase
33
+ result = hex_to_short val
34
+ hash = result.nil?
35
+ color_val val, result, hash
36
+ end
37
+
38
+ def min_color value
39
+ val = value.downcase
40
+ result = long_to_hex val
41
+ hash = !result.nil?
42
+ color_val val, result, hash
43
+ end
44
+
45
+ def min_rgb value
46
+ rgb = [value.red, value.green, value.blue]
47
+ rgb.map! do |color|
48
+ color = color.to_s
49
+ if color =~ /\%$/ then
50
+ color = (color[0..-2].to_f / 100 * 256).round
51
+ else
52
+ color = color.to_i
53
+ end
54
+ if color > 255 then
55
+ raise ArgumentError.new "Color value should be [0..255] or [0%..100%]"
56
+ end
57
+ color
58
+ end
59
+ rgb = '%02x%02x%02x' % rgb
60
+ result = min_hex(rgb)
61
+ if result.nil? then
62
+ result = rgb
63
+ end
64
+ result
65
+ end
66
+
67
+ end
68
+ end
69
+ end
@@ -0,0 +1,20 @@
1
+ require 'csspool'
2
+
3
+ module CssPress
4
+ class Css
5
+
6
+ DEFAULTS = {
7
+ }
8
+
9
+ def initialize (options = {})
10
+ @options = DEFAULTS.merge(options)
11
+ end
12
+
13
+ def press (css)
14
+ css_in = css.respond_to?(:read) ? css.read : css.dup
15
+ doc = CSSPool.CSS css_in
16
+ doc.min_css
17
+ end
18
+
19
+ end
20
+ end
@@ -0,0 +1,280 @@
1
+ module CSSPool
2
+ module Visitors
3
+ class MinCSS < Visitor
4
+
5
+ CSS_IDENTIFIER_ILLEGAL_CHARACTERS =
6
+ (0..255).to_a.pack('U*').gsub(/[a-zA-Z0-9_-]/, '')
7
+ CSS_STRING_ESCAPE_MAP = {
8
+ "\\" => "\\\\",
9
+ "\"" => "\\\"",
10
+ "\n" => "\\a ", # CSS2 4.1.3 p3.2
11
+ "\r" => "\\\r",
12
+ "\f" => "\\\f"
13
+ }
14
+
15
+ def initialize
16
+ end
17
+
18
+ visitor_for CSS::Document do |target|
19
+ # Default media list is []
20
+ current_media_type = []
21
+
22
+ tokens = []
23
+
24
+ target.charsets.each do |char_set|
25
+ tokens << char_set.accept(self)
26
+ end
27
+
28
+ target.import_rules.each do |ir|
29
+ tokens << ir.accept(self)
30
+ end
31
+
32
+ target.rule_sets.each { |rs|
33
+ rule_sets = rs.accept(self)
34
+ if rule_sets != "" then
35
+ if rs.media != current_media_type
36
+ media = " " + rs.media.map do |medium|
37
+ medium.name.map{ |i| i.accept(self) }.join(' ')
38
+ end.join(',')
39
+ tokens << "@media#{media}{"
40
+ end
41
+
42
+ tokens << rule_sets
43
+
44
+ if rs.media != current_media_type
45
+ current_media_type = rs.media
46
+ tokens << "}"
47
+ end
48
+ end
49
+ }
50
+ tokens.join
51
+ end
52
+
53
+ visitor_for CSS::Charset do |target|
54
+ "@charset \"#{escape_css_string target.name}\";"
55
+ end
56
+
57
+ visitor_for CSS::ImportRule do |target|
58
+ media = ''
59
+ media = " " + target.media.map do |medium|
60
+ medium.name.map{ |i| i.accept(self) }.join(' ')
61
+ end.join(', ') if target.media.length > 0
62
+
63
+ "@import #{target.uri.accept(self)}#{media};"
64
+ end
65
+
66
+ visitor_for CSS::RuleSet do |target|
67
+
68
+ is_q = false
69
+ selectors = target.selectors.map do |sel|
70
+ selector = sel.accept self
71
+ if selector =~ /^q:(after|before)/ then
72
+ is_q = true
73
+ end
74
+ selector
75
+ end.join(",")
76
+
77
+ temp = {}
78
+ i = 0
79
+ target.declarations.each do |decl|
80
+ if temp.has_key?(decl.property) && !(decl.property == 'content' && is_q) then
81
+ target.declarations[temp[decl.property]] = nil
82
+ end
83
+ temp[decl.property] = i
84
+ i+=1
85
+ end
86
+ temp = nil
87
+ target.declarations.compact!
88
+
89
+ declarations = target.declarations.map { |decl| decl.nil? ? '' : decl.accept(self) }.join(";")
90
+ if declarations == "" then
91
+ ""
92
+ else
93
+ selectors + "{" + declarations + "}"
94
+ end
95
+ end
96
+
97
+ visitor_for CSS::Declaration do |target|
98
+ important = target.important? ? ' !important' : ''
99
+
100
+ "#{escape_css_identifier target.property}:" + target.expressions.map { |exp|
101
+
102
+ special_value = nil
103
+ property = target.property.downcase
104
+
105
+ if /(color|background|background-color)/ =~ property then
106
+ special_value = Color::min_color exp.to_s
107
+ end
108
+
109
+ if /border(\-(top|bottom|left|right))?/ =~ property then
110
+ if exp.to_s == 'none' then
111
+ special_value = '0'
112
+ else
113
+ special_value = Color::min_color exp.to_s
114
+ end
115
+ end
116
+
117
+ if special_value.nil? then
118
+ [exp.operator, exp.accept(self)].join
119
+ else
120
+ special_value
121
+ end
122
+ }.join(' ').strip + "#{important}"
123
+
124
+ end
125
+
126
+ visitor_for Terms::Ident do |target|
127
+ escape_css_identifier target.value
128
+ end
129
+
130
+ visitor_for Terms::Hash do |target|
131
+ value = Color::min_hex target.value
132
+ if value.nil? then
133
+ value = "##{target.value}"
134
+ end
135
+ value
136
+ end
137
+
138
+ visitor_for Selectors::MediaExpression do |target|
139
+ '(' + escape_css_string(target.name) + ':' +
140
+ target.value.map { |x| x.accept self }.join + ')'
141
+ end
142
+
143
+ visitor_for Terms::URI do |target|
144
+ "url(\"#{escape_css_string target.value}\")"
145
+ end
146
+
147
+ visitor_for Terms::Function do |target|
148
+ "#{escape_css_identifier target.name}(" +
149
+ target.params.map { |x|
150
+ [
151
+ x.operator,
152
+ x.accept(self)
153
+ ].compact.join(' ')
154
+ }.join + ')'
155
+ end
156
+
157
+ visitor_for Terms::Rgb do |target|
158
+ begin
159
+ value = Color::min_rgb target
160
+ rescue ArgumentError
161
+ params = [
162
+ target.red,
163
+ target.green,
164
+ target.blue
165
+ ].map { |c|
166
+ c.accept(self)
167
+ }.join ','
168
+
169
+ value = "rgb(#{params})"
170
+ end
171
+ value
172
+ end
173
+
174
+ visitor_for Terms::String do |target|
175
+ "\"#{escape_css_string target.value}\""
176
+ end
177
+
178
+ visitor_for Terms::Number do |target|
179
+ value = target.value
180
+ if value == 0 then
181
+ value = '0'
182
+ else
183
+ trunc = value.truncate.round
184
+ fract = value - trunc
185
+ value = [
186
+ trunc == 0 ? '' : trunc.to_s,
187
+ fract == 0 ? '' : fract.to_s.sub(/^0/, '')
188
+ ].join
189
+ end
190
+
191
+ [
192
+ target.unary_operator == :minus && target.value != 0 ? '-' : nil,
193
+ value,
194
+ target.value == 0 ? '' : target.type
195
+ ].compact.join
196
+ end
197
+
198
+ visitor_for Selector do |target|
199
+ target.simple_selectors.map { |ss| ss.accept self }.join
200
+ end
201
+
202
+ visitor_for Selectors::Simple, Selectors::Universal, Selectors::Type do |target|
203
+ combo = {
204
+ :s => ' ',
205
+ :+ => '+',
206
+ :> => '>'
207
+ }[target.combinator]
208
+
209
+ name = [nil, '*'].include?(target.name) ? target.name : escape_css_identifier(target.name)
210
+ [combo, name].compact.join +
211
+ target.additional_selectors.map { |as| as.accept self }.join
212
+ end
213
+
214
+ visitor_for Selectors::Id do |target|
215
+ "##{escape_css_identifier target.name}"
216
+ end
217
+
218
+ visitor_for Selectors::Class do |target|
219
+ ".#{escape_css_identifier target.name}"
220
+ end
221
+
222
+ visitor_for Selectors::PseudoClass do |target|
223
+ if target.extra.nil?
224
+ ":#{escape_css_identifier target.name}"
225
+ else
226
+ ":#{escape_css_identifier target.name}(#{escape_css_identifier target.extra})"
227
+ end
228
+ end
229
+
230
+ visitor_for Selectors::PseudoElement do |target|
231
+ if target.css2.nil?
232
+ "::#{escape_css_identifier target.name}"
233
+ else
234
+ ":#{escape_css_identifier target.name}"
235
+ end
236
+ end
237
+
238
+ visitor_for Selectors::Attribute do |target|
239
+ case target.match_way
240
+ when Selectors::Attribute::SET
241
+ "[#{escape_css_identifier target.name}]"
242
+ when Selectors::Attribute::EQUALS
243
+ "[#{escape_css_identifier target.name}=#{escape_attribute target.value}]"
244
+ when Selectors::Attribute::INCLUDES
245
+ "[#{escape_css_identifier target.name}~=#{escape_attribute target.value}]"
246
+ when Selectors::Attribute::DASHMATCH
247
+ "[#{escape_css_identifier target.name}|=#{escape_attribute target.value}]"
248
+ else
249
+ raise "no matching matchway"
250
+ end
251
+ end
252
+
253
+ private
254
+
255
+ def escape_css_identifier text
256
+ # CSS2 4.1.3 p2
257
+ unsafe_chars = /[#{Regexp.escape CSS_IDENTIFIER_ILLEGAL_CHARACTERS}]/
258
+ text.gsub(/^\d|^\-(?=\-|\d)|#{unsafe_chars}/um) do |char|
259
+ if ':()-\\ ='.include? char
260
+ "\\#{char}"
261
+ else # I don't trust others to handle space termination well.
262
+ "\\#{char.unpack('U').first.to_s(16).rjust(6, '0')}"
263
+ end
264
+ end
265
+ end
266
+
267
+ def escape_css_string text
268
+ text.gsub(/[\\"\n\r\f]/) {CSS_STRING_ESCAPE_MAP[$&]}
269
+ end
270
+
271
+ def escape_attribute text
272
+ if text.size == 0 || text =~ /[ \t\r\n\f"'`=<>]/ then
273
+ '"' + escape_css_string(text) + '"'
274
+ else
275
+ escape_css_string text
276
+ end
277
+ end
278
+ end
279
+ end
280
+ end
@@ -0,0 +1,7 @@
1
+ module CSSPool
2
+ class Node
3
+ def min_css
4
+ accept Visitors::MinCSS.new
5
+ end
6
+ end
7
+ end
@@ -0,0 +1,3 @@
1
+ module CssPress
2
+ VERSION = "0.3.0"
3
+ end
@@ -0,0 +1,206 @@
1
+ # encoding: UTF-8
2
+
3
+ require_relative "../lib/css_press"
4
+
5
+ describe CssPress do
6
+ before :each do
7
+ end
8
+
9
+ it "should raise error on malformed css" do
10
+ expect { CssPress.press('a { b: c') }.to raise_error(Racc::ParseError)
11
+ expect { CssPress.press('a{;}') }.to raise_error(Racc::ParseError)
12
+ end
13
+
14
+ it "should remove unnecessary spaces" do
15
+ CssPress.press('a { b: c ; }').should eql 'a{b:c}'
16
+ # CssPress.press('a { b : c ; }').should eql 'a{b:c}'
17
+ end
18
+
19
+ # it "should remove unnecessary quotes from urls" do
20
+ # CssPress.press('a{b:url( "i.gif" )}').should eql 'a{b:url(i.gif)}'
21
+ # CssPress.press('a{b:url( " i.gif" )}').should eql 'a{b:url(" i.gif")}'
22
+ # end
23
+
24
+ it "should remove unnecessary quotes from attributes" do
25
+ CssPress.press('a[d="e"]{b:c}').should eql 'a[d=e]{b:c}'
26
+ CssPress.press('a[d="e f"]{b:c}').should eql 'a[d="e f"]{b:c}'
27
+ end
28
+
29
+ # it "should minify class attributes" do
30
+ # CssPress.press('a[class="x y z"]{b:c}').should eql 'a.x.y.z{b:c}'
31
+ # CssPress.press('a[class="x.y z"]{b:c}').should eql 'a[class="x.y z"]{b:c}'
32
+ # end
33
+
34
+ # it "should minify id attributes" do
35
+ # CssPress.press('a[id="x"]{b:c}').should eql 'a#x{b:c}'
36
+ # CssPress.press('a[id="x.y z"]{b:c}').should eql 'a[id="x.y z"]{b:c}'
37
+ # end
38
+
39
+ it "should minify color" do
40
+ CssPress.press('a{color:#aaaaaa}').should eql 'a{color:#aaa}'
41
+ CssPress.press('a{color:#AaaAaa}').should eql 'a{color:#aaa}'
42
+ CssPress.press('a{color:#ff0000}').should eql 'a{color:red}'
43
+ CssPress.press('a{color:#f00}').should eql 'a{color:red}'
44
+ CssPress.press('#f00{color:#f00}').should eql '#f00{color:red}'
45
+ CssPress.press('a{color:black}').should eql 'a{color:#000}'
46
+ CssPress.press('a{background:black}').should eql 'a{background:#000}'
47
+ CssPress.press('a{border:black}').should eql 'a{border:#000}'
48
+ CssPress.press('a{background-color:black}').should eql 'a{background-color:#000}'
49
+ CssPress.press('a{border-top:black}').should eql 'a{border-top:#000}'
50
+ CssPress.press('a{b:black}').should eql 'a{b:black}'
51
+ CssPress.press('a{color:rgb(0,0,0)}').should eql 'a{color:#000}'
52
+ CssPress.press('a{color:rgb(0%,0%,0%)}').should eql 'a{color:#000}'
53
+ CssPress.press('a{color:rgb(300,0,0)}').should eql 'a{color:rgb(300,0,0)}'
54
+ CssPress.press('a{color:rgb(101%,0%,0%)}').should eql 'a{color:rgb(101%,0,0)}'
55
+ end
56
+
57
+ it "should remove comments" do
58
+ CssPress.press('/* cooment*/a{b:c}').should eql 'a{b:c}'
59
+ CssPress.press('a/* cooment*/{b:c}').should eql 'a{b:c}'
60
+ CssPress.press('a{/* cooment*/b:c}').should eql 'a{b:c}'
61
+ CssPress.press('a{b:/* cooment */c}').should eql 'a{b:c}'
62
+ CssPress.press('a{b:c/* cooment */}').should eql 'a{b:c}'
63
+ CssPress.press('a{b:c/* cooment \*/}').should eql 'a{b:c}'
64
+ CssPress.press('a{b:"\"}\""/* cooment */}').should eql 'a{b:"\"}\""}'
65
+ end
66
+
67
+ it "should remove units from 0" do
68
+ CssPress.press('a{padding:0px}').should eql 'a{padding:0}'
69
+ end
70
+
71
+ it "should remove leading/trailing zeros from decimals" do
72
+ CssPress.press('a{padding:0.1%}').should eql 'a{padding:.1%}'
73
+ CssPress.press('a{padding:1.0%}').should eql 'a{padding:1%}'
74
+ CssPress.press('a{margin:-1.0%}').should eql 'a{margin:-1%}'
75
+ CssPress.press('a{margin:-0.1%}').should eql 'a{margin:-.1%}'
76
+ CssPress.press('a{margin:-0.0%}').should eql 'a{margin:0}'
77
+ end
78
+
79
+ it "should change none to 0" do
80
+ CssPress.press('a{border:none}').should eql 'a{border:0}'
81
+ CssPress.press('a{border-bottom:none}').should eql 'a{border-bottom:0}'
82
+ end
83
+
84
+ it "should remove duplicate rules" do
85
+ CssPress.press('a{x:y;x:z}').should eql 'a{x:z}'
86
+ CssPress.press('a{x:y1;b:b;x:z}').should eql 'a{b:b;x:z}'
87
+ CssPress.press('a{c:c;x:y;b:b;x:z}').should eql 'a{c:c;b:b;x:z}'
88
+ CssPress.press('a{c:c;x:y;b:b;x:z;d:d;x:x}').should eql 'a{c:c;b:b;d:d;x:x}'
89
+ CssPress.press('a{c:c;x:y;b:b;x:z;c:c;x:x}').should eql 'a{b:b;c:c;x:x}'
90
+ # Safari does not support the "quotes" property.
91
+ # CSS 2; used to remove quotes in case "none" fails below.
92
+ # CSS 2.1; will remove quotes if supported, and override the above.
93
+ # User-agents that don't understand "none" should ignore it, and keep the above value.
94
+ CssPress.press('q:before,q:after{content:"";content:none}').should eql 'q:before,q:after{content:"";content:none}'
95
+ end
96
+
97
+ # it "should remove unnecessary values from padding/margin" do
98
+ # CssPress.press('a{padding:0 0 0 0}').should eql 'a{padding:0}'
99
+ # CssPress.press('a{padding:0 0 10px 0}').should eql 'a{padding:0 0 10px}'
100
+ # CssPress.press('a{padding:0 auto 0 auto}').should eql 'a{padding:0 auto}'
101
+ # CssPress.press('a{background-position:0 0}').should eql 'a{background-position:0 0}'
102
+ # end
103
+
104
+ it "should remove empty rules" do
105
+ CssPress.press('a{}').should eql ''
106
+ CssPress.press('a{/*b:c*/}').should eql ''
107
+ CssPress.press('@media print{a{}}').should eql ''
108
+ CssPress.press('@media print{a{}b{c:d}}').should eql '@media print{b{c:d}}'
109
+ end
110
+
111
+ # it "should combine all background related properties" do
112
+ # css_in = 'a{
113
+ # background-color: #fff;
114
+ # background-image: url(image.gif);
115
+ # background-repeat: repeat-x;
116
+ # background-attachment: fixed;
117
+ # background-position: 0 0}'
118
+ # css_out = 'a{background:#fff url(image.gif) repeat-x fixed 0 0}'
119
+ # CssPress.press(css_in).should eql css_out
120
+ # end
121
+
122
+ # it "should combine all border related properties" do
123
+ # css_in = 'a{
124
+ # border-left-color: #000;
125
+ # border-left-style: solid;
126
+ # border-left-width: 2px;
127
+ # border-right-color: #000;
128
+ # border-right-style: solid;
129
+ # border-right-width: 2px;
130
+ # border-top-color: #000;
131
+ # border-top-style: solid;
132
+ # border-top-width: 3px}'
133
+ # css_out = 'a{border:solid #000;border-width:3px 2px 0}'
134
+ # CssPress.press(css_in).should eql css_out
135
+ # end
136
+
137
+ # it "should combine all border-radius related properties" do
138
+ # -moz- -webkit- -o-
139
+ # end
140
+
141
+ # it "should combine all font related properties" do
142
+ # css_in = 'a{
143
+ # font-style: italic;
144
+ # font-variant: small-caps;
145
+ # font-weight: 500;
146
+ # font-size: 1em;
147
+ # line-height: 24px;
148
+ # font-family: arial,sans-serif}'
149
+ # css_out = 'a{font:italic small-caps 500 1em/24px arial,sans-serif}'
150
+ # CssPress.press(css_in).should eql css_out
151
+ # end
152
+
153
+ # it "should minimize font-family" do
154
+ # end
155
+
156
+ # it "should combine all list related properties" do
157
+ # css_in = 'a{
158
+ # list-style-type: circle;
159
+ # list-style-position: inside;
160
+ # list-style-image: url(bullet.gif)}'
161
+ # css_out = 'a{list-style:inside circle url(bullet.gif)}'
162
+ # CssPress.press(css_in).should eql css_out
163
+ # end
164
+
165
+ # it "should combine all outline related properties" do
166
+ # css_in = 'a{
167
+ # outline-color: #fff;
168
+ # outline-style: dotted;
169
+ # outline-width: 1px}'
170
+ # css_out = 'a{outline:#fff dotted 1px}'
171
+ # CssPress.press(css_in).should eql css_out
172
+ # end
173
+
174
+ # it "should combine all margin/padding related properties" do
175
+ # css_in = 'a{margin-top:1px;margin-bottom:2px;margin-right:3px;margin-left:0px}'
176
+ # css_out = 'a{margin:1px 3px 2px 0}'
177
+ # CssPress.press(css_in).should eql css_out
178
+
179
+ # css_in = 'a{margin-top:1px;margin-bottom:1px;margin-right:1px;margin-left:1px}'
180
+ # css_out = 'a{margin:1px}'
181
+ # CssPress.press(css_in).should eql css_out
182
+
183
+ # css_in = 'a{padding-top:1px;padding-bottom:2px;padding-right:3px;padding-left:0px}'
184
+ # css_out = 'a{padding:1px 3px 2px 0}'
185
+ # CssPress.press(css_in).should eql css_out
186
+ # end
187
+
188
+ # it "should combine rules with the same selectors" do
189
+ # css_in = 'a{color:red}a{text-decoration:none}'
190
+ # css_out = 'a{color:red;text-decoration:none}'
191
+ # CssPress.press(css_in).should eql css_out
192
+ # end
193
+
194
+ # it "should combine rules with the same rulesets" do
195
+ # css_in = 'a{color:red}b{color:red}'
196
+ # css_out = 'a,b{color:red}'
197
+ # CssPress.press(css_in).should eql css_out
198
+ # end
199
+
200
+ # it "should inline import" do
201
+ # end
202
+
203
+ # it "should inline small images" do
204
+ # end
205
+
206
+ end
metadata ADDED
@@ -0,0 +1,99 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: css_press
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.3.0
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - stereobooster
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2012-04-28 00:00:00.000000000Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: csspool-st
16
+ requirement: &29000628 !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
19
+ - - =
20
+ - !ruby/object:Gem::Version
21
+ version: 3.1.2
22
+ type: :runtime
23
+ prerelease: false
24
+ version_requirements: *29000628
25
+ - !ruby/object:Gem::Dependency
26
+ name: rake
27
+ requirement: &29000352 !ruby/object:Gem::Requirement
28
+ none: false
29
+ requirements:
30
+ - - ! '>='
31
+ - !ruby/object:Gem::Version
32
+ version: '0'
33
+ type: :development
34
+ prerelease: false
35
+ version_requirements: *29000352
36
+ - !ruby/object:Gem::Dependency
37
+ name: rspec
38
+ requirement: &29000076 !ruby/object:Gem::Requirement
39
+ none: false
40
+ requirements:
41
+ - - ! '>='
42
+ - !ruby/object:Gem::Version
43
+ version: '0'
44
+ type: :development
45
+ prerelease: false
46
+ version_requirements: *29000076
47
+ description: Ruby gem for compressing CSS
48
+ email:
49
+ - stereobooster@gmail.com
50
+ executables: []
51
+ extensions: []
52
+ extra_rdoc_files: []
53
+ files:
54
+ - .gitignore
55
+ - Gemfile
56
+ - Rakefile
57
+ - Readme.md
58
+ - css_press.gemspec
59
+ - lib/colors/hex_to_safe.json
60
+ - lib/colors/hex_to_short.json
61
+ - lib/colors/long_to_hex.json
62
+ - lib/css_press.rb
63
+ - lib/css_press/color.rb
64
+ - lib/css_press/css.rb
65
+ - lib/css_press/min_css.rb
66
+ - lib/css_press/node.rb
67
+ - lib/css_press/version.rb
68
+ - spec/css_press_spec.rb
69
+ homepage: ''
70
+ licenses: []
71
+ post_install_message:
72
+ rdoc_options: []
73
+ require_paths:
74
+ - lib
75
+ required_ruby_version: !ruby/object:Gem::Requirement
76
+ none: false
77
+ requirements:
78
+ - - ! '>='
79
+ - !ruby/object:Gem::Version
80
+ version: '0'
81
+ segments:
82
+ - 0
83
+ hash: 419184687
84
+ required_rubygems_version: !ruby/object:Gem::Requirement
85
+ none: false
86
+ requirements:
87
+ - - ! '>='
88
+ - !ruby/object:Gem::Version
89
+ version: '0'
90
+ segments:
91
+ - 0
92
+ hash: 419184687
93
+ requirements: []
94
+ rubyforge_project: css_press
95
+ rubygems_version: 1.8.15
96
+ signing_key:
97
+ specification_version: 3
98
+ summary: Compress CSS
99
+ test_files: []