ruby-clean-css 0.0.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (124) hide show
  1. data/.gitmodules +3 -0
  2. data/EXAMPLE.md +25 -0
  3. data/Gemfile +4 -0
  4. data/Gemfile.lock +30 -0
  5. data/LICENSE +19 -0
  6. data/README.md +106 -0
  7. data/lib/javascript/clean-css/.gitignore +4 -0
  8. data/lib/javascript/clean-css/.jshintignore +3 -0
  9. data/lib/javascript/clean-css/.jshintrc +14 -0
  10. data/lib/javascript/clean-css/.travis.yml +11 -0
  11. data/lib/javascript/clean-css/History.md +556 -0
  12. data/lib/javascript/clean-css/LICENSE +19 -0
  13. data/lib/javascript/clean-css/README.md +218 -0
  14. data/lib/javascript/clean-css/bin/cleancss +157 -0
  15. data/lib/javascript/clean-css/index.js +1 -0
  16. data/lib/javascript/clean-css/lib/clean.js +426 -0
  17. data/lib/javascript/clean-css/lib/colors/hsl-to-hex.js +64 -0
  18. data/lib/javascript/clean-css/lib/colors/long-to-short-hex.js +12 -0
  19. data/lib/javascript/clean-css/lib/colors/rgb-to-hex.js +14 -0
  20. data/lib/javascript/clean-css/lib/colors/shortener.js +174 -0
  21. data/lib/javascript/clean-css/lib/images/url-rebase.js +32 -0
  22. data/lib/javascript/clean-css/lib/images/url-rewriter.js +60 -0
  23. data/lib/javascript/clean-css/lib/imports/inliner.js +319 -0
  24. data/lib/javascript/clean-css/lib/properties/optimizer.js +276 -0
  25. data/lib/javascript/clean-css/lib/properties/override-compactor.js +116 -0
  26. data/lib/javascript/clean-css/lib/properties/processable.js +859 -0
  27. data/lib/javascript/clean-css/lib/properties/scanner.js +20 -0
  28. data/lib/javascript/clean-css/lib/properties/shorthand-compactor.js +244 -0
  29. data/lib/javascript/clean-css/lib/properties/token.js +184 -0
  30. data/lib/javascript/clean-css/lib/properties/validator.js +140 -0
  31. data/lib/javascript/clean-css/lib/selectors/empty-removal.js +30 -0
  32. data/lib/javascript/clean-css/lib/selectors/optimizer.js +341 -0
  33. data/lib/javascript/clean-css/lib/selectors/tokenizer.js +168 -0
  34. data/lib/javascript/clean-css/lib/text/comments.js +83 -0
  35. data/lib/javascript/clean-css/lib/text/escape-store.js +32 -0
  36. data/lib/javascript/clean-css/lib/text/expressions.js +73 -0
  37. data/lib/javascript/clean-css/lib/text/free.js +26 -0
  38. data/lib/javascript/clean-css/lib/text/name-quotes.js +37 -0
  39. data/lib/javascript/clean-css/lib/text/quote-scanner.js +91 -0
  40. data/lib/javascript/clean-css/lib/text/splitter.js +35 -0
  41. data/lib/javascript/clean-css/lib/text/urls.js +41 -0
  42. data/lib/javascript/clean-css/package.json +50 -0
  43. data/lib/javascript/clean-css/test/batch-test.js +56 -0
  44. data/lib/javascript/clean-css/test/bench.js +11 -0
  45. data/lib/javascript/clean-css/test/binary-test.js +323 -0
  46. data/lib/javascript/clean-css/test/data-bench/_partial.css +1 -0
  47. data/lib/javascript/clean-css/test/data-bench/complex.css +4 -0
  48. data/lib/javascript/clean-css/test/data/129-assets/assets/ui.css +2 -0
  49. data/lib/javascript/clean-css/test/data/129-assets/components/bootstrap/css/bootstrap.css +3 -0
  50. data/lib/javascript/clean-css/test/data/129-assets/components/bootstrap/images/glyphs.gif +0 -0
  51. data/lib/javascript/clean-css/test/data/129-assets/components/jquery-ui/css/style.css +6 -0
  52. data/lib/javascript/clean-css/test/data/129-assets/components/jquery-ui/images/next.gif +0 -0
  53. data/lib/javascript/clean-css/test/data/129-assets/components/jquery-ui/images/prev.gif +0 -0
  54. data/lib/javascript/clean-css/test/data/960-min.css +125 -0
  55. data/lib/javascript/clean-css/test/data/960.css +602 -0
  56. data/lib/javascript/clean-css/test/data/big-min.css +2984 -0
  57. data/lib/javascript/clean-css/test/data/big.css +13794 -0
  58. data/lib/javascript/clean-css/test/data/blueprint-min.css +245 -0
  59. data/lib/javascript/clean-css/test/data/blueprint.css +556 -0
  60. data/lib/javascript/clean-css/test/data/charset-mixed-with-fonts-min.css +3 -0
  61. data/lib/javascript/clean-css/test/data/charset-mixed-with-fonts.css +14 -0
  62. data/lib/javascript/clean-css/test/data/font-awesome-ie7-min.css +392 -0
  63. data/lib/javascript/clean-css/test/data/font-awesome-ie7.css +1203 -0
  64. data/lib/javascript/clean-css/test/data/font-awesome-min.css +319 -0
  65. data/lib/javascript/clean-css/test/data/font-awesome.css +540 -0
  66. data/lib/javascript/clean-css/test/data/imports-min.css +5 -0
  67. data/lib/javascript/clean-css/test/data/imports.css +4 -0
  68. data/lib/javascript/clean-css/test/data/issue-117-snippet-min.css +3 -0
  69. data/lib/javascript/clean-css/test/data/issue-117-snippet.css +19 -0
  70. data/lib/javascript/clean-css/test/data/issue-159-snippet-min.css +7 -0
  71. data/lib/javascript/clean-css/test/data/issue-159-snippet.css +7 -0
  72. data/lib/javascript/clean-css/test/data/issue-192-min.css +1 -0
  73. data/lib/javascript/clean-css/test/data/issue-192.css +8 -0
  74. data/lib/javascript/clean-css/test/data/issue-198-min.css +3 -0
  75. data/lib/javascript/clean-css/test/data/issue-198.css +4 -0
  76. data/lib/javascript/clean-css/test/data/issue-232-min.css +2 -0
  77. data/lib/javascript/clean-css/test/data/issue-232.css +17 -0
  78. data/lib/javascript/clean-css/test/data/issue-241-min.css +2 -0
  79. data/lib/javascript/clean-css/test/data/issue-241.css +2 -0
  80. data/lib/javascript/clean-css/test/data/issue-304-min.css +1 -0
  81. data/lib/javascript/clean-css/test/data/issue-304.css +4 -0
  82. data/lib/javascript/clean-css/test/data/issue-305-min.css +1 -0
  83. data/lib/javascript/clean-css/test/data/issue-305.css +3 -0
  84. data/lib/javascript/clean-css/test/data/issue-308-min.css +1 -0
  85. data/lib/javascript/clean-css/test/data/issue-308.css +5 -0
  86. data/lib/javascript/clean-css/test/data/issue-312-min.css +1 -0
  87. data/lib/javascript/clean-css/test/data/issue-312.css +7 -0
  88. data/lib/javascript/clean-css/test/data/issue-337-min.css +1 -0
  89. data/lib/javascript/clean-css/test/data/issue-337.css +4 -0
  90. data/lib/javascript/clean-css/test/data/line-breaks-in-attributes-min.css +2 -0
  91. data/lib/javascript/clean-css/test/data/line-breaks-in-attributes.css +8 -0
  92. data/lib/javascript/clean-css/test/data/partials-absolute/base.css +3 -0
  93. data/lib/javascript/clean-css/test/data/partials-absolute/base2.css +1 -0
  94. data/lib/javascript/clean-css/test/data/partials-absolute/extra/sub.css +3 -0
  95. data/lib/javascript/clean-css/test/data/partials-relative/base.css +3 -0
  96. data/lib/javascript/clean-css/test/data/partials-relative/extra/included.css +1 -0
  97. data/lib/javascript/clean-css/test/data/partials/comment.css +2 -0
  98. data/lib/javascript/clean-css/test/data/partials/extra/down.gif +0 -0
  99. data/lib/javascript/clean-css/test/data/partials/extra/four.css +3 -0
  100. data/lib/javascript/clean-css/test/data/partials/extra/three.css +1 -0
  101. data/lib/javascript/clean-css/test/data/partials/five.css +1 -0
  102. data/lib/javascript/clean-css/test/data/partials/four.css +1 -0
  103. data/lib/javascript/clean-css/test/data/partials/one.css +1 -0
  104. data/lib/javascript/clean-css/test/data/partials/three.css +1 -0
  105. data/lib/javascript/clean-css/test/data/partials/two.css +5 -0
  106. data/lib/javascript/clean-css/test/data/reset-min.css +12 -0
  107. data/lib/javascript/clean-css/test/data/reset.css +64 -0
  108. data/lib/javascript/clean-css/test/data/sample1-min.css +4 -0
  109. data/lib/javascript/clean-css/test/data/sample1.css +12 -0
  110. data/lib/javascript/clean-css/test/data/unsupported/selectors-ie7.css +20 -0
  111. data/lib/javascript/clean-css/test/data/unsupported/selectors-ie8.css +17 -0
  112. data/lib/javascript/clean-css/test/module-test.js +185 -0
  113. data/lib/javascript/clean-css/test/protocol-imports-test.js +409 -0
  114. data/lib/javascript/clean-css/test/text/splitter-test.js +20 -0
  115. data/lib/javascript/clean-css/test/unit-test.js +2071 -0
  116. data/lib/ruby-clean-css.rb +6 -0
  117. data/lib/ruby-clean-css/compressor.rb +142 -0
  118. data/lib/ruby-clean-css/exports.rb +258 -0
  119. data/lib/ruby-clean-css/railtie.rb +27 -0
  120. data/lib/ruby-clean-css/sprockets.rb +19 -0
  121. data/lib/ruby-clean-css/version.rb +5 -0
  122. data/ruby-clean-css.gemspec +30 -0
  123. data/test/test_compressor.rb +84 -0
  124. metadata +203 -0
@@ -0,0 +1,409 @@
1
+ /* jshint unused: false */
2
+
3
+ var vows = require('vows');
4
+ var assert = require('assert');
5
+ var http = require('http');
6
+ var nock = require('nock');
7
+ var CleanCSS = require('../index');
8
+
9
+ var port = 24682;
10
+
11
+ if (process.platform == 'win32')
12
+ return;
13
+
14
+ vows.describe('protocol imports').addBatch({
15
+ 'of a missing file': {
16
+ topic: function() {
17
+ this.reqMocks = nock('http://127.0.0.1')
18
+ .get('/missing.css')
19
+ .reply(404);
20
+
21
+ new CleanCSS().minify('@import url(http://127.0.0.1/missing.css);a{color:red}', this.callback);
22
+ },
23
+ 'should raise error': function(errors, minified) {
24
+ assert.equal(errors.length, 1);
25
+ },
26
+ 'should ignore @import': function(errors, minified) {
27
+ assert.equal(minified, '@import url(http://127.0.0.1/missing.css);a{color:red}');
28
+ },
29
+ teardown: function() {
30
+ assert.equal(this.reqMocks.isDone(), true);
31
+ nock.restore();
32
+ }
33
+ },
34
+ 'of an existing file': {
35
+ topic: function() {
36
+ this.reqMocks = nock('http://127.0.0.1')
37
+ .get('/present.css')
38
+ .reply(200, 'p{font-size:13px}');
39
+
40
+ new CleanCSS().minify('@import url(http://127.0.0.1/present.css);a{color:red}', this.callback);
41
+ },
42
+ 'should not raise errors': function(errors, minified) {
43
+ assert.isNull(errors);
44
+ },
45
+ 'should process @import': function(errors, minified) {
46
+ assert.equal(minified, 'p{font-size:13px}a{color:red}');
47
+ },
48
+ teardown: function() {
49
+ assert.equal(this.reqMocks.isDone(), true);
50
+ nock.restore();
51
+ }
52
+ },
53
+ 'of an existing file with spaces in path': {
54
+ topic: function() {
55
+ this.reqMocks = nock('http://fonts.googleapis.com')
56
+ .get('/css?family=Oleo%20Script%20Swash%20Caps')
57
+ .reply(200, 'p{font-size:13px}');
58
+
59
+ new CleanCSS().minify('@import url(\'//fonts.googleapis.com/css?family=Oleo Script Swash Caps\');', this.callback);
60
+ },
61
+ 'should not raise errors': function(errors, minified) {
62
+ assert.isNull(errors);
63
+ },
64
+ 'should process @import': function(errors, minified) {
65
+ assert.equal(minified, 'p{font-size:13px}');
66
+ },
67
+ teardown: function() {
68
+ assert.equal(this.reqMocks.isDone(), true);
69
+ nock.restore();
70
+ }
71
+ },
72
+ 'of an existing file via HTTPS': {
73
+ topic: function() {
74
+ this.reqMocks = nock('https://127.0.0.1')
75
+ .get('/present.css')
76
+ .reply(200, 'p{font-size:13px}');
77
+
78
+ new CleanCSS().minify('@import url(https://127.0.0.1/present.css);a{color:red}', this.callback);
79
+ },
80
+ 'should not raise errors': function(errors, minified) {
81
+ assert.isNull(errors);
82
+ },
83
+ 'should process @import': function(errors, minified) {
84
+ assert.equal(minified, 'p{font-size:13px}a{color:red}');
85
+ },
86
+ teardown: function() {
87
+ assert.equal(this.reqMocks.isDone(), true);
88
+ nock.restore();
89
+ }
90
+ },
91
+ 'of an existing file with media': {
92
+ topic: function() {
93
+ this.reqMocks = nock('http://127.0.0.1')
94
+ .get('/present.css')
95
+ .reply(200, 'p{font-size:13px}');
96
+
97
+ new CleanCSS().minify('@import url(http://127.0.0.1/present.css) screen;a{color:red}', this.callback);
98
+ },
99
+ 'should not raise errors': function(errors, minified) {
100
+ assert.isNull(errors);
101
+ },
102
+ 'should process @import': function(errors, minified) {
103
+ assert.equal(minified, '@media screen{p{font-size:13px}}a{color:red}');
104
+ },
105
+ teardown: function() {
106
+ assert.equal(this.reqMocks.isDone(), true);
107
+ nock.restore();
108
+ }
109
+ },
110
+ 'of an existing file with dependencies': {
111
+ topic: function() {
112
+ this.reqMocks1 = nock('http://127.0.0.1')
113
+ .get('/present.css')
114
+ .reply(200, '@import url(/vendor/reset.css);@import url(https://assets.127.0.0.1/base.css);p{font-size:13px}')
115
+ .get('/vendor/reset.css')
116
+ .reply(200, 'body{margin:0}');
117
+ this.reqMocks2 = nock('https://assets.127.0.0.1')
118
+ .get('/base.css')
119
+ .reply(200, 'div{padding:0}');
120
+
121
+ new CleanCSS().minify('@import url(http://127.0.0.1/present.css);a{color:red}', this.callback);
122
+ },
123
+ 'should not raise errors': function(errors, minified) {
124
+ assert.isNull(errors);
125
+ },
126
+ 'should process @import': function(errors, minified) {
127
+ assert.equal(minified, 'body{margin:0}div{padding:0}p{font-size:13px}a{color:red}');
128
+ },
129
+ teardown: function() {
130
+ assert.equal(this.reqMocks1.isDone(), true);
131
+ assert.equal(this.reqMocks2.isDone(), true);
132
+ nock.restore();
133
+ }
134
+ },
135
+ 'of an existing file with relative dependencies': {
136
+ topic: function() {
137
+ this.reqMocks = nock('http://127.0.0.1')
138
+ .get('/nested/present.css')
139
+ .reply(200, '@import url(../vendor/reset.css);p{font-size:13px}')
140
+ .get('/vendor/reset.css')
141
+ .reply(200, 'body{margin:0}');
142
+
143
+ new CleanCSS().minify('@import url(http://127.0.0.1/nested/present.css);a{color:red}', this.callback);
144
+ },
145
+ 'should not raise errors': function(errors, minified) {
146
+ assert.isNull(errors);
147
+ },
148
+ 'should process @import': function(errors, minified) {
149
+ assert.equal(minified, 'body{margin:0}p{font-size:13px}a{color:red}');
150
+ },
151
+ teardown: function() {
152
+ assert.equal(this.reqMocks.isDone(), true);
153
+ nock.restore();
154
+ }
155
+ },
156
+ 'of an existing file missing relative dependency': {
157
+ topic: function() {
158
+ this.reqMocks = nock('http://127.0.0.1')
159
+ .get('/nested/present.css')
160
+ .reply(200, '@import url(../missing.css);p{font-size:13px}')
161
+ .get('/missing.css')
162
+ .reply(404);
163
+
164
+ new CleanCSS().minify('@import url(http://127.0.0.1/nested/present.css);a{color:red}', this.callback);
165
+ },
166
+ 'should not raise errors': function(errors, minified) {
167
+ assert.equal(errors.length, 1);
168
+ assert.equal(errors[0], 'Broken @import declaration of "http://127.0.0.1/missing.css" - error 404');
169
+ },
170
+ 'should process @import': function(errors, minified) {
171
+ assert.equal(minified, '@import url(http://127.0.0.1/missing.css);p{font-size:13px}a{color:red}');
172
+ },
173
+ teardown: function() {
174
+ assert.equal(this.reqMocks.isDone(), true);
175
+ nock.restore();
176
+ }
177
+ },
178
+ 'of an existing file with URLs to rebase': {
179
+ topic: function() {
180
+ this.reqMocks = nock('http://127.0.0.1')
181
+ .get('/urls.css')
182
+ .reply(200, 'a{background:url(test.png)}');
183
+
184
+ new CleanCSS().minify('@import url(http://127.0.0.1/urls.css);', this.callback);
185
+ },
186
+ 'should not raise errors': function(errors, minified) {
187
+ assert.isNull(errors);
188
+ },
189
+ 'should process @import': function(errors, minified) {
190
+ assert.equal(minified, 'a{background:url(http://127.0.0.1/test.png)}');
191
+ },
192
+ teardown: function() {
193
+ assert.equal(this.reqMocks.isDone(), true);
194
+ nock.restore();
195
+ }
196
+ },
197
+ 'of an existing file with relative URLs to rebase': {
198
+ topic: function() {
199
+ this.reqMocks = nock('http://127.0.0.1')
200
+ .get('/base.css')
201
+ .reply(200, '@import url(deeply/nested/urls.css);')
202
+ .get('/deeply/nested/urls.css')
203
+ .reply(200, 'a{background:url(../images/test.png)}');
204
+
205
+ new CleanCSS().minify('@import url(http://127.0.0.1/base.css);', this.callback);
206
+ },
207
+ 'should not raise errors': function(errors, minified) {
208
+ assert.isNull(errors);
209
+ },
210
+ 'should process @import': function(errors, minified) {
211
+ assert.equal(minified, 'a{background:url(http://127.0.0.1/deeply/images/test.png)}');
212
+ },
213
+ teardown: function() {
214
+ assert.equal(this.reqMocks.isDone(), true);
215
+ nock.restore();
216
+ }
217
+ },
218
+ 'of a non-resolvable domain': {
219
+ topic: function() {
220
+ new CleanCSS().minify('@import url(http://notdefined.127.0.0.1/custom.css);a{color:red}', this.callback);
221
+ },
222
+ 'should not raise errors': function(errors, minified) {
223
+ assert.equal(errors.length, 1);
224
+ assert.equal(errors[0], 'Broken @import declaration of "http://notdefined.127.0.0.1/custom.css" - getaddrinfo ENOTFOUND');
225
+ },
226
+ 'should process @import': function(errors, minified) {
227
+ assert.equal(minified, '@import url(http://notdefined.127.0.0.1/custom.css);a{color:red}');
228
+ }
229
+ },
230
+ 'of a 30x response with absolute URL': {
231
+ topic: function() {
232
+ this.reqMocks = nock('http://127.0.0.1')
233
+ .get('/moved.css')
234
+ .reply(301, '', { 'Location': 'http://127.0.0.1/present.css' })
235
+ .get('/present.css')
236
+ .reply(200, 'body{margin:0}');
237
+
238
+ new CleanCSS().minify('@import url(http://127.0.0.1/moved.css);a{color:red}', this.callback);
239
+ },
240
+ 'should not raise errors': function(errors, minified) {
241
+ assert.isNull(errors);
242
+ },
243
+ 'should process @import': function(errors, minified) {
244
+ assert.equal(minified, 'body{margin:0}a{color:red}');
245
+ },
246
+ teardown: function() {
247
+ assert.equal(this.reqMocks.isDone(), true);
248
+ nock.restore();
249
+ }
250
+ },
251
+ 'of a 30x response with relative URL': {
252
+ topic: function() {
253
+ this.reqMocks = nock('http://127.0.0.1')
254
+ .get('/moved.css')
255
+ .reply(301, '', { 'Location': '/present.css' })
256
+ .get('/present.css')
257
+ .reply(200, 'body{margin:0}');
258
+
259
+ new CleanCSS().minify('@import url(http://127.0.0.1/moved.css);a{color:red}', this.callback);
260
+ },
261
+ 'should not raise errors': function(errors, minified) {
262
+ assert.isNull(errors);
263
+ },
264
+ 'should process @import': function(errors, minified) {
265
+ assert.equal(minified, 'body{margin:0}a{color:red}');
266
+ },
267
+ teardown: function() {
268
+ assert.equal(this.reqMocks.isDone(), true);
269
+ nock.restore();
270
+ }
271
+ },
272
+ 'of a timed out response': {
273
+ topic: function() {
274
+ var self = this;
275
+ var timeout = 100;
276
+ this.server = http.createServer(function(req, res) {
277
+ setTimeout(function() {}, timeout * 2);
278
+ });
279
+ this.server.listen(port, function() {
280
+ new CleanCSS({
281
+ inliner: {
282
+ timeout: timeout
283
+ }
284
+ }).minify('@import url(http://localhost:' + port + '/timeout.css);a{color:red}', self.callback);
285
+ });
286
+ },
287
+ 'should not raise errors': function(errors, minified) {
288
+ assert.equal(errors.length, 1);
289
+ assert.equal(errors[0], 'Broken @import declaration of "http://localhost:' + port + '/timeout.css" - timeout');
290
+ },
291
+ 'should process @import': function(errors, minified) {
292
+ assert.equal(minified, '@import url(http://localhost:' + port + '/timeout.css);a{color:red}');
293
+ },
294
+ teardown: function() {
295
+ this.server.close();
296
+ }
297
+ },
298
+ 'of a cyclical reference response': {
299
+ topic: function() {
300
+ this.reqMocks = nock('http://127.0.0.1')
301
+ .get('/one.css')
302
+ .reply(200, '@import url(/two.css);div{padding:0}')
303
+ .get('/two.css')
304
+ .reply(200, '@import url(http://127.0.0.1/two.css);body{margin:0}');
305
+
306
+ new CleanCSS().minify('@import url(http://127.0.0.1/one.css);a{color:red}', this.callback);
307
+ },
308
+ 'should not raise errors': function(errors, minified) {
309
+ assert.isNull(errors);
310
+ },
311
+ 'should process @import': function(errors, minified) {
312
+ assert.equal(minified, 'body{margin:0}div{padding:0}a{color:red}');
313
+ },
314
+ teardown: function() {
315
+ assert.equal(this.reqMocks.isDone(), true);
316
+ nock.restore();
317
+ }
318
+ },
319
+ 'of a resource without protocol': {
320
+ topic: function() {
321
+ this.reqMocks = nock('http://127.0.0.1')
322
+ .get('/no-protocol.css')
323
+ .reply(200, 'div{padding:0}');
324
+
325
+ new CleanCSS().minify('@import url(//127.0.0.1/no-protocol.css);a{color:red}', this.callback);
326
+ },
327
+ 'should not raise errors': function(errors, minified) {
328
+ assert.isNull(errors);
329
+ },
330
+ 'should process @import': function(errors, minified) {
331
+ assert.equal(minified, 'div{padding:0}a{color:red}');
332
+ },
333
+ teardown: function() {
334
+ assert.equal(this.reqMocks.isDone(), true);
335
+ nock.restore();
336
+ }
337
+ },
338
+ 'of a resource available via POST only': {
339
+ topic: function() {
340
+ this.reqMocks = nock('http://127.0.0.1')
341
+ .post('/computed.css')
342
+ .reply(200, 'div{padding:0}');
343
+
344
+ new CleanCSS({
345
+ inliner: {
346
+ request: {
347
+ method: 'POST'
348
+ }
349
+ }
350
+ }).minify('@import url(http://127.0.0.1/computed.css);a{color:red}', this.callback);
351
+ },
352
+ 'should not raise errors': function(errors, minified) {
353
+ assert.isNull(errors);
354
+ },
355
+ 'should process @import': function(errors, minified) {
356
+ assert.equal(minified, 'div{padding:0}a{color:red}');
357
+ },
358
+ teardown: function() {
359
+ assert.equal(this.reqMocks.isDone(), true);
360
+ nock.restore();
361
+ }
362
+ },
363
+ 'of a remote resource mixed with local ones': {
364
+ topic: function() {
365
+ var source = '@import url(http://127.0.0.1/remote.css);@import url(test/data/partials/one.css);';
366
+ this.reqMocks = nock('http://127.0.0.1')
367
+ .get('/remote.css')
368
+ .reply(200, 'div{padding:0}');
369
+
370
+ new CleanCSS().minify(source, this.callback);
371
+ },
372
+ 'should not raise errors': function(errors, minified) {
373
+ assert.isNull(errors);
374
+ },
375
+ 'should process @import': function(errors, minified) {
376
+ assert.equal(minified, 'div{padding:0}.one{color:red}');
377
+ },
378
+ teardown: function() {
379
+ assert.equal(this.reqMocks.isDone(), true);
380
+ nock.restore();
381
+ }
382
+ },
383
+ 'of a remote resource mixed with local ones but no callback': {
384
+ topic: function() {
385
+ var source = '@import url(http://127.0.0.1/remote.css);@import url(test/data/partials/one.css);';
386
+ this.reqMocks = nock('http://127.0.0.1')
387
+ .get('/remote.css')
388
+ .reply(200, 'div{padding:0}');
389
+
390
+ var minifier = new CleanCSS();
391
+ var minified = minifier.minify(source);
392
+ this.callback(null, minifier, minified);
393
+ },
394
+ 'should not raise errors': function(error, minifier) {
395
+ assert.isEmpty(minifier.errors);
396
+ },
397
+ 'should raise warnings': function(error, minifier) {
398
+ assert.equal(minifier.warnings.length, 1);
399
+ assert.match(minifier.warnings[0], /no callback given/);
400
+ },
401
+ 'should process @import': function(error, minifier, minified) {
402
+ assert.equal(minified, '@import url(http://127.0.0.1/remote.css);.one{color:red}');
403
+ },
404
+ teardown: function() {
405
+ assert.equal(this.reqMocks.isDone(), false);
406
+ nock.restore();
407
+ }
408
+ }
409
+ }).export(module);
@@ -0,0 +1,20 @@
1
+ var vows = require('vows');
2
+ var assert = require('assert');
3
+ var Splitter = require('../../lib/text/splitter');
4
+
5
+ var split = function (value, expectedValue, separator) {
6
+ return function () {
7
+ assert.deepEqual(new Splitter(separator).split(value), expectedValue);
8
+ };
9
+ };
10
+
11
+ vows.describe('splitter').addBatch({
12
+ 'empty': split('', [''], ','),
13
+ 'simple': split('none', ['none'], ','),
14
+ 'comma separated - level 0': split('#000,#fff,#0f0', ['#000', '#fff', '#0f0'], ','),
15
+ 'comma separated - level 1': split('rgb(0,0,0),#fff', ['rgb(0,0,0)', '#fff'], ','),
16
+ 'comma separated - level 2': split('linear-gradient(0,#fff,rgba(0,0,0)),red', ['linear-gradient(0,#fff,rgba(0,0,0))', 'red'], ','),
17
+ 'space separated - level 0': split('#000 #fff #0f0', ['#000', '#fff', '#0f0'], ' '),
18
+ 'space separated - level 1': split('rgb(0, 0, 0) #fff', ['rgb(0, 0, 0)', '#fff'], ' '),
19
+ 'space separated - level 2': split('linear-gradient(0, #fff, rgba(0, 0, 0)) red', ['linear-gradient(0, #fff, rgba(0, 0, 0))', 'red'], ' ')
20
+ }).export(module);
@@ -0,0 +1,2071 @@
1
+ /* jshint indent: false, multistr: true, quotmark: false */
2
+
3
+ var vows = require('vows');
4
+ var assert = require('assert');
5
+ var path = require('path');
6
+ var CleanCSS = require('../index');
7
+ var ColorShortener = require('../lib/colors/shortener');
8
+
9
+ var lineBreak = process.platform == 'win32' ? '\r\n' : '\n';
10
+ var cssContext = function(groups, options) {
11
+ var context = {};
12
+ var clean = function(expectedCss) {
13
+ return function(css) {
14
+ var minifiedCss = new CleanCSS(options).minify(css);
15
+ assert.equal(minifiedCss, expectedCss);
16
+ };
17
+ };
18
+
19
+ for (var g in groups) {
20
+ var transformation = groups[g];
21
+ if (typeof transformation == 'string')
22
+ transformation = [transformation, transformation];
23
+
24
+ context[g] = {
25
+ topic: transformation[0],
26
+ clean: clean(transformation[1])
27
+ };
28
+ }
29
+
30
+ return context;
31
+ };
32
+
33
+ var colorShorteningContext = function() {
34
+ var shortenerContext = {};
35
+ var shortener = new ColorShortener();
36
+
37
+ ['toName', 'toHex'].forEach(function(type) {
38
+ for (var from in shortener[type]) {
39
+ var to = shortener[type][from];
40
+ shortenerContext['should turn ' + from + ' into ' + to] = [
41
+ 'a{color:' + from + '}',
42
+ 'a{color:' + to + '}'
43
+ ];
44
+ }
45
+ });
46
+
47
+ return cssContext(shortenerContext);
48
+ };
49
+
50
+ var redefineContext = function(redefinitions, options) {
51
+ var context = {};
52
+ var vendorPrefixes = ['', '-moz-', '-o-', '-webkit-']; // there is no -ms-animation nor -ms-transition.
53
+
54
+ for (var property in redefinitions) {
55
+ for (var i = 0; i < redefinitions[property].length; i++) {
56
+ var by = redefinitions[property][i];
57
+ var prefixes = options.vendorPrefixes.indexOf(by) > -1 ? vendorPrefixes : [''];
58
+
59
+ for (var j = 0, m = prefixes.length; j < m; j++) {
60
+ var prefixedProperty = prefixes[j] + property;
61
+ var prefixedBy = prefixes[j] + by;
62
+ var zeroValue = options.noneFor.indexOf(prefixedProperty) > -1 ? 'none' : '0';
63
+
64
+ context['should override ' + prefixedProperty + ' by ' + prefixedBy] = [
65
+ 'a{' + prefixedProperty + ':inherit;' + prefixedBy + ':' + zeroValue + '}',
66
+ 'a{' + prefixedBy + ':' + zeroValue + '}'
67
+ ];
68
+ context['should not override ' + prefixedBy + ' by ' + prefixedProperty] =
69
+ 'a{' + prefixedBy + ':' + zeroValue + ';' + prefixedProperty + ':inherit}';
70
+ }
71
+ }
72
+ }
73
+
74
+ return cssContext(context);
75
+ };
76
+
77
+ vows.describe('clean-units').addBatch({
78
+ 'identity': cssContext({
79
+ 'preserve minified content': 'a{color:#f10}'
80
+ }),
81
+ 'semicolons': cssContext({
82
+ 'multiple semicolons': [
83
+ 'a{color:#fff;;;width:0; ;}',
84
+ 'a{color:#fff;width:0}'
85
+ ],
86
+ 'trailing semicolon': [
87
+ 'a{color:#fff;}',
88
+ 'a{color:#fff}'
89
+ ],
90
+ 'trailing semicolon and space': [
91
+ 'a{color:#fff ; }',
92
+ 'a{color:#fff}'
93
+ ],
94
+ 'comma and space': [
95
+ 'a{color:rgba(0, 0, 5, .5)}',
96
+ 'a{color:rgba(0,0,5,.5)}'
97
+ ]
98
+ }),
99
+ 'whitespace': cssContext({
100
+ 'one argument': [
101
+ 'div a { color:#fff }',
102
+ 'div a{color:#fff}'
103
+ ],
104
+ 'tabs': [
105
+ 'div\t\ta{display:block}\tp{color:red}',
106
+ 'div a{display:block}p{color:red}'
107
+ ],
108
+ 'line breaks #1': [
109
+ 'div \na\r\n { width:500px }',
110
+ 'div a{width:500px}'
111
+ ],
112
+ 'line breaks #2': [
113
+ 'div \na\r\n, p { width:500px }',
114
+ 'div a,p{width:500px}'
115
+ ],
116
+ 'line breaks #3': [
117
+ 'div a{width:500px\r\n}',
118
+ 'div a{width:500px}'
119
+ ],
120
+ 'line breaks with whitespace lines': [
121
+ 'div \n \t\n \na\r\n, p { width:500px }',
122
+ 'div a,p{width:500px}'
123
+ ],
124
+ 'multiple arguments': [
125
+ 'a{color:#fff ; font-weight: bolder }',
126
+ 'a{color:#fff;font-weight:bolder}'
127
+ ],
128
+ 'space delimited arguments': [
129
+ 'a {border: 1px solid #f10; margin: 0 auto }',
130
+ 'a{border:1px solid #f10;margin:0 auto}'
131
+ ],
132
+ 'at beginning': [
133
+ ' a {color:#fff}',
134
+ 'a{color:#fff}'
135
+ ],
136
+ 'at end': [
137
+ 'a{color:#fff } ',
138
+ 'a{color:#fff}'
139
+ ],
140
+ 'not inside calc method #1': [
141
+ 'a{width:-moz-calc(100% - 1em);width:calc(100% - 1em)}',
142
+ 'a{width:-moz-calc(100% - 1em);width:calc(100% - 1em)}'
143
+ ],
144
+ 'not inside calc method #2': [
145
+ 'div{margin:-moz-calc(50% + 15px) -moz-calc(50% + 15px);margin:calc(50% + .5rem) calc(50% + .5rem)}',
146
+ 'div{margin:-moz-calc(50% + 15px);margin:calc(50% + .5rem)}'
147
+ ],
148
+ 'not inside calc method with more parentheses': [
149
+ 'div{height:-moz-calc((10% + 12px)/2 + 10em)}',
150
+ 'div{height:-moz-calc((10% + 12px)/2 + 10em)}'
151
+ ],
152
+ 'not inside calc method with multiplication': [
153
+ 'div{height:-moz-calc(3 * 2em + 10px)}',
154
+ 'div{height:-moz-calc(3 * 2em + 10px)}'
155
+ ],
156
+ 'before colon': [
157
+ '#test{padding-left :0}',
158
+ '#test{padding-left:0}'
159
+ ],
160
+ 'before colon but not selectors #1': 'div :before{display:block}',
161
+ 'before colon but not selectors #2': 'div ::-webkit-search-decoration{display:block}',
162
+ 'before colon but not selectors #3': 'div :after{color:red}',
163
+ 'windows breaks': [
164
+ 'div>a{color:red\r\n }',
165
+ 'div>a{color:red}'
166
+ ],
167
+ 'whitespace in media queries': [
168
+ '@media ( min-width: 980px ) {\n#page .span4 {\nwidth: 250px;\n}\n\n.row {\nmargin-left: -10px;\n}\n}',
169
+ '@media (min-width:980px){#page .span4{width:250px}.row{margin-left:-10px}}'
170
+ ],
171
+ 'line breaks in media queries': [
172
+ '@media\nonly screen and (max-width: 1319px) and (min--moz-device-pixel-ratio: 1.5),\nonly screen and (max-width: 1319px) and (-moz-min-device-pixel-ratio: 1.5)\n{ a { color:#000 } }',
173
+ '@media only screen and (max-width:1319px) and (min--moz-device-pixel-ratio:1.5),only screen and (max-width:1319px) and (-moz-min-device-pixel-ratio:1.5){a{color:#000}}'
174
+ ],
175
+ 'in content preceded by #content': '#content{display:block}#foo{content:"\0BB "}',
176
+ 'in content preceded by .content': '.content{display:block}#foo{content:"\0BB "}',
177
+ 'in content preceded by line break': [
178
+ '.content{display:block}#foo{' + lineBreak + 'content:"x"}',
179
+ '.content{display:block}#foo{content:"x"}'
180
+ ],
181
+ 'after rgb': [
182
+ 'a{text-shadow:rgb(255,0,1) 1px 1px}',
183
+ 'a{text-shadow:#ff0001 1px 1px}'
184
+ ],
185
+ 'after rgba': 'a{text-shadow:rgba(255,0,0,1) 0 1px}',
186
+ 'after hsl': [
187
+ 'a{text-shadow:hsl(240,100%,40%) -1px 1px}',
188
+ 'a{text-shadow:#00c -1px 1px}'
189
+ ],
190
+ 'after hsla': 'a{text-shadow:hsla(240,100%,40%,.5) -1px 1px}'
191
+ }),
192
+ 'line breaks': cssContext({
193
+ 'line breaks': [
194
+ 'div\na\r\n{width:500px}',
195
+ 'div a{width:500px}'
196
+ ],
197
+ 'line breaks #2': [
198
+ 'div\na\r\n,p{width:500px}',
199
+ 'div a,p{width:500px}'
200
+ ],
201
+ 'multiple line breaks #2': [
202
+ 'div \r\n\r\na\r\n,p{width:500px}',
203
+ 'div a,p{width:500px}'
204
+ ],
205
+ 'line breaks with whitespace lines': [
206
+ 'div \n \t\n \na\r\n, p { width:500px }',
207
+ 'div a,p{width:500px}'
208
+ ],
209
+ 'line breaks with multiple selectors': [
210
+ 'p{width:500px}a{color:red}span{font-style:italic}',
211
+ 'p{width:500px}' + lineBreak + 'a{color:red}' + lineBreak + 'span{font-style:italic}'
212
+ ],
213
+ 'charset not at beginning': [
214
+ "a{ color: #f10; }\n@charset 'utf-8';\nb { font-weight: bolder}",
215
+ "@charset 'utf-8';" + lineBreak + "a{color:#f10}" + lineBreak + "b{font-weight:bolder}"
216
+ ],
217
+ 'charset multiple charsets': [
218
+ "@charset 'utf-8';\ndiv :before { display: block }\n@charset 'utf-8';\na { color: #f10 }",
219
+ "@charset 'utf-8';" + lineBreak + "div :before{display:block}" + lineBreak + "a{color:#f10}"
220
+ ],
221
+ 'charset with double line break': [
222
+ "@charset 'utf-8';" + lineBreak + lineBreak + "a{display:block}",
223
+ "@charset 'utf-8';" + lineBreak + "a{display:block}"
224
+ ],
225
+ 'uppercase charset': [
226
+ "@CHARSET 'utf-8';h1{color:red}",
227
+ 'h1{color:red}'
228
+ ],
229
+ 'mixed case charset': [
230
+ "@chArSET 'utf-8';h1{color:red}",
231
+ 'h1{color:red}'
232
+ ]
233
+ }, { keepBreaks: true }),
234
+ 'line breaks and important comments': cssContext({
235
+ 'charset to beginning with comment removal': [
236
+ "/*! some comment */" + lineBreak + lineBreak + "@charset 'utf-8';" + lineBreak + lineBreak + "a{display:block}",
237
+ "@charset 'utf-8';" + lineBreak + "a{display:block}"
238
+ ]
239
+ }, { keepBreaks: true, keepSpecialComments: 0 }),
240
+ 'selectors': cssContext({
241
+ 'remove spaces around selectors': [
242
+ 'div + span > em{display:block}',
243
+ 'div+span>em{display:block}'
244
+ ],
245
+ 'not remove spaces for pseudo-classes': [
246
+ 'div :first-child{display:block}',
247
+ 'div :first-child{display:block}'
248
+ ],
249
+ 'strip universal selector from id and class selectors': [
250
+ '* > *#id > *.class{display:block}',
251
+ '*>#id>.class{display:block}'
252
+ ],
253
+ 'strip universal selector from attribute selectors': [
254
+ '*:first-child > *[data-id]{display:block}',
255
+ ':first-child>[data-id]{display:block}'
256
+ ],
257
+ 'not strip standalone universal selector': [
258
+ 'label ~ * + span{display:block}',
259
+ 'label~*+span{display:block}'
260
+ ],
261
+ 'not expand + in selectors mixed with calc methods': [
262
+ 'div{width:calc(50% + 3em)}div + div{width:100%}div:hover{width:calc(50% + 4em)}* > div {border:1px solid #f0f}',
263
+ 'div{width:calc(50% + 3em)}div+div{width:100%}div:hover{width:calc(50% + 4em)}*>div{border:1px solid #f0f}'
264
+ ],
265
+ 'process selectors ending with -0 correctly': '.selector-0,a{display:block}',
266
+ 'process selectors ending with -1 correctly': '.selector-1,a{display:block}'
267
+ }),
268
+ 'comments': cssContext({
269
+ 'single line': [
270
+ 'a{color:#fff}/* some comment*/p{height:10px/* other comment */}',
271
+ 'a{color:#fff}p{height:10px}'
272
+ ],
273
+ 'multiline': [
274
+ '/* \r\n multiline \n comment */a{color:rgba(0,0,0,0.8)}',
275
+ 'a{color:rgba(0,0,0,.8)}'
276
+ ],
277
+ 'comment chars in comments': [
278
+ '/* \r\n comment chars * inside / comments */a{color:#fff}',
279
+ 'a{color:#fff}'
280
+ ],
281
+ 'comment inside block': [
282
+ 'a{/* \r\n some comments */color:#fff}',
283
+ 'a{color:#fff}'
284
+ ],
285
+ 'special comments': [
286
+ '/*! special comment */a{color:#f10} /* normal comment */',
287
+ '/*! special comment */a{color:#f10}'
288
+ ],
289
+ 'should keep exact structure': [
290
+ '/*! \n a > span { } with some content */',
291
+ '/*! \n a > span { } with some content */'
292
+ ],
293
+ 'should remove comments with forward slashes inside': [
294
+ '/*////*/a{color:red}',
295
+ 'a{color:red}'
296
+ ],
297
+ 'should properly handle line breaks and ** characters inside comments': [
298
+ '/**====**\\\n/**2nd comment line/**===**/a{color:red}',
299
+ 'a{color:red}'
300
+ ],
301
+ 'selector between comments': [
302
+ '/*comment*/*/*comment*/{color:red}',
303
+ '*{color:red}'
304
+ ],
305
+ 'inside url': [
306
+ "p{background-image:url('/*')}/* */",
307
+ "p{background-image:url(/*)}"
308
+ ],
309
+ 'inside url twice': [
310
+ "p{background-image:url('/* */\" /*')}/* */",
311
+ "p{background-image:url('/* */\" /*')}"
312
+ ],
313
+ 'inside url with more quotation': [
314
+ "p{background-image:url('/*');content:\"\"/* */}",
315
+ "p{background-image:url(/*);content:\"\"}"
316
+ ],
317
+ 'with quote marks': [
318
+ '/*"*//* */',
319
+ ''
320
+ ]
321
+ }),
322
+ 'escaping': cssContext({
323
+ 'escaped @ symbol in class name': '.pad--all0\\@sm{padding:0}',
324
+ 'escaped @ symbol in id': '#id\\@sm{padding:0}'
325
+ }),
326
+ 'important comments - one': cssContext({
327
+ 'strip all but first': [
328
+ '/*! important comment */a{color:red}/* some comment *//*! important comment */',
329
+ '/*! important comment */a{color:red}'
330
+ ]
331
+ }, { keepSpecialComments: 1 }),
332
+ 'important comments - none': cssContext({
333
+ 'strip all': [
334
+ '/*! important comment */a{color:red}/* some comment *//*! important comment */',
335
+ 'a{color:red}'
336
+ ],
337
+ 'move charset before': [
338
+ "/*! some comment */" + lineBreak + lineBreak + "@charset 'utf-8';" + lineBreak + lineBreak + "a{display:block}",
339
+ "@charset 'utf-8';a{display:block}"
340
+ ]
341
+ }, { keepSpecialComments: 0 }),
342
+ 'important comments - keepSpecialComments when a string': cssContext({
343
+ 'strip all': [
344
+ '/*! important comment */a{color:red}/* some comment *//*! important comment */',
345
+ 'a{color:red}'
346
+ ]
347
+ }, { keepSpecialComments: '0' }),
348
+ 'expressions': cssContext({
349
+ 'empty': 'a{color:expression()}',
350
+ 'method call': 'a{color:expression(this.parentNode.currentStyle.color)}',
351
+ 'multiple call': 'a{color:expression(x = 0 , this.parentNode.currentStyle.color)}',
352
+ 'mixed content': "a{zoom:expression(this.runtimeStyle[\"zoom\"] = '1', this.innerHTML = '&#xf187;')}",
353
+ 'in comment': "/*! expression(this.runtimeStyle['zoom']) */",
354
+ 'complex': 'a{width:expression((this.parentNode.innerWidth + this.parentNode.innerHeight) / 2 )}',
355
+ 'with parentheses': "a{width:expression(this.parentNode.innerText == ')' ? '5px' : '10px' )}",
356
+ 'open ended (broken)': "a{width:expression(this.parentNode.innerText == }",
357
+ 'function call & advanced': 'a{zoom:expression(function(el){el.style.zoom="1"}(this))}'
358
+ }),
359
+ 'text content': cssContext({
360
+ 'normal #1': 'a{content:"."}',
361
+ 'normal #2': [
362
+ 'a:before{content : "test\'s test"; }',
363
+ 'a:before{content:"test\'s test"}'
364
+ ],
365
+ 'open quote': [
366
+ 'a{content : open-quote;opacity:1}',
367
+ 'a{content:open-quote;opacity:1}'
368
+ ],
369
+ 'close quote': [
370
+ 'a{content: close-quote;clear:left}',
371
+ 'a{content:close-quote;clear:left}'
372
+ ],
373
+ 'special characters': [
374
+ 'a{content : " a > div { } "}',
375
+ 'a{content:" a > div { } "}'
376
+ ],
377
+ 'with JSON': 'body::before{content:\'{ "current" : "small", "all" : ["small"], "position" : 0 }\'}'
378
+ }),
379
+ 'zero values': cssContext({
380
+ 'with units': [
381
+ 'a{margin:0px 0pt 0em 0%;padding: 0in 0cm 0mm 0pc;border-top-width:0ex}',
382
+ 'a{margin:0;padding:0;border-top-width:0}'
383
+ ],
384
+ 'multiple into one': [
385
+ 'a{margin:0 0 0 0;padding:0 0 0 0;border-width:0 0 0 0}',
386
+ 'a{margin:0;padding:0;border-width:0}'
387
+ ],
388
+ 'background\'s none to zero': [
389
+ 'a{background:none}',
390
+ 'a{background:0 0}'
391
+ ],
392
+ 'border\'s none to none': 'a{border:none}p{border-top:none}',
393
+ 'background:transparent to zero': [
394
+ 'a{background:transparent}p{background:transparent url(logo.png)}',
395
+ 'a{background:0 0}p{background:url(logo.png)}'
396
+ ],
397
+ 'outline:none to outline:0': [
398
+ 'a{outline:none}',
399
+ 'a{outline:0}'
400
+ ],
401
+ 'display:none not changed': 'a{display:none}',
402
+ 'mixed zeros not changed': 'div{margin:0 0 1px 2px}',
403
+ 'mixed zeros not changed #2': 'div{padding:0 1px 0 3px}',
404
+ 'mixed zeros not changed #3': 'div{padding:10px 0 0 1px}',
405
+ 'multiple zeros with fractions #1': [
406
+ 'div{padding:0 0 0 0.5em}',
407
+ 'div{padding:0 0 0 .5em}'
408
+ ],
409
+ 'multiple zeros with fractions #2': [
410
+ 'div{padding:0 0 0 .5em}',
411
+ 'div{padding:0 0 0 .5em}'
412
+ ],
413
+ 'rect zeros #1': 'div{clip:rect(0 0 0 0)}',
414
+ 'rect zeros #2': [
415
+ 'div{clip:rect(0px 0px 0px 0px)}',
416
+ 'div{clip:rect(0 0 0 0)}'
417
+ ],
418
+ 'rect zeros #3': [
419
+ 'div{clip:rect( 0px 0px 0px 0px )}',
420
+ 'div{clip:rect(0 0 0 0)}'
421
+ ],
422
+ 'rect zeros #4': [
423
+ 'div{clip:rect(0px, 0px, 0px, 0px)}',
424
+ 'div{clip:rect(0,0,0,0)}'
425
+ ],
426
+ 'rect zeros #5': [
427
+ 'div{clip:rect(0.5% 0px 0px 0px)}',
428
+ 'div{clip:rect(0.5% 0 0 0)}'
429
+ ],
430
+ 'rect zeros #6': [
431
+ 'div{clip:rect(0px 0px 0px 10px)}',
432
+ 'div{clip:rect(0 0 0 10px)}'
433
+ ],
434
+ 'box shadow zeros with four zeros': [
435
+ 'a{box-shadow:0 0 0 0}',
436
+ 'a{box-shadow:0 0}'
437
+ ],
438
+ 'box shadow with two zeros': 'a{box-shadow:0 0}',
439
+ 'box shadow with three zeros and a fraction': [
440
+ 'a{box-shadow:0 0 0 0.15em #EBEBEB}',
441
+ 'a{box-shadow:0 0 0 .15em #EBEBEB}'
442
+ ],
443
+ 'box shadow with three zeros and a value': 'a{box-shadow:0 0 0 15px #EBEBEB}',
444
+ 'rems': [
445
+ 'div{width:0rem;height:0rem}',
446
+ 'div{width:0;height:0}'
447
+ ],
448
+ 'prefixed box shadow zeros': [
449
+ 'a{-webkit-box-shadow:0 0 0 0; -moz-box-shadow:0 0 0 0}',
450
+ 'a{-webkit-box-shadow:0 0;-moz-box-shadow:0 0}'
451
+ ],
452
+ 'zero as .0 #1': [
453
+ 'a{color:rgba(0,0,.0,1)}',
454
+ 'a{color:rgba(0,0,0,1)}'
455
+ ],
456
+ 'zero as .0 #2': [
457
+ 'body{margin:.0}',
458
+ 'body{margin:0}'
459
+ ],
460
+ 'zero as .0 #3': [
461
+ 'body{margin:.0em}',
462
+ 'body{margin:0}'
463
+ ],
464
+ 'zero as .0 #4': [
465
+ 'body{margin:.0 1em .0 .0}',
466
+ 'body{margin:0 1em 0 0}'
467
+ ],
468
+ 'missing #1': [
469
+ 'body{margin:2.em}',
470
+ 'body{margin:2em}'
471
+ ],
472
+ 'missing #2': [
473
+ 'p{opacity:1.}',
474
+ 'p{opacity:1}'
475
+ ],
476
+ 'missing #3': [
477
+ 'p{opacity:11.px}',
478
+ 'p{opacity:11px}'
479
+ ],
480
+ 'minus zero as value to zero': [
481
+ 'body{margin:-0}',
482
+ 'body{margin:0}'
483
+ ],
484
+ 'minus zero in function to zero': [
485
+ 'body{color:rgba(-0,-0,-0,-0)}',
486
+ 'body{color:transparent}'
487
+ ],
488
+ 'minus zero px to zero': [
489
+ 'body{margin:-0px}',
490
+ 'body{margin:0}'
491
+ ],
492
+ 'zero em to zero': [
493
+ 'body{margin:0.0em}',
494
+ 'body{margin:0}'
495
+ ]
496
+ }),
497
+ 'zero values in ie8 compatibility mode': cssContext({
498
+ 'rems': 'div{width:0rem;height:0rem}'
499
+ }, { compatibility: 'ie8' }),
500
+ 'zero values in any other compatibility mode': cssContext({
501
+ 'rems': [
502
+ 'div{width:0rem;height:0rem}',
503
+ 'div{width:0;height:0}'
504
+ ]
505
+ }, { compatibility: '*' }),
506
+ 'shorthands': cssContext({
507
+ 'padding - same 4 values': [
508
+ 'div{padding:1px 1px 1px 1px}',
509
+ 'div{padding:1px}'
510
+ ],
511
+ 'margin - same 4 values': [
512
+ 'div{margin:1% 1% 1% 1%}',
513
+ 'div{margin:1%}'
514
+ ],
515
+ 'border-width - same 4 values': [
516
+ 'div{border-width:1em 1em 1em 1em}',
517
+ 'div{border-width:1em}'
518
+ ],
519
+ 'border-style - same 4 values': [
520
+ 'div{border-style:solid solid solid solid}',
521
+ 'div{border-style:solid}'
522
+ ],
523
+ 'border-color - same 4 values': [
524
+ 'div{border-color:red red red red}',
525
+ 'div{border-color:red}'
526
+ ],
527
+ 'border-color - same 4 values as hex': [
528
+ 'div{border-color:#f0f #f0f #f0f #f0f}',
529
+ 'div{border-color:#f0f}'
530
+ ],
531
+ 'border-color - same 4 values as rgb': [
532
+ 'div{border-color:rgb(0,0,0) rgb(0,0,0) rgb(0,0,0) rgb(0,0,0)}',
533
+ 'div{border-color:#000}'
534
+ ],
535
+ 'border-color - same 4 values as rgba': [
536
+ 'div{border-color:rgba(0,0,0,.5) rgba(0,0,0,.5) rgba(0,0,0,.5) rgba(0,0,0,.5)}',
537
+ 'div{border-color:rgba(0,0,0,.5)}'
538
+ ],
539
+ 'border-radius - same 4 values': [
540
+ 'div{border-radius:3px 3px 3px 3px}',
541
+ 'div{border-radius:3px}'
542
+ ],
543
+ 'border-radius - same 4 values with vendor prefixes': [
544
+ 'div{-moz-border-radius:3px 3px 3px 3px;-o-border-radius:3px 3px 3px 3px;-webkit-border-radius:3px 3px 3px 3px;border-radius:3px 3px 3px 3px}',
545
+ 'div{-moz-border-radius:3px;-o-border-radius:3px;-webkit-border-radius:3px;border-radius:3px}'
546
+ ],
547
+ 'padding - same pairs': [
548
+ 'div{padding:15.5em 10.5em 15.5em 10.5em}',
549
+ 'div{padding:15.5em 10.5em}'
550
+ ],
551
+ 'margin - same 2nd and 4th value': [
552
+ 'div{margin:1px 2px 3px 2px}',
553
+ 'div{margin:1px 2px 3px}'
554
+ ],
555
+ 'padding - same 3 values': [
556
+ 'div{padding:1px 1px 1px}',
557
+ 'div{padding:1px}'
558
+ ],
559
+ 'padding - different 3 values': 'div{padding:1px 1em 1%}',
560
+ 'margin - 3 callapsible values': [
561
+ 'div{margin:1ex 2ex 1ex}',
562
+ 'div{margin:1ex 2ex}'
563
+ ],
564
+ 'border-radius - same 3 values with one vendor prefixe': [
565
+ 'div{-webkit-border-radius:3px 3px 3px;border-radius:3px 3px 3px}',
566
+ 'div{-webkit-border-radius:3px;border-radius:3px}'
567
+ ],
568
+ 'border-color - same 2nd and 4th value as rgb': [
569
+ 'div{border-color:rgb(0,0,0) rgb(34,0,0) rgb(255,0,0) rgb(34,0,0)}',
570
+ 'div{border-color:#000 #200 red}'
571
+ ],
572
+ 'margin - 3 different values': 'div{margin:1px 1px 3px}',
573
+ 'border width - 3 different values': 'div{border-width:1px 2px 3px}',
574
+ 'padding - same 2 values': [
575
+ 'div{padding:1px 1px}',
576
+ 'div{padding:1px}'
577
+ ],
578
+ 'margin - same 2 values': [
579
+ 'div{margin:5% 5%}',
580
+ 'div{margin:5%}'
581
+ ],
582
+ 'border-width - same 2 values': [
583
+ 'div{border-width:.5em .5em}',
584
+ 'div{border-width:.5em}'
585
+ ],
586
+ 'different units': 'div{padding:1px 1em 1% 1rem}',
587
+ 'fractions': [
588
+ 'div{margin:.1em .1em .1em .1em}',
589
+ 'div{margin:.1em}'
590
+ ],
591
+ 'preceeding value': [
592
+ 'div{padding:010px 00015px}',
593
+ 'div{padding:10px 15px}'
594
+ ],
595
+ 'preceeding value with fraction zeros': [
596
+ 'div{padding:010.0em .05rem}',
597
+ 'div{padding:10em .05rem}'
598
+ ]
599
+ }),
600
+ 'floats': cssContext({
601
+ 'strips zero in fractions': [
602
+ 'a{ margin-bottom: 0.5em}',
603
+ 'a{margin-bottom:.5em}'
604
+ ],
605
+ 'not strips zero in fractions of numbers greater than zero': [
606
+ 'a{ margin-bottom: 20.5em}',
607
+ 'a{margin-bottom:20.5em}'
608
+ ],
609
+ 'strip fraction zero #1': [
610
+ 'a{opacity:1.0}',
611
+ 'a{opacity:1}'
612
+ ],
613
+ 'strip fraction zero #2': [
614
+ 'a{opacity:15.000%}',
615
+ 'a{opacity:15%}'
616
+ ],
617
+ 'strip fraction zero #3': [
618
+ 'a{padding:15.55000em}',
619
+ 'a{padding:15.55em}'
620
+ ],
621
+ 'strip fraction zero #4': 'a{padding:15.101em}',
622
+ 'strip fraction zero #5': [
623
+ 'a{border-width:0.20em 20.30em}',
624
+ 'a{border-width:.2em 20.3em}'
625
+ ],
626
+ 'strip fraction zeros': [
627
+ 'div{margin:1.000em 2.00em 3.100em 4.01em}',
628
+ 'div{margin:1em 2em 3.1em 4.01em}'
629
+ ],
630
+ 'round pixels up to 2nd decimal place': [
631
+ 'div{transform:translateY(-418.505123px)}',
632
+ 'div{transform:translateY(-418.51px)}'
633
+ ],
634
+ 'round pixels down to 2nd decimal place': [
635
+ 'div{transform:translateY(0.504123px)}',
636
+ 'div{transform:translateY(0.5px)}'
637
+ ],
638
+ 'do not round 2nd decimal place pixels': 'div{transform:translateY(20.55px)}',
639
+ 'do not round percentages': 'div{left:20.505%}',
640
+ 'do not round ems': 'div{font-size:1.505em}'
641
+ }),
642
+ 'floats custom rounding': cssContext({
643
+ 'rounds to 4 values': [
644
+ 'div{transform:translateY(-418.505123px)}',
645
+ 'div{transform:translateY(-418.5051px)}'
646
+ ]
647
+ }, { roundingPrecision: 4 }),
648
+ 'colors': cssContext({
649
+ 'shorten rgb to standard hexadecimal format': [
650
+ 'a{ color:rgb(5, 10, 15) }',
651
+ 'a{color:#050a0f}'
652
+ ],
653
+ 'skip rgba shortening': [
654
+ 'a{ color:rgba(5, 10, 15, 0.5)}',
655
+ 'a{color:rgba(5,10,15,.5)}'
656
+ ],
657
+ 'shorten colors to 3 digit hex instead of 6 digit': [
658
+ 'a{ background-color: #aa0000; color:rgb(0, 17, 255)}',
659
+ 'a{background-color:#a00;color:#01f}'
660
+ ],
661
+ 'skip shortening IE filter colors': [
662
+ 'a{ filter: chroma(color = "#ff0000")}',
663
+ 'a{filter:chroma(color="#ff0000")}'
664
+ ],
665
+ 'color names to hex values': [
666
+ 'a{color:white;border-color:black;background-color:fuchsia}p{background:yellow}',
667
+ 'a{color:#fff;border-color:#000;background-color:#f0f}p{background:#ff0}'
668
+ ],
669
+ 'keep selectors with color name #1': ".black-and-white .foo{color:#fff;background-color:#000}",
670
+ 'keep selectors with color name #2': ".go-blues{background:#000}",
671
+ 'keep selectors with color name #3': "#top_white{background:#000}",
672
+ 'keep selectors with color name #4': "a[data-sth=white]{background:#000}",
673
+ 'color names to hex values with important': [
674
+ 'a{color:white !important}',
675
+ 'a{color:#fff!important}'
676
+ ],
677
+ 'color names to hex values in gradients': [
678
+ 'p{background:linear-gradient(-90deg,black,white)}',
679
+ 'p{background:linear-gradient(-90deg,#000,#fff)}'
680
+ ],
681
+ 'hex value to color name if shorter': [
682
+ 'p{color:#f00}',
683
+ 'p{color:red}'
684
+ ],
685
+ 'upper case hex value to color name if shorter': [
686
+ 'p{color:#F00}',
687
+ 'p{color:red}'
688
+ ],
689
+ 'upper case long hex value to color name if shorter': [
690
+ 'p{color:#FF0000}',
691
+ 'p{color:red}'
692
+ ],
693
+ 'hex value to color name in borders': [
694
+ 'p{border:1px solid #f00}',
695
+ 'p{border:1px solid red}'
696
+ ],
697
+ 'hex value to color name in gradients': [
698
+ 'p{background:-moz-linear-gradient(-90deg,#000,#f00)}',
699
+ 'p{background:-moz-linear-gradient(-90deg,#000,red)}'
700
+ ],
701
+ 'hex value to color name in gradients #2': [
702
+ 'p{background:-webkit-gradient(linear, left top, left bottom, from(#000), to(#f00))}',
703
+ 'p{background:-webkit-gradient(linear,left top,left bottom,from(#000),to(red))}'
704
+ ],
705
+ 'border color - keep unchanged': 'p{border:1px solid #f94311}',
706
+ 'border color - hex to name': [
707
+ 'p{border:1em dotted #f00}',
708
+ 'p{border:1em dotted red}'
709
+ ],
710
+ 'border color - name to hex': [
711
+ 'p{border:1em dotted white}',
712
+ 'p{border:1em dotted #fff}'
713
+ ],
714
+ 'border color - rgb': [
715
+ 'p{border:1em dotted rgb(255,0,0)}',
716
+ 'p{border:1em dotted red}'
717
+ ],
718
+ 'colors and colons': 'a{background-image:linear-gradient(top,red,#e6e6e6)}',
719
+ 'colors and parentheses': 'a{background-image:-webkit-gradient(linear,0 0,0 100%,from(#fff),to(#e6e6e6))}',
720
+ 'colors in ie filters': 'a{filter:chroma(color=#ffffff)}',
721
+ 'colors in ie filters 2': "a{progid:DXImageTransform.Microsoft.gradient(startColorstr='#cccccc', endColorstr='#000000')}",
722
+ 'colors in ie filters 3': "a{progid:DXImageTransform.Microsoft.gradient(startColorstr='#DDDDDD', endColorstr='#333333')}",
723
+ 'rgb percents': 'a{color:rgb(100%,0%,0%)}',
724
+ 'rgba percents': 'a{color:rgba(100%,0%,0%,.5)}',
725
+ 'hsla percents': 'a{color:hsla(1,0%,0%,.5)}',
726
+ 'hsla custom ': 'a{color:hsl(80,30%,50%,.5)}',
727
+ 'hsl to hex #1': [
728
+ 'a{color:hsl(0,0%,0%)}',
729
+ 'a{color:#000}'
730
+ ],
731
+ 'hsl to hex #2': [
732
+ 'a{color:hsl(0,100%,100%)}',
733
+ 'a{color:#fff}'
734
+ ],
735
+ 'hsl to hex #3': [
736
+ 'a{color:hsl(240,100%,50%)}',
737
+ 'a{color:#00f}'
738
+ ],
739
+ 'hsl to hex #4': [
740
+ 'a{color:hsl(240,100%,50%)}',
741
+ 'a{color:#00f}'
742
+ ],
743
+ 'hsl to hex #5': [
744
+ 'a{color:hsl(120,100%,25%)}',
745
+ 'a{color:#007f00}'
746
+ ],
747
+ 'hsl to hex #6': [
748
+ 'a{color:hsl(99,66%,33%)}',
749
+ 'a{color:#438b1c}'
750
+ ],
751
+ 'hsl to hex #7': [
752
+ 'a{color:hsl(360,100%,50%)}',
753
+ 'a{color:red}'
754
+ ],
755
+ 'hsla not to hex': 'a{color:hsl(99,66%,33%,.5)}',
756
+ 'hsl out of bounds #1': [
757
+ 'a{color:hsl(120,200%,50%)}',
758
+ 'a{color:#0f0}'
759
+ ],
760
+ 'hsl out of bounds #2': [
761
+ 'a{color:hsl(120,-100%,50%)}',
762
+ 'a{color:#7f7f7f}'
763
+ ],
764
+ 'hsl out of bounds #3': [
765
+ 'a{color:hsl(480,100%,25%)}',
766
+ 'a{color:#007f00}'
767
+ ],
768
+ 'hsl out of bounds #4': [
769
+ 'a{color:hsl(-240,100%,75%)}',
770
+ 'a{color:#7fff7f}'
771
+ ],
772
+ 'hsl out of bounds #5': [
773
+ 'a{color:hsl(-600,100%,75%)}',
774
+ 'a{color:#7fff7f}'
775
+ ],
776
+ 'hsl out of bounds #6': [
777
+ 'a{color:hsl(0,0%,122%)}',
778
+ 'a{color:#fff}'
779
+ ],
780
+ 'hsl out of bounds #7': [
781
+ 'a{color:hsl(0,0%,-10%)}',
782
+ 'a{color:#000}'
783
+ ],
784
+ 'rgb out of a lower bound': [
785
+ 'a{color:rgb(-1,-1,-1)}',
786
+ 'a{color:#000}'
787
+ ],
788
+ 'rgb out of an upper bound': [
789
+ 'a{color:rgb(256,256,256)}',
790
+ 'a{color:#fff}'
791
+ ],
792
+ 'turns rgba(0,0,0,0) to transparent': [
793
+ 'a{color:rgba(0,0,0,0)}',
794
+ 'a{color:transparent}'
795
+ ],
796
+ 'turns rgba(0.0,0.0,.0,0) to transparent': [
797
+ 'a{color:rgba(0.0,0.0,.0,0)}',
798
+ 'a{color:transparent}'
799
+ ],
800
+ 'turns rgba(255,255,255,0) to transparent': [
801
+ 'a{color:rgba(255,255,255,0)}',
802
+ 'a{color:transparent}'
803
+ ],
804
+ 'turns rgba(255,0,255,0) to transparent': [
805
+ 'a{color:rgba(255,0,255,0)}',
806
+ 'a{color:transparent}'
807
+ ],
808
+ 'turns hsla(120,100%,50%,0) to transparent': [
809
+ 'a{color:hsla(120,100%,50%,0)}',
810
+ 'a{color:transparent}'
811
+ ],
812
+ 'keeps rgba(0,0,0,.5)': 'a{color:rgba(0,0,0,.5)}',
813
+ 'keeps rgba(0,255,0,.5)': 'a{color:rgba(0,255,0,.5)}',
814
+ 'keeps hsla(120,100%,50%,.5)': 'a{color:hsla(120,100%,50%,.5)}',
815
+ 'keeps rgba(0,0,0,0) when inside a gradient': 'a{background:linear-gradient(0,#000,rgba(0,0,0,0))}',
816
+ 'keeps hsla(120,100%,50%,0) when inside a gradient': 'a{background:linear-gradient(0,#000,hsla(120,100%,50%,0))}',
817
+ 'removes only right transparent colors': [
818
+ 'a{background-color:linear-gradient(0,#000,hsla(120,100%,50%,0)),rgba(0,0,0,0)}',
819
+ 'a{background-color:linear-gradient(0,#000,hsla(120,100%,50%,0)),transparent}'
820
+ ]
821
+ }),
822
+ 'border-radius': cssContext({
823
+ 'border radius H+V 0/0': [
824
+ 'a{border-radius:0 / 0}',
825
+ 'a{border-radius:0}'
826
+ ],
827
+ 'border radius side H+V 0/0': [
828
+ 'a{border-top-left-radius:0 / 0}',
829
+ 'a{border-top-left-radius:0}'
830
+ ],
831
+ 'border radius H+V same values': [
832
+ 'a{border-radius:5px / 5px}',
833
+ 'a{border-radius:5px}'
834
+ ],
835
+ 'border radius side H+V same values': [
836
+ 'a{border-top-left-radius:1em / 1em}',
837
+ 'a{border-top-left-radius:1em}'
838
+ ],
839
+ 'border radius H+V same expanded values': [
840
+ 'a{border-radius:5px 5px 5px 5px / 5px 5px}',
841
+ 'a{border-radius:5px}'
842
+ ]
843
+ }),
844
+ 'shortening colors': colorShorteningContext(),
845
+ 'font weights': cssContext({
846
+ 'font-weight:normal to 400': [
847
+ 'p{font-weight:normal}',
848
+ 'p{font-weight:400}'
849
+ ],
850
+ 'font-weight:bold to 700': [
851
+ 'p{font-weight:bold}',
852
+ 'p{font-weight:700}'
853
+ ],
854
+ 'font weight in font declarations': [
855
+ 'body{font:normal 13px/20px "Helvetica Neue",Helvetica,Arial,sans-serif}',
856
+ 'body{font:400 13px/20px "Helvetica Neue",Helvetica,Arial,sans-serif}'
857
+ ],
858
+ 'font weight in font declarations with fraction units': [
859
+ 'p{font:bold .9rem Helvetica}',
860
+ 'p{font:700 .9rem Helvetica}'
861
+ ],
862
+ 'multiple changes': [
863
+ 'p{font-weight:bold!important;width:100%;font:normal 12px Helvetica}',
864
+ 'p{font-weight:700!important;width:100%;font:400 12px Helvetica}'
865
+ ],
866
+ 'font weight in extended font declarations': 'a{font:normal normal normal 13px/20px Helvetica}',
867
+ 'font weight where style and weight are declared': 'a{font:normal 300 100%/1.5 sans-serif}'
868
+ }),
869
+ 'unicode': cssContext({
870
+ 'font-names': 'body{font-family:\\5FAE\\8F6F\\96C5\\9ED1,\\5B8B\\4F53,sans-serif}'
871
+ }),
872
+ 'urls': cssContext({
873
+ 'keep urls without parentheses unchanged': 'a{background:url(/images/blank.png)}',
874
+ 'keep non-encoded data URI unchanged': ".icon-logo{background-image:url('data:image/svg+xml;charset=US-ASCII')}",
875
+ 'strip quotes from base64 encoded PNG data URI': [
876
+ ".icon-logo{background-image:url('data:image/png;base64,iVBORw0')}",
877
+ ".icon-logo{background-image:url(data:image/png;base64,iVBORw0)}"
878
+ ],
879
+ 'strip quotes from base64 encoded ICO data URI': [
880
+ '.icon-logo{background-image:url("data:image/x-icon;base64,AAABAAEAEBA")}',
881
+ '.icon-logo{background-image:url(data:image/x-icon;base64,AAABAAEAEBA)}'
882
+ ],
883
+ 'strip single parentheses': [
884
+ "a{background:url('/images/blank.png')}",
885
+ "a{background:url(/images/blank.png)}"
886
+ ],
887
+ 'strip double parentheses': [
888
+ 'a{background:url("/images/blank.png")}',
889
+ 'a{background:url(/images/blank.png)}'
890
+ ],
891
+ 'strip more': [
892
+ 'p{background:url("/images/blank.png")}b{display:block}a{background:url("/images/blank2.png")}',
893
+ 'p{background:url(/images/blank.png)}b{display:block}a{background:url(/images/blank2.png)}'
894
+ ],
895
+ 'not strip comments if spaces inside': [
896
+ 'p{background:url("/images/long image name.png")}b{display:block}a{background:url("/images/no-spaces.png")}',
897
+ 'p{background:url("/images/long image name.png")}b{display:block}a{background:url(/images/no-spaces.png)}'
898
+ ],
899
+ 'not add a space before url\'s hash': "a{background:url(/fonts/d90b3358-e1e2-4abb-ba96-356983a54c22.svg#d90b3358-e1e2-4abb-ba96-356983a54c22)}",
900
+ 'keep urls from being stripped down #1': 'a{background:url(/image-1.0.png)}',
901
+ 'keep urls from being stripped down #2': "a{background:url(/image-white.png)}",
902
+ 'keep urls from being stripped down #3': "a{background:url(/libraries/jquery-ui-1.10.1.custom/images/ui-bg_highlight-soft_100_eeeeee_1x100.png) 50% top #eee}",
903
+ 'keep special markers in comments (so order is important)': '/*! __ESCAPED_URL_CLEAN_CSS0__ */a{display:block}',
904
+ 'strip new line in urls': [
905
+ 'a{background:url(/very/long/\
906
+ path)}',
907
+ 'a{background:url(/very/long/path)}'
908
+ ],
909
+ 'strip new line in urls which could be unquoted': [
910
+ 'a{background:url("/very/long/\
911
+ path")}',
912
+ 'a{background:url(/very/long/path)}'
913
+ ]
914
+ }),
915
+ 'urls rewriting - no root or target': cssContext({
916
+ 'no @import': 'a{background:url(test/data/partials/extra/down.gif) no-repeat}',
917
+ 'relative @import': [
918
+ '@import url(test/data/partials-relative/base.css);',
919
+ 'a{background:url(test/data/partials/extra/down.gif) no-repeat}'
920
+ ],
921
+ 'relative @import twice': [
922
+ '@import url(test/data/partials-relative/extra/included.css);',
923
+ 'a{background:url(test/data/partials/extra/down.gif) no-repeat}'
924
+ ],
925
+ 'absolute @import': [
926
+ '@import url(/test/data/partials-relative/base.css);',
927
+ 'a{background:url(test/data/partials/extra/down.gif) no-repeat}'
928
+ ]
929
+ }),
930
+ 'urls rewriting - root but no target': cssContext({
931
+ 'no @import': [
932
+ 'a{background:url(../partials/extra/down.gif) no-repeat}',
933
+ 'a{background:url(/test/data/partials/extra/down.gif) no-repeat}'
934
+ ],
935
+ 'relative @import': [
936
+ '@import url(base.css);',
937
+ 'a{background:url(/test/data/partials/extra/down.gif) no-repeat}'
938
+ ],
939
+ 'absolute @import': [
940
+ '@import url(/test/data/partials-relative/base.css);',
941
+ 'a{background:url(/test/data/partials/extra/down.gif) no-repeat}'
942
+ ]
943
+ }, {
944
+ root: process.cwd(),
945
+ relativeTo: path.join('test', 'data', 'partials-relative')
946
+ }),
947
+ 'urls rewriting - no root but target': cssContext({
948
+ 'no @import': [
949
+ 'a{background:url(../partials/extra/down.gif) no-repeat}',
950
+ 'a{background:url(test/data/partials/extra/down.gif) no-repeat}'
951
+ ],
952
+ 'relative @import': [
953
+ '@import url(base.css);',
954
+ 'a{background:url(test/data/partials/extra/down.gif) no-repeat}'
955
+ ],
956
+ 'absolute @import': [
957
+ '@import url(/test/data/partials-relative/base.css);',
958
+ 'a{background:url(test/data/partials/extra/down.gif) no-repeat}'
959
+ ]
960
+ }, {
961
+ target: path.join(process.cwd(), 'test.css'),
962
+ relativeTo: path.join('test', 'data', 'partials-relative')
963
+ }),
964
+ 'urls rewriting - root and target': cssContext({
965
+ 'no @import': [
966
+ 'a{background:url(../partials/extra/down.gif) no-repeat}',
967
+ 'a{background:url(/test/data/partials/extra/down.gif) no-repeat}'
968
+ ],
969
+ 'relative @import': [
970
+ '@import url(base.css);',
971
+ 'a{background:url(/test/data/partials/extra/down.gif) no-repeat}'
972
+ ],
973
+ 'absolute @import': [
974
+ '@import url(/test/data/partials-relative/base.css);',
975
+ 'a{background:url(/test/data/partials/extra/down.gif) no-repeat}'
976
+ ]
977
+ }, {
978
+ root: process.cwd(),
979
+ target: path.join(process.cwd(), 'test.css'),
980
+ relativeTo: path.join('test', 'data', 'partials-relative')
981
+ }),
982
+ 'fonts': cssContext({
983
+ 'keep format quotation': "@font-face{font-family:PublicVintage;src:url(/PublicVintage.otf) format('opentype')}",
984
+ 'remove font family quotation': [
985
+ "a{font-family:\"Helvetica\",'Arial'}",
986
+ "a{font-family:Helvetica,Arial}"
987
+ ],
988
+ 'do not remove font family double quotation if space inside': 'a{font-family:"Courier New"}',
989
+ 'do not remove font quotation if starts with a number': 'a{font:\'123font\'}',
990
+ 'do not remove font family quotation if starts with a number': 'a{font-family:\'123font\'}',
991
+ 'remove font quotation': [
992
+ "a{font:12px/16px \"Helvetica\",'Arial'}",
993
+ "a{font:12px/16px Helvetica,Arial}"
994
+ ],
995
+ 'remove font quotation #2': [
996
+ "a{font:12px/16px \"Helvetica1_12\",'Arial_1451'}",
997
+ "a{font:12px/16px Helvetica1_12,Arial_1451}"
998
+ ],
999
+ 'remove font quotation #3': [
1000
+ "a{font:12px/16px \"Helvetica-Regular\",'Arial-Bold'}",
1001
+ "a{font:12px/16px Helvetica-Regular,Arial-Bold}"
1002
+ ],
1003
+ 'do not remove quotation from enclosed JSON (weird, I know)': "p{font-family:'{ \"current\" : \"large\", \"all\" : [\"small\", \"medium\", \"large\"], \"position\" : 2 }'}"
1004
+ }),
1005
+ 'IE hacks': cssContext({
1006
+ 'star': 'a{*color:#fff}',
1007
+ 'unserscore': 'a{_color:#fff}',
1008
+ 'backslash': 'a{color:#fff\\9}',
1009
+ 'overriding by a star': 'a{color:red;display:block;*color:#fff}',
1010
+ 'overriding by a unserscore': 'a{color:red;display:block;_color:#fff}',
1011
+ 'overriding by a backslash': 'a{color:red;display:block;color:#fff\\9}',
1012
+ 'overriding !important by a star': 'a{color:red!important;display:block;*color:#fff}',
1013
+ 'overriding !important by a unserscore': 'a{color:red!important;display:block;_color:#fff}',
1014
+ 'overriding !important by a backslash': [
1015
+ 'a{color:red!important;display:block;color:#fff\\9}',
1016
+ 'a{color:red!important;display:block}'
1017
+ ],
1018
+ 'overriding a star': [
1019
+ 'a{*color:red;display:block;*color:#fff}',
1020
+ 'a{display:block;*color:#fff}'
1021
+ ],
1022
+ 'overriding a unserscore': [
1023
+ 'a{_color:red;display:block;_color:#fff}',
1024
+ 'a{display:block;_color:#fff}'
1025
+ ],
1026
+ 'overriding a backslash': [
1027
+ 'a{color:red\\9;display:block;color:#fff\\9}',
1028
+ 'a{display:block;color:#fff\\9}'
1029
+ ],
1030
+ 'overriding a star by a non-ajacent selector': 'a{color:red}.one{display:block}a{*color:#fff}',
1031
+ 'overriding a unserscore by a non-ajacent selector': 'a{color:red}.one{display:block}a{_color:#fff}',
1032
+ 'overriding a backslash by a non-ajacent selector': 'a{color:red}.one{display:block}a{color:#fff\\9}',
1033
+ 'keeps rgba(0,0,0,0)': 'a{color:rgba(0,0,0,0)}',
1034
+ 'keeps rgba(255,255,255,0)': 'a{color:rgba(255,255,255,0)}',
1035
+ 'keeps hsla(120,100%,50%,0)': 'a{color:hsla(120,100%,50%,0)}'
1036
+ }, { compatibility: 'ie8' }),
1037
+ 'IE hacks without IE compatibility': cssContext({
1038
+ 'star': [
1039
+ 'a{*color:#fff}',
1040
+ ''
1041
+ ],
1042
+ 'unserscore': [
1043
+ 'a{_color:#fff}',
1044
+ ''
1045
+ ],
1046
+ 'two in a row': [
1047
+ 'a{padding:0;*height:13px;*width:13px}',
1048
+ 'a{padding:0}'
1049
+ ],
1050
+ 'two in a row mixed': [
1051
+ 'a{padding:0;*height:13px;_width:13px}',
1052
+ 'a{padding:0}'
1053
+ ],
1054
+ 'backslash': [
1055
+ 'a{color:#fff\\9}',
1056
+ ''
1057
+ ]
1058
+ }),
1059
+ 'animations': cssContext({
1060
+ 'shorten': [
1061
+ '@keyframes test\n{ from\n { width:100px; }\n to { width:200px; }\n}',
1062
+ '@keyframes test{from{width:100px}to{width:200px}}'
1063
+ ],
1064
+ 'remove name quotes': [
1065
+ "@keyframes \"test1\"{a{display:block}}@keyframes 'test2'{a{display:block}}",
1066
+ "@keyframes test1{a{display:block}}@keyframes test2{a{display:block}}"
1067
+ ],
1068
+ 'not remove name quotes if whitespace inside': "@keyframes \"test 1\"{a{display:block}}@keyframes 'test 2'{a{display:block}}",
1069
+ 'remove name quotes for vendor prefixes': [
1070
+ "@-moz-keyframes 'test'{a{display:block}}@-o-keyframes 'test'{a{display:block}}@-webkit-keyframes 'test'{a{display:block}}",
1071
+ "@-moz-keyframes test{a{display:block}}@-o-keyframes test{a{display:block}}@-webkit-keyframes test{a{display:block}}"
1072
+ ],
1073
+ 'remove quotes in animation': [
1074
+ "div{animation:'test' 2s ease-in .5s 3}",
1075
+ "div{animation:test 2s ease-in .5s 3}"
1076
+ ],
1077
+ 'not remove quotes in animation when name with space inside': "div{animation:'test 1' 2s ease-in .5s 3}",
1078
+ 'remove quotes in vendor prefixed animation': [
1079
+ "div{-moz-animation:'test' 2s ease-in;-o-animation:'test' 2s ease-in;-webkit-animation:'test' 2s ease-in}",
1080
+ "div{-moz-animation:test 2s ease-in;-o-animation:test 2s ease-in;-webkit-animation:test 2s ease-in}"
1081
+ ],
1082
+ 'remove quotes in animation-name': [
1083
+ "div{animation-name:'test'}",
1084
+ "div{animation-name:test}"
1085
+ ],
1086
+ 'not remove quotes in animation-name when name with space inside': "div{animation-name:'test 1'}",
1087
+ 'remove quotes in vendor prefixed animation-name': [
1088
+ "div{-moz-animation-name:'test';-o-animation-name:'test';-webkit-animation-name:'test'}",
1089
+ "div{-moz-animation-name:test;-o-animation-name:test;-webkit-animation-name:test}"
1090
+ ]
1091
+ }),
1092
+ 'attributes': cssContext({
1093
+ 'should keep selector if no value': 'div[data-type]{border-color:red}',
1094
+ 'should keep selector if no quotation': 'div[data-type=something]{border-color:red}',
1095
+ 'should keep selector if equals in value': 'div[data-type="stupid=value"]{border-color:red}',
1096
+ 'should keep quotation if whitespace inside': 'div[data-type^=\'object 1\']{border-color:red}',
1097
+ 'should keep quotations if special characters inside': 'a[data-type="object+1"]{color:red}p[data-target="#some-place"]{color:#0f0}',
1098
+ 'should keep quotation if is a number': 'div[data-number=\'1\']{border-color:red}',
1099
+ 'should keep quotation if starts with a number': 'div[data-type^=\'1something\']{border-color:red}',
1100
+ 'should keep quotation if starts with a hyphen': 'div[data-type$=\'-something\']{border-color:red}',
1101
+ 'should keep quotation if key only (which is invalid)': 'div["data-type"]{color:red}',
1102
+ 'should strip quotation if is a word': [
1103
+ 'a[data-href=\'object\']{border-color:red}',
1104
+ 'a[data-href=object]{border-color:red}'
1105
+ ],
1106
+ 'should strip quotation if is a hyphen separated words': [
1107
+ 'a[data-href=\'object-1-two\']{border-color:red}',
1108
+ 'a[data-href=object-1-two]{border-color:red}'
1109
+ ],
1110
+ 'should strip quotations if is less specific selectors': [
1111
+ 'a[data-href*=\'object1\']{border-color:red}a[data-href|=\'object2\']{border-color:#0f0}',
1112
+ 'a[data-href*=object1]{border-color:red}a[data-href|=object2]{border-color:#0f0}'
1113
+ ],
1114
+ 'should keep special characters inside attributes #1': "a[data-css='color:white']{display:block}",
1115
+ 'should keep special characters inside attributes #2': 'a[href="/version-0.01.html"]{display:block}',
1116
+ 'should strip new lines inside attributes': [
1117
+ ".test[title='my very long \
1118
+ title']{display:block}",
1119
+ ".test[title='my very long title']{display:block}"
1120
+ ],
1121
+ 'should strip new lines inside attributes which can be unquoted': [
1122
+ ".test[title='my_very_long_\
1123
+ title']{display:block}",
1124
+ ".test[title=my_very_long_title]{display:block}"
1125
+ ],
1126
+ 'should strip whitespace between square brackets': [
1127
+ 'body[ data-title ]{color:red}',
1128
+ 'body[data-title]{color:red}'
1129
+ ],
1130
+ 'should strip whitespace inside square brackets': [
1131
+ 'body[ data-title = x ]{color:red}',
1132
+ 'body[data-title=x]{color:red}'
1133
+ ]
1134
+ }),
1135
+ 'ie filters': cssContext({
1136
+ 'short alpha': [
1137
+ "a{ filter:progid:DXImageTransform.Microsoft.Alpha(Opacity=80); -ms-filter:'progid:DXImageTransform.Microsoft.Alpha(Opacity=50)';}",
1138
+ "a{filter:alpha(Opacity=80);-ms-filter:'alpha(Opacity=50)'}"
1139
+ ],
1140
+ 'short chroma': [
1141
+ 'a{filter:progid:DXImageTransform.Microsoft.Chroma(color=#919191)}',
1142
+ 'a{filter:chroma(color=#919191)}'
1143
+ ],
1144
+ 'matrix filter spaces': [
1145
+ "a{filter:progid:DXImageTransform.Microsoft.Matrix(M11=0.984, M22=0.984, M12=0.17, M21=-0.17, SizingMethod='auto expand')}",
1146
+ "a{filter:progid:DXImageTransform.Microsoft.Matrix(M11=.984, M22=.984, M12=.17, M21=-.17, SizingMethod='auto expand')}"
1147
+ ],
1148
+ 'multiple filters (IE7 issue)': [
1149
+ "a{filter:progid:DXImageTransform.Microsoft.Chroma(color=#919191) progid:DXImageTransform.Microsoft.Matrix(M11=0.984, M22=0.984, M12=0.17, M21=-0.17, SizingMethod='auto expand')}",
1150
+ "a{filter:progid:DXImageTransform.Microsoft.Chroma(color=#919191) progid:DXImageTransform.Microsoft.Matrix(M11=.984, M22=.984, M12=.17, M21=-.17, SizingMethod='auto expand')}"
1151
+ ]
1152
+ }),
1153
+ 'charsets': cssContext({
1154
+ 'not at beginning': [
1155
+ "a{ color: #f10; }@charset 'utf-8';b { font-weight: bolder}",
1156
+ "@charset 'utf-8';a{color:#f10}b{font-weight:bolder}"
1157
+ ],
1158
+ 'multiple charsets': [
1159
+ "@charset 'utf-8';div :before { display: block }@charset 'utf-8';a { color: #f10 }",
1160
+ "@charset 'utf-8';div :before{display:block}a{color:#f10}"
1161
+ ],
1162
+ 'charset and space after': [
1163
+ "@charset 'utf-8';" + lineBreak + lineBreak + "a{display:block}",
1164
+ "@charset 'utf-8';a{display:block}"
1165
+ ]
1166
+ }),
1167
+ 'important': cssContext({
1168
+ 'space before': [
1169
+ "body{background-color:#fff !important}",
1170
+ "body{background-color:#fff!important}"
1171
+ ],
1172
+ 'space between ! and important': [
1173
+ "body{background-color:#fff ! important}",
1174
+ "body{background-color:#fff!important}"
1175
+ ]
1176
+ }),
1177
+ 'empty elements': cssContext({
1178
+ 'single': [
1179
+ ' div p { \n}',
1180
+ ''
1181
+ ],
1182
+ 'between non-empty': [
1183
+ 'div {color:#fff} a{ } p{ line-height:1.35em}',
1184
+ 'div{color:#fff}p{line-height:1.35em}'
1185
+ ],
1186
+ 'just a semicolon': [
1187
+ 'div { ; }',
1188
+ ''
1189
+ ],
1190
+ 'inside @media': [
1191
+ "@media screen { .test {} } .test1 { color: green; }",
1192
+ ".test1{color:green}"
1193
+ ],
1194
+ 'inside nested @media': [
1195
+ '@media screen { @media (orientation:landscape) { @media (max-width:999px) { .test {} } } }',
1196
+ ''
1197
+ ],
1198
+ 'inside not empty @media': [
1199
+ "@media screen { .test {} .some { display:none } }",
1200
+ "@media screen{.some{display:none}}"
1201
+ ],
1202
+ 'inside nested not empty @media': [
1203
+ '@media screen { @media (orientation:landscape) { @media (max-width:999px) { .test {} } a {color:red} } }',
1204
+ '@media screen{@media (orientation:landscape){a{color:red}}}'
1205
+ ]
1206
+ }),
1207
+ 'empty @media': cssContext({
1208
+ 'simple': [
1209
+ '@media print{}',
1210
+ ''
1211
+ ],
1212
+ 'simple with and': [
1213
+ '@media print and screen{}',
1214
+ ''
1215
+ ],
1216
+ 'complex': [
1217
+ '@media print, (-o-min-device-pixel-ratio: 5/4), (-webkit-min-device-pixel-ratio: 1.25), (min-resolution: 120dpi) {\n}',
1218
+ ''
1219
+ ]
1220
+ }),
1221
+ 'empty with disabled advanced optimizations': cssContext({
1222
+ 'selector': [
1223
+ 'a{}p{}',
1224
+ ''
1225
+ ],
1226
+ 'media': [
1227
+ '@media screen{}',
1228
+ ''
1229
+ ]
1230
+ }, { noAdvanced: true }),
1231
+ '@import': cssContext({
1232
+ 'empty': [
1233
+ "@import url();",
1234
+ ''
1235
+ ],
1236
+ 'of an unknown file': [
1237
+ "@import url('fake.css');",
1238
+ ''
1239
+ ],
1240
+ 'of an unknown file with a missing trailing semicolon': [
1241
+ "@import url(fake.css)",
1242
+ ''
1243
+ ],
1244
+ 'of a directory': [
1245
+ "@import url(test/data/partials);",
1246
+ ''
1247
+ ],
1248
+ 'of a real file': [
1249
+ "@import url(test/data/partials/one.css);",
1250
+ ".one{color:red}"
1251
+ ],
1252
+ 'of a real file twice': [
1253
+ "@import url(test/data/partials/one.css);@import url(test/data/partials/one.css);",
1254
+ ".one{color:red}"
1255
+ ],
1256
+ 'of a real file with current path prefix': [
1257
+ "@import url(./test/data/partials/one.css);",
1258
+ ".one{color:red}"
1259
+ ],
1260
+ 'of a real file with quoted path': [
1261
+ "@import url('test/data/partials/one.css');",
1262
+ ".one{color:red}"
1263
+ ],
1264
+ 'of a real file with double-quoted path': [
1265
+ '@import url("test/data/partials/one.css");',
1266
+ ".one{color:red}"
1267
+ ],
1268
+ 'of a real file with bare path': [
1269
+ "@import test/data/partials/one.css;",
1270
+ ".one{color:red}"
1271
+ ],
1272
+ 'of a real file with bare quoted path': [
1273
+ "@import 'test/data/partials/one.css';",
1274
+ ".one{color:red}"
1275
+ ],
1276
+ 'of a real file with bare double-quoted path': [
1277
+ '@import "test/data/partials/one.css";',
1278
+ ".one{color:red}"
1279
+ ],
1280
+ 'of a real file with single simple media': [
1281
+ '@import url(test/data/partials/one.css) screen;',
1282
+ "@media screen{.one{color:red}}"
1283
+ ],
1284
+ 'of a real file with multiple simple media': [
1285
+ '@import "test/data/partials/one.css" screen, tv, print;',
1286
+ "@media screen,tv,print{.one{color:red}}"
1287
+ ],
1288
+ 'of a real file with complex media': [
1289
+ '@import \'test/data/partials/one.css\' screen and (orientation:landscape);',
1290
+ "@media screen and (orientation:landscape){.one{color:red}}"
1291
+ ],
1292
+ 'of a real file with a missing trailing semicolon': [
1293
+ "@import url(test/data/partials/one.css)",
1294
+ ''
1295
+ ],
1296
+ 'of a real files with a missing trailing semicolon': [
1297
+ "@import url(test/data/partials/one.css)@import url(test/data/partials/two.css)",
1298
+ ''
1299
+ ],
1300
+ 'of more files': [
1301
+ "@import url(test/data/partials/one.css);\n\n@import url(test/data/partials/extra/three.css);\n\na{display:block}",
1302
+ ".one{color:red}.three{color:#0f0}a{display:block}"
1303
+ ],
1304
+ 'of more files with media': [
1305
+ "@import url(test/data/partials/one.css) screen;@import url(test/data/partials/extra/three.css) tv;",
1306
+ "@media screen{.one{color:red}}@media tv{.three{color:#0f0}}"
1307
+ ],
1308
+ 'of multi-level, circular dependency file': [
1309
+ "@import url(test/data/partials/two.css);",
1310
+ ".one{color:red}.three{color:#0f0}.four{color:#00f}.two{color:#fff}"
1311
+ ],
1312
+ 'of a file with a relative resource path': [
1313
+ "@import url(test/data/partials/three.css);",
1314
+ ".three{background-image:url(test/data/partials/extra/down.gif)}"
1315
+ ],
1316
+ 'of a file with an absolute resource path': [
1317
+ "@import url(test/data/partials/four.css);",
1318
+ ".four{background-image:url(/partials/extra/down.gif)}"
1319
+ ],
1320
+ 'of a file with a resource URI': [
1321
+ "@import url(test/data/partials/five.css);",
1322
+ ".five{background:url(data:image/jpeg;base64,/9j/)}"
1323
+ ],
1324
+ 'inside a comment': [
1325
+ '/* @import url(test/data/partials/five.css); */a { color: red; }',
1326
+ 'a{color:red}'
1327
+ ],
1328
+ 'after a comment': [
1329
+ '/* @import url(test/data/partials/one.css); */@import url(test/data/partials/one.css);a { color: red; }',
1330
+ '.one,a{color:red}'
1331
+ ],
1332
+ 'used arbitrarily in comment': [
1333
+ '/* @import foo */a { color: red; }',
1334
+ 'a{color:red}'
1335
+ ],
1336
+ 'used arbitrarily in comment multiple times': [
1337
+ '/* @import foo */a { color: red; }\n/* @import bar */p { color: #fff; }',
1338
+ 'a{color:red}p{color:#fff}'
1339
+ ],
1340
+ 'used arbitrarily in comment including unrelated comment': [
1341
+ '/* foo */a { color: red; }/* bar *//* @import */',
1342
+ 'a{color:red}'
1343
+ ],
1344
+ 'of a file with a comment': [
1345
+ '@import url(test/data/partials/comment.css);',
1346
+ 'a{display:block}'
1347
+ ],
1348
+ 'of a file (with media) with a comment': [
1349
+ '@import url(test/data/partials/comment.css) screen and (device-height: 600px);',
1350
+ '@media screen and (device-height:600px){a{display:block}}'
1351
+ ],
1352
+ 'after standard content': [
1353
+ "a{display:block}@import url(test/data/partials/one.css);body{margin:0}",
1354
+ "a{display:block}body{margin:0}"
1355
+ ],
1356
+ 'after quoted content': [
1357
+ "/*a{display:block}*/@import url(test/data/partials/one.css);",
1358
+ ".one{color:red}"
1359
+ ]
1360
+ }, { root: process.cwd() }),
1361
+ '@import with absolute paths': cssContext({
1362
+ 'of an unknown file': [
1363
+ "@import url(/fake.css);",
1364
+ ''
1365
+ ],
1366
+ 'of a real file': [
1367
+ "@import url(/partials/one.css);",
1368
+ ".one{color:red}"
1369
+ ],
1370
+ 'of a real file with quoted paths': [
1371
+ "@import url(\"/partials/one.css\");",
1372
+ ".one{color:red}"
1373
+ ],
1374
+ 'of two files with mixed paths': [
1375
+ "@import url(/partials/one.css);@import url(partials/extra/three.css);a{display:block}",
1376
+ ".one{color:red}.three{color:#0f0}a{display:block}"
1377
+ ],
1378
+ 'of a multi-level, circular dependency file': [
1379
+ "@import url(/partials/two.css);",
1380
+ ".one{color:red}.three{color:#0f0}.four{color:#00f}.two{color:#fff}"
1381
+ ],
1382
+ 'of a multi-level, circular dependency file with mixed paths': [
1383
+ "@import url(/partials-absolute/base.css);",
1384
+ ".base2{border-width:0}.sub{padding:0}.base{margin:0}"
1385
+ ]
1386
+ }, { root: path.join(process.cwd(), 'test', 'data') }),
1387
+ '@import with option processImport': cssContext({
1388
+ 'of an unknown file': [
1389
+ "@import url(/fake.css);",
1390
+ "@import url(/fake.css);"
1391
+ ],
1392
+ 'of an unknown file with extra whitespace': [
1393
+ "@import url( /fake.css );",
1394
+ "@import url(/fake.css);"
1395
+ ],
1396
+ 'of comment chars within import url': "@import 'necolas/normalize.css@*/normalize.css';"
1397
+ }, { processImport: false }),
1398
+ '@import with no import and no advanced': cssContext({
1399
+ 'empty body': [
1400
+ '@import url(//fonts.googleapis.com/css?family=Domine:700);body{/* comment */}body h1{font-family:Domine}',
1401
+ '@import url(//fonts.googleapis.com/css?family=Domine:700);body h1{font-family:Domine}'
1402
+ ],
1403
+ 'no empty body': '@import url(//fonts.googleapis.com/css?family=Domine:700);body{color:red}body h1{font-family:Domine}'
1404
+ }, { processImport: false, noAdvanced: true }),
1405
+ 'duplicate selectors with disabled advanced processing': cssContext({
1406
+ 'of a duplicate selector': 'a,a{color:red}'
1407
+ }, { noAdvanced: true }),
1408
+ 'line breaks with disabled advanced processing': cssContext({
1409
+ 'should be applied': [
1410
+ 'a{color:red}p{display:block}',
1411
+ 'a{color:red}' + lineBreak + 'p{display:block}'
1412
+ ]
1413
+ }, { noAdvanced: true, keepBreaks: true }),
1414
+ 'invalid data tokenization': cssContext({
1415
+ 'extra top-level closing brace': [
1416
+ 'a{color:red}}p{width:auto}',
1417
+ 'a{color:red}p{width:auto}'
1418
+ ],
1419
+ 'extra top-level closing braces': [
1420
+ 'a{color:red}}}}p{width:auto}',
1421
+ 'a{color:red}p{width:auto}'
1422
+ ]
1423
+ }),
1424
+ 'duplicate selectors in a list': cssContext({
1425
+ 'of a duplicate selector': [
1426
+ 'a,a{color:red}',
1427
+ 'a{color:red}'
1428
+ ],
1429
+ 'of an unordered multiply repeated selector': [
1430
+ 'a,b,p,a{color:red}',
1431
+ 'a,b,p{color:red}'
1432
+ ],
1433
+ 'of an unordered multiply repeated selector within a block': [
1434
+ '@media screen{a,b,p,a{color:red}}',
1435
+ '@media screen{a,b,p{color:red}}'
1436
+ ],
1437
+ 'of an unordered multiply repeated complex selector within a block #1': [
1438
+ '@media screen{.link[data-path],a,p,.link[data-path]{color:red}}',
1439
+ '@media screen{.link[data-path],a,p{color:red}}'
1440
+ ],
1441
+ 'of an unordered multiply repeated complex selector within a block #2': [
1442
+ '@media screen{#foo[data-path^="bar bar"],a,p,#foo[data-path^="bar bar"]{color:red}}',
1443
+ '@media screen{#foo[data-path^="bar bar"],a,p{color:red}}'
1444
+ ]
1445
+ }),
1446
+ 'duplicate selectors in a scope': cssContext({
1447
+ 'of two successive selectors': [
1448
+ 'a{color:red}a{color:red}',
1449
+ 'a{color:red}'
1450
+ ],
1451
+ 'of two successive selectors with different body': [
1452
+ 'a{color:red}a{display:block}',
1453
+ 'a{color:red;display:block}'
1454
+ ],
1455
+ 'of many successive selectors': [
1456
+ 'a{color:red}a{color:red}a{color:red}a{color:red}',
1457
+ 'a{color:red}'
1458
+ ],
1459
+ 'of two non-successive selectors': [
1460
+ 'a{color:red}p{color:#fff}a{color:red}',
1461
+ 'p{color:#fff}a{color:red}'
1462
+ ],
1463
+ 'of many non-successive selectors': [
1464
+ 'div{width:100%}a{color:red}a{color:red}p{color:#fff}div{width:100%}ol{margin:0}p{color:#fff}',
1465
+ 'a{color:red}div{width:100%}ol{margin:0}p{color:#fff}'
1466
+ ],
1467
+ 'with global and media scope': [
1468
+ 'a{color:red}@media screen{a{color:red}p{width:100px}a{color:red}}',
1469
+ 'a{color:red}@media screen{p{width:100px}a{color:red}}'
1470
+ ],
1471
+ 'with two media scopes': [
1472
+ '@media (min-width:100px){a{color:red}}@media screen{a{color:red}p{width:100px}a{color:red}}',
1473
+ '@media (min-width:100px){a{color:red}}@media screen{p{width:100px}a{color:red}}'
1474
+ ]
1475
+ }),
1476
+ 'duplicate properties': cssContext({
1477
+ 'of two properties one after another': 'a{display:-moz-inline-box;display:inline-block}',
1478
+ 'of two properties in one declaration': [
1479
+ 'a{display:inline-block;color:red;display:block}',
1480
+ 'a{color:red;display:block}'
1481
+ ],
1482
+ 'of two properties in one declaration with former as !important': [
1483
+ 'a{display:inline-block!important;color:red;display:block}',
1484
+ 'a{display:inline-block!important;color:red}'
1485
+ ],
1486
+ 'of two properties in one declaration with latter as !important': [
1487
+ 'a{display:inline-block;color:red;display:block!important}',
1488
+ 'a{color:red;display:block!important}'
1489
+ ],
1490
+ 'of two properties in one declaration with both as !important': [
1491
+ 'a{display:inline-block!important;color:red;display:block!important}',
1492
+ 'a{color:red;display:block!important}'
1493
+ ],
1494
+ 'of many properties in one declaration': [
1495
+ 'a{display:inline-block;color:red;font-weight:bolder;font-weight:700;display:block!important;color:#fff}',
1496
+ 'a{font-weight:bolder;font-weight:700;display:block!important;color:#fff}'
1497
+ ],
1498
+ 'both redefined and overridden': [
1499
+ 'p{display:block;display:-moz-inline-box;color:red;display:table-cell}',
1500
+ 'p{color:red;display:table-cell}'
1501
+ ],
1502
+ 'background redefined with merging': [
1503
+ '.one{display:block}.one{background:#fff;background:-webkit-gradient();background:-moz-linear-gradient();filter:progid:DXImageTransform}',
1504
+ '.one{display:block;background:#fff;background:-webkit-gradient();background:-moz-linear-gradient();filter:progid:DXImageTransform}'
1505
+ ],
1506
+ 'filter treated as background': 'p{background:-moz-linear-gradient();background:-webkit-linear-gradient();filter:"progid:DXImageTransform";background:linear-gradient()}',
1507
+ 'filter treated as background-image': 'p{background-image:-moz-linear-gradient();background-image:-webkit-linear-gradient();filter:"progid:DXImageTransform";background-image:linear-gradient()}',
1508
+ '-ms-filter treated as background': 'p{background:-moz-linear-gradient();background:-webkit-linear-gradient();-ms-filter:"progid:DXImageTransform";background:linear-gradient()}',
1509
+ '-ms-filter treated as background-image': 'p{background-image:-moz-linear-gradient();background-image:-webkit-linear-gradient();-ms-filter:"progid:DXImageTransform";background-image:linear-gradient()}',
1510
+ '-ms-transform with different values #1': 'div{-ms-transform:translate(0,0);-ms-transform:translate3d(0,0,0)}',
1511
+ '-ms-transform with different values #2': 'div{-ms-transform:translate(0,0);-webkit-transform:translate3d(0,0,0);-ms-transform:translate3d(0,0,0)}',
1512
+ 'transform with different values #1': 'div{transform:translate(0,0);transform:translate3d(0,0,0)}',
1513
+ 'transform with different values #2': 'div{transform:translate(0,0);-webkit-transform:translate3d(0,0,0);transform:translate3d(0,0,0)}',
1514
+ 'border(hex) with border(rgba)': 'a{border:1px solid #fff;display:none;border:1px solid rgba(1,0,0,.5)}',
1515
+ 'border(hex !important) with border(hex)': [
1516
+ 'a{border:1px solid #fff!important;display:none;border:1px solid #fff}',
1517
+ 'a{border:1px solid #fff!important;display:none}'
1518
+ ],
1519
+ 'border(hex) with border(hex !important)': [
1520
+ 'a{border:1px solid #fff;display:none;border:1px solid #fff!important}',
1521
+ 'a{display:none;border:1px solid #fff!important}'
1522
+ ]
1523
+ }),
1524
+ 'duplicate properties with aggressive merging disabled': cssContext({
1525
+ 'of (yet) unmergeable properties': 'a{display:inline-block;color:red;display:-moz-block}',
1526
+ 'of mergeable properties': [
1527
+ 'a{background:red;display:block;background:white}',
1528
+ 'a{display:block;background:#fff}'
1529
+ ]
1530
+ }, { noAggressiveMerging: true }),
1531
+ 'same selectors': cssContext({
1532
+ 'of two non-adjacent selectors': '.one{color:red}.two{color:#00f}.one{font-weight:700}',
1533
+ 'of two adjacent single selectors': [
1534
+ '.one{color:red}.one{font-weight:700}',
1535
+ '.one{color:red;font-weight:700}'
1536
+ ],
1537
+ 'of three adjacent single selectors': [
1538
+ '.one{color:red}.one{font-weight:700}.one{font-size:12px}',
1539
+ '.one{color:red;font-weight:700;font-size:12px}'
1540
+ ],
1541
+ 'of two adjacent single, complex selectors': [
1542
+ '#box>.one{color:red}#box>.one{font-weight:700}',
1543
+ '#box>.one{color:red;font-weight:700}'
1544
+ ],
1545
+ 'of two adjacent multiple, complex selectors': [
1546
+ '#box>.one,.zero{color:red}#box>.one,.zero{font-weight:700}',
1547
+ '#box>.one,.zero{color:red;font-weight:700}'
1548
+ ],
1549
+ 'of two adjacent selectors with duplicate properties #1': [
1550
+ '.one{color:red}.one{color:#fff}',
1551
+ '.one{color:#fff}'
1552
+ ],
1553
+ 'of two adjacent selectors with duplicate properties #2': [
1554
+ '.one{color:red;font-weight:bold}.one{color:#fff;font-weight:400}',
1555
+ '.one{color:#fff;font-weight:400}'
1556
+ ],
1557
+ 'of two adjacent complex selectors with different selector order': [
1558
+ '.one,.two{color:red}.two,.one{line-height:1em}',
1559
+ '.one,.two{color:red;line-height:1em}'
1560
+ ],
1561
+ 'two adjacent with hex color definitions': [
1562
+ 'a:link,a:visited{color:#fff}.one{display:block}a:link,a:visited{color:red}',
1563
+ '.one{display:block}a:link,a:visited{color:red}'
1564
+ ],
1565
+ 'in two passes': [
1566
+ 'a{color:red}a{background:red}b{color:red}b{background:red}',
1567
+ 'a,b{color:red;background:red}'
1568
+ ],
1569
+ 'when overriden with a browser specific selector': 'a{color:red}::-webkit-scrollbar,a{color:#fff}'
1570
+ }),
1571
+ 'same non-adjacent selectors': cssContext({
1572
+ 'with different properties': 'a{color:red;display:block}.one{font-size:12px}a{margin:2px}',
1573
+ 'with one redefined property': [
1574
+ 'a{color:red;display:block}.one{font-size:12px}a{color:#fff;margin:2px}',
1575
+ 'a{display:block}.one{font-size:12px}a{color:#fff;margin:2px}'
1576
+ ],
1577
+ 'with intentionally redefined properties on joins': [
1578
+ 'a{display:inline-block;display:-moz-inline-box;color:red}.one{font-size:12px}a{color:#fff;margin:2px}',
1579
+ 'a{display:inline-block;display:-moz-inline-box}.one{font-size:12px}a{color:#fff;margin:2px}'
1580
+ ],
1581
+ 'with intentionally redefined properties on nultiple joins': [
1582
+ 'a{color:red}.one{font-size:12px}a{color:#fff;margin:2px}.two{font-weight:400}a{margin:0}',
1583
+ '.one{font-size:12px}a{color:#fff}.two{font-weight:400}a{margin:0}'
1584
+ ],
1585
+ 'with all redefined properties': [
1586
+ 'a{color:red;display:block}.one{font-size:12px}a{color:#fff;display:inline-block;margin:2px}',
1587
+ '.one{font-size:12px}a{color:#fff;display:inline-block;margin:2px}'
1588
+ ],
1589
+ 'many with all redefined properties': [
1590
+ 'a{padding:10px}.zero{color:transparent}a{color:red;display:block}.one{font-size:12px}a{color:#fff;display:inline-block;margin:2px}',
1591
+ 'a{padding:10px}.zero{color:transparent}.one{font-size:12px}a{color:#fff;display:inline-block;margin:2px}'
1592
+ ],
1593
+ 'when overriden by an empty selector': [
1594
+ 'a{padding:10px}.one{color:red}a{}',
1595
+ 'a{padding:10px}.one{color:red}'
1596
+ ],
1597
+ 'when overriden by a complex selector': [
1598
+ 'a{padding:10px;margin:0;color:red}.one{color:red}a,p{color:red;padding:0}',
1599
+ 'a{margin:0}.one{color:red}a,p{color:red;padding:0}'
1600
+ ],
1601
+ 'when overriden by complex selectors': [
1602
+ 'a{padding:10px;margin:0;color:red}.one{color:red}a,p{color:red;padding:0}.one,a{color:#fff}',
1603
+ 'a{margin:0}a,p{color:red;padding:0}.one,a{color:#fff}'
1604
+ ],
1605
+ 'when complex selector overriden by simple selectors': 'a,p{margin:0;color:red}a{color:#fff}',
1606
+ // Pending re-run selectors merge - see #160
1607
+ 'when complex selector overriden by complex and simple selectors': [
1608
+ 'a,p{margin:0;color:red}a{color:#fff}a,p{color:#00f}p{color:#0f0}',
1609
+ 'a,p{margin:0}a,p{color:#00f}p{color:#0f0}'
1610
+ ],
1611
+ 'when complex selector overriden by complex selectors': [
1612
+ '.one>.two,.three{color:red;line-height:1rem}#zero,.one>.two,.three,.www{color:#fff;margin:0}a{color:red}.one>.two,.three{line-height:2rem;font-size:1.5rem}',
1613
+ '#zero,.one>.two,.three,.www{color:#fff;margin:0}a{color:red}.one>.two,.three{line-height:2rem;font-size:1.5rem}'
1614
+ ],
1615
+ 'when undefined is used as a value': '.one{text-shadow:undefined}p{color:red}.one{font-size:12px}',
1616
+ 'when undefined is used as a value with reduction': [
1617
+ '.one{text-shadow:undefined}p{color:red}.one{font-size:12px;text-shadow:none}',
1618
+ 'p{color:red}.one{font-size:12px;text-shadow:none}'
1619
+ ],
1620
+ 'when overriden with a browser specific selector': 'a{color:red}p{display:block}::-moz-selection,a{color:#fff}',
1621
+ 'when same browser specific selector more than once': [
1622
+ 'a,::-moz-selection{color:red}p{display:block}a,::-moz-selection{color:#fff}',
1623
+ 'p{display:block}::-moz-selection,a{color:#fff}'
1624
+ ],
1625
+ 'with full property comparison': '.one{height:7rem}.two{height:auto}.one{line-height:7rem;color:red}'
1626
+ }),
1627
+ 'rerun optimizers': cssContext({
1628
+ 'selectors reducible once': [
1629
+ '.one{color:red;margin:0}.two{color:red}.one{margin:0}',
1630
+ '.one,.two{color:red}.one{margin:0}'
1631
+ ]
1632
+ }),
1633
+ 'same bodies': cssContext({
1634
+ 'of two non-adjacent selectors': '.one{color:red}.two{color:#00f}.three{color:red}',
1635
+ 'of two adjacent single selectors': [
1636
+ '.one{color:red}.two{color:red}',
1637
+ '.one,.two{color:red}'
1638
+ ],
1639
+ 'of three adjacent complex, multiple selectors': [
1640
+ '.one{color:red}#two.three{color:red}.four>.five{color:red}',
1641
+ '#two.three,.four>.five,.one{color:red}'
1642
+ ],
1643
+ 'with repeated selectors': [
1644
+ '#zero>p,.one,.two{color:red}.two,#zero>p,.three{color:red}',
1645
+ '#zero>p,.one,.three,.two{color:red}'
1646
+ ]
1647
+ }),
1648
+ 'same bodies - IE8 compat': cssContext({
1649
+ 'of two supported selectors': [
1650
+ '.one:first-child{color:red}.two>.three{color:red}',
1651
+ '.one:first-child,.two>.three{color:red}'
1652
+ ],
1653
+ 'of supported and unsupported selector': '.one:first-child{color:red}.two:last-child{color:red}',
1654
+ 'of two unsupported selectors': '.one:nth-child(5){color:red}.two:last-child{color:red}'
1655
+ }, { compatibility: 'ie8' }),
1656
+ 'same bodies - IE7 compat': cssContext({
1657
+ 'of two supported selectors': [
1658
+ '.one{color:red}.two>.three{color:red}',
1659
+ '.one,.two>.three{color:red}'
1660
+ ],
1661
+ 'of supported and unsupported selector': '.one{color:red}.two:last-child{color:red}',
1662
+ 'of two unsupported selectors': '.one:before{color:red}.two:last-child{color:red}'
1663
+ }, { compatibility: 'ie7' }),
1664
+ 'redefined more granular properties': redefineContext({
1665
+ 'animation-delay': ['animation'],
1666
+ 'animation-direction': ['animation'],
1667
+ 'animation-duration': ['animation'],
1668
+ 'animation-fill-mode': ['animation'],
1669
+ 'animation-iteration-count': ['animation'],
1670
+ 'animation-name': ['animation'],
1671
+ 'animation-play-state': ['animation'],
1672
+ 'animation-timing-function': ['animation'],
1673
+ 'background-attachment': ['background'],
1674
+ 'background-clip': ['background'],
1675
+ 'background-color': ['background'],
1676
+ 'background-image': ['background'],
1677
+ 'background-origin': ['background'],
1678
+ 'background-position': ['background'],
1679
+ 'background-repeat': ['background'],
1680
+ 'background-size': ['background'],
1681
+ 'border-color': ['border'],
1682
+ 'border-style': ['border'],
1683
+ 'border-width': ['border'],
1684
+ 'border-bottom': ['border'],
1685
+ 'border-bottom-color': ['border-bottom', 'border-color', 'border'],
1686
+ 'border-bottom-style': ['border-bottom', 'border-style', 'border'],
1687
+ 'border-bottom-width': ['border-bottom', 'border-width', 'border'],
1688
+ 'border-left': ['border'],
1689
+ 'border-left-color': ['border-left', 'border-color', 'border'],
1690
+ 'border-left-style': ['border-left', 'border-style', 'border'],
1691
+ 'border-left-width': ['border-left', 'border-width', 'border'],
1692
+ 'border-right': ['border'],
1693
+ 'border-right-color': ['border-right', 'border-color', 'border'],
1694
+ 'border-right-style': ['border-right', 'border-style', 'border'],
1695
+ 'border-right-width': ['border-right', 'border-width', 'border'],
1696
+ 'border-top': ['border'],
1697
+ 'border-top-color': ['border-top', 'border-color', 'border'],
1698
+ 'border-top-style': ['border-top', 'border-style', 'border'],
1699
+ 'border-top-width': ['border-top', 'border-width', 'border'],
1700
+ 'font-family': ['font'],
1701
+ 'font-size': ['font'],
1702
+ 'font-style': ['font'],
1703
+ 'font-variant': ['font'],
1704
+ 'font-weight': ['font'],
1705
+ 'list-style-image': ['list-style'],
1706
+ 'list-style-position': ['list-style'],
1707
+ 'list-style-type': ['list-style'],
1708
+ 'margin-bottom': ['margin'],
1709
+ 'margin-left': ['margin'],
1710
+ 'margin-right': ['margin'],
1711
+ 'margin-top': ['margin'],
1712
+ 'outline-color': ['outline'],
1713
+ 'outline-style': ['outline'],
1714
+ 'outline-width': ['outline'],
1715
+ 'padding-bottom': ['padding'],
1716
+ 'padding-left': ['padding'],
1717
+ 'padding-right': ['padding'],
1718
+ 'padding-top': ['padding'],
1719
+ 'transition-delay': ['transition'],
1720
+ 'transition-duration': ['transition'],
1721
+ 'transition-property': ['transition'],
1722
+ 'transition-timing-function': ['transition']
1723
+ }, { vendorPrefixes: ['animation', 'transition'], noneFor: ['list-style-image'] }),
1724
+ 'redefined more granular properties with property merging': cssContext({
1725
+ 'should merge background with background-attachment': [
1726
+ 'a{background:0;background-attachment:fixed}',
1727
+ 'a{background:0 fixed}'
1728
+ ],
1729
+ 'should NOT merge background with inherited background-attachment': [
1730
+ 'a{background:0;background-attachment:inherit}',
1731
+ 'a{background:0;background-attachment:inherit}'
1732
+ ],
1733
+ 'should merge background with background-color': [
1734
+ 'a{background:0;background-color:#9fce00}',
1735
+ 'a{background:0 #9fce00}'
1736
+ ],
1737
+ 'should NOT merge background with inherited background-color': [
1738
+ 'a{background:0;background-color:inherit}',
1739
+ 'a{background:0;background-color:inherit}'
1740
+ ],
1741
+ 'should merge background with background-image': [
1742
+ 'a{background:0;background-image:url(hello_world)}',
1743
+ 'a{background:url(hello_world) 0}'
1744
+ ],
1745
+ 'should NOT merge background with inherited background-image': [
1746
+ 'a{background:0;background-image:inherit}',
1747
+ 'a{background:0;background-image:inherit}'
1748
+ ],
1749
+ 'should merge background with background-position': [
1750
+ 'a{background:0;background-position:3px 4px}',
1751
+ 'a{background:3px 4px}'
1752
+ ],
1753
+ 'should NOT merge background with inherited background-position': [
1754
+ 'a{background:0;background-position:inherit}',
1755
+ 'a{background:0;background-position:inherit}'
1756
+ ],
1757
+ 'should merge background with background-repeat': [
1758
+ 'a{background:0;background-repeat:repeat-y}',
1759
+ 'a{background:0 repeat-y}'
1760
+ ],
1761
+ 'should NOT merge background with inherited background-repeat': [
1762
+ 'a{background:0;background-repeat:inherit}',
1763
+ 'a{background:0;background-repeat:inherit}'
1764
+ ],
1765
+ 'should merge outline with outline-color': [
1766
+ 'a{outline:1px;outline-color:#9fce00}',
1767
+ 'a{outline:#9fce00 1px}'
1768
+ ],
1769
+ 'should NOT merge outline with inherited outline-color': [
1770
+ 'a{outline:0;outline-color:inherit}',
1771
+ 'a{outline:0;outline-color:inherit}'
1772
+ ],
1773
+ 'should merge outline with outline-style': [
1774
+ 'a{outline:0;outline-style:dashed}',
1775
+ 'a{outline:dashed 0}'
1776
+ ],
1777
+ 'should NOT merge outline with inherited outline-style': [
1778
+ 'a{outline:0;outline-style:inherit}',
1779
+ 'a{outline:0;outline-style:inherit}'
1780
+ ],
1781
+ 'should merge outline with outline-width': [
1782
+ 'a{outline:0;outline-width:5px}',
1783
+ 'a{outline:5px}'
1784
+ ],
1785
+ 'should NOT merge outline with inherited outline-width': [
1786
+ 'a{outline:0;outline-width:inherit}',
1787
+ 'a{outline:0;outline-width:inherit}'
1788
+ ],
1789
+ 'should merge list-style with list-style-type': [
1790
+ 'li{list-style-type:disc;list-style:inside}',
1791
+ 'li{list-style:inside}'
1792
+ ]
1793
+ }),
1794
+ 'shorthand properties': cssContext({
1795
+ 'shorthand background #1' : [
1796
+ 'div{background-color:#111;background-image:url(aaa);background-repeat:repeat;background-position:0 0;background-attachment:scroll;background-size:auto}',
1797
+ 'div{background:url(aaa) #111}'
1798
+ ],
1799
+ 'shorthand background #2' : [
1800
+ 'div{background-color:#111;background-image:url(aaa);background-repeat:no-repeat;background-position:0 0;background-attachment:scroll;background-size:auto}',
1801
+ 'div{background:url(aaa) no-repeat #111}'
1802
+ ],
1803
+ 'shorthand important background' : [
1804
+ 'div{background-color:#111!important;background-image:url(aaa)!important;background-repeat:repeat!important;background-position:0 0!important;background-attachment:scroll!important;background-size:auto!important}',
1805
+ 'div{background:url(aaa) #111!important}'
1806
+ ],
1807
+ 'shorthand border-width': [
1808
+ '.t{border-top-width:7px;border-bottom-width:7px;border-left-width:4px;border-right-width:4px}',
1809
+ '.t{border-width:7px 4px}'
1810
+ ],
1811
+ 'shorthand border-color #1': [
1812
+ '.t{border-top-color:#9fce00;border-bottom-color:#9fce00;border-left-color:#9fce00;border-right-color:#9fce00}',
1813
+ '.t{border-color:#9fce00}'
1814
+ ],
1815
+ 'shorthand border-color #2': [
1816
+ '.t{border-right-color:#002;border-bottom-color:#003;border-top-color:#001;border-left-color:#004}',
1817
+ '.t{border-color:#001 #002 #003 #004}'
1818
+ ],
1819
+ 'shorthand border-radius': [
1820
+ '.t{border-top-left-radius:7px;border-bottom-right-radius:6px;border-bottom-left-radius:5px;border-top-right-radius:3px}',
1821
+ '.t{border-radius:7px 3px 6px 5px}'
1822
+ ],
1823
+ 'shorthand border-radius none': 'li{border-radius:none}',
1824
+ 'shorthand list-style #1': [
1825
+ '.t{list-style-type:circle;list-style-position:outside;list-style-image:url(aaa)}',
1826
+ '.t{list-style:circle url(aaa)}'
1827
+ ],
1828
+ 'shorthand list-style #2': [
1829
+ '.t{list-style-image:url(aaa);list-style-type:circle;list-style-position:inside}',
1830
+ '.t{list-style:circle inside url(aaa)}'
1831
+ ]
1832
+ }),
1833
+ 'care about understandability of shorthand components': cssContext({
1834
+ 'linear-gradient should NOT clear out background with color only' : [
1835
+ 'div{background:#fff;background:linear-gradient(whatever)}',
1836
+ 'div{background:#fff;background:linear-gradient(whatever)}'
1837
+ ],
1838
+ 'linear-gradient should NOT clear out background with color only, even if it has a color' : [
1839
+ 'div{background:#fff;background:linear-gradient(whatever) #222}',
1840
+ 'div{background:#fff;background:linear-gradient(whatever) #222}'
1841
+ ],
1842
+ 'a background-image with just a linear-gradient should not be compacted to a shorthand' : [
1843
+ 'div{background-color:#111;background-image:linear-gradient(aaa);background-repeat:no-repeat;background-position:0 0;background-attachment:scroll}',
1844
+ 'div{background-color:#111;background-image:linear-gradient(aaa);background-repeat:no-repeat;background-position:0 0;background-attachment:scroll}'
1845
+ ],
1846
+ 'a background-image with a none and a linear-gradient should result in two shorthands' : [
1847
+ 'div{background-color:#111;background-image:none;background-image:linear-gradient(aaa);background-repeat:repeat;background-position:0 0;background-attachment:scroll;background-size:auto}',
1848
+ 'div{background:#111;background:linear-gradient(aaa) #111}'
1849
+ ]
1850
+ }),
1851
+ 'cares about understandability of border components': cssContext({
1852
+ 'border(none) with border(rgba)': 'a{border:none;border:1px solid rgba(1,0,0,.5)}',
1853
+ 'border(rgba) with border(none)': 'a{border:1px solid rgba(1,0,0,.5);border:none}',
1854
+ 'border(hex) with border(rgba)': 'a{border:1px solid #fff;border:1px solid rgba(1,0,0,.5)}'
1855
+ }),
1856
+ 'merge same properties sensibly': cssContext({
1857
+ 'should merge color values with same understandability #1': [
1858
+ 'p{color:red;color:#fff;color:blue}',
1859
+ 'p{color:#00f}'
1860
+ ],
1861
+ 'should merge color values with same understandability #2': [
1862
+ 'p{color:red;color:#fff;color:blue;color:transparent}',
1863
+ 'p{color:transparent}'
1864
+ ],
1865
+ 'should NOT destroy less understandable values': [
1866
+ 'p{color:red;color:#fff;color:blue;color:rgba(1,2,3,.4)}',
1867
+ 'p{color:#00f;color:rgba(1,2,3,.4)}'
1868
+ ],
1869
+ 'should destroy even less understandable values if a more understandable one comes after them': [
1870
+ 'p{color:red;color:#fff;color:blue;color:rgba(1,2,3,.4);color:#9fce00}',
1871
+ 'p{color:#9fce00}'
1872
+ ],
1873
+ 'should merge functions with the same name but keep different functions intact': [
1874
+ 'p{background:-webkit-linear-gradient(aaa);background:-webkit-linear-gradient(bbb);background:linear-gradient(aaa);}',
1875
+ 'p{background:-webkit-linear-gradient(bbb);background:linear-gradient(aaa)}'
1876
+ ],
1877
+ 'should merge nonimportant + important into one important': [
1878
+ 'a{color:#aaa;color:#bbb!important}',
1879
+ 'a{color:#bbb!important}'
1880
+ ],
1881
+ 'should merge important + nonimportant into one important': [
1882
+ 'a{color:#aaa!important;color:#bbb}',
1883
+ 'a{color:#aaa!important}'
1884
+ ],
1885
+ 'should merge importants just like nonimportants while also overriding them': [
1886
+ 'p{color:red!important;color:#fff!important;color:blue!important;color:rgba(1,2,3,.4)}',
1887
+ 'p{color:#00f!important}'
1888
+ ]
1889
+ }),
1890
+ 'shorthand granular properties when other granular properties are already covered by the shorthand': cssContext({
1891
+ 'should consider the already existing margin to shorthand margin-top and margin-bottom': [
1892
+ 'p{margin:5px;margin-top:foo(1);margin-left:foo(2)}',
1893
+ 'p{margin:5px;margin:foo(1) 5px 5px foo(2)}'
1894
+ ],
1895
+ 'should merge margin-top and margin-left with shorthand if their understandability is the same': [
1896
+ 'p{margin:5px;margin-top:1px;margin-left:2px}',
1897
+ 'p{margin:1px 5px 5px 2px}'
1898
+ ],
1899
+ 'should NOT shorthand to margin-top if the result would be longer than the input': [
1900
+ 'p{margin:5px;margin-top:foo(1)}',
1901
+ 'p{margin:5px;margin-top:foo(1)}'
1902
+ ],
1903
+ 'should consider the already existing background to shorthand background-color': [
1904
+ 'p{background:#9fce00;background-color:rgba(1,2,3,.4)}',
1905
+ 'p{background:#9fce00;background:rgba(1,2,3,.4)}'
1906
+ ],
1907
+ 'should NOT touch important outline-color but should minify default value of outline to 0': [
1908
+ 'p{outline:medium;outline-color:#9fce00!important}',
1909
+ 'p{outline:0;outline-color:#9fce00!important}'
1910
+ ]
1911
+ }),
1912
+ 'take advantage of importants for optimalization opportunities': cssContext({
1913
+ 'should take into account important margin-left to shorthand non-important margin-top, margin-right and margin-bottom': [
1914
+ 'p{margin-top:1px;margin-right:2px;margin-bottom:3px;margin-left:4px !important}',
1915
+ 'p{margin:1px 2px 3px;margin-left:4px!important}'
1916
+ ],
1917
+ 'should take into account important margin-bottom and margin-left to shorten shorthanded non-important margin-top and margin-bottom': [
1918
+ 'p{margin-top:1px;margin-right:2px;margin-bottom:3px!important;margin-left:4px !important}',
1919
+ 'p{margin:1px 2px;margin-bottom:3px!important;margin-left:4px!important}'
1920
+ ],
1921
+ 'should take into account important margin-right and margin-left to shorten shorthanded non-important margin-top and margin-bottom': [
1922
+ 'p{margin-top:1px;margin-bottom:3px;margin-right:2px!important;margin-left:4px !important}',
1923
+ 'p{margin:1px 0 3px;margin-right:2px!important;margin-left:4px!important}'
1924
+ ],
1925
+ 'should take into account important margin-right and margin-left to shorten shorthanded non-important margin-top and margin-bottom #2': [
1926
+ 'p{margin-top:1px;margin-bottom:1px;margin-right:2px!important;margin-left:4px !important}',
1927
+ 'p{margin:1px;margin-right:2px!important;margin-left:4px!important}'
1928
+ ],
1929
+ 'should take into account important background-color and shorthand others into background': [
1930
+ 'p{background-color:#9fce00!important;background-image:url(hello);background-attachment:scroll;background-position:1px 2px;background-repeat:repeat-y;background-size:auto}',
1931
+ 'p{background-color:#9fce00!important;background:url(hello) 1px 2px repeat-y}'
1932
+ ],
1933
+ 'should take into account important outline-color and default value of outline-width': [
1934
+ 'p{outline:inset medium;outline-color:#9fce00!important;outline-style:inset!important}',
1935
+ 'p{outline:0;outline-color:#9fce00!important;outline-style:inset!important}'
1936
+ ],
1937
+ 'should take into account important background-position remove its irrelevant counterpart': [
1938
+ 'p{background:#9fce00 url(hello) 4px 5px;background-position:5px 3px!important}',
1939
+ 'p{background:url(hello) #9fce00;background-position:5px 3px!important}'
1940
+ ],
1941
+ 'should take into account important background-position and assign the shortest possible value for its irrelevant counterpart': [
1942
+ 'p{background:transparent;background-position:5px 3px!important}',
1943
+ 'p{background:0;background-position:5px 3px!important}'
1944
+ ]
1945
+ }),
1946
+ 'properly care about inherit': cssContext({
1947
+ 'merge multiple inherited margin granular properties into one inherited shorthand': [
1948
+ 'p{margin-top:inherit;margin-right:inherit;margin-bottom:inherit;margin-left:inherit}',
1949
+ 'p{margin:inherit}'
1950
+ ],
1951
+ 'merge multiple inherited background granular properties into one inherited shorthand': [
1952
+ 'p{background-color:inherit;background-image:inherit;background-attachment:inherit;background-position:inherit;background-repeat:inherit;;background-size:inherit}',
1953
+ 'p{background:inherit}'
1954
+ ],
1955
+ 'when shorter, optimize inherited/non-inherited background granular properties into an inherited shorthand and some non-inherited granular properties': [
1956
+ 'p{background-color:inherit;background-image:inherit;background-attachment:inherit;background-position:inherit;background-repeat:repeat-y;background-size:inherit}',
1957
+ 'p{background:inherit;background-repeat:repeat-y}'
1958
+ ],
1959
+ 'when shorter, optimize inherited/non-inherited background granular properties into a non-inherited shorthand and some inherited granular properties': [
1960
+ 'p{background-color:#9fce00;background-image:inherit;background-attachment:scroll;background-position:1px 2px;background-repeat:repeat-y;background-size:auto}',
1961
+ 'p{background:1px 2px repeat-y #9fce00;background-image:inherit}'
1962
+ ],
1963
+ 'put inherit to the place where it consumes the least space': [
1964
+ 'div{padding:0;padding-bottom:inherit;padding-right:inherit}',
1965
+ 'div{padding:inherit;padding-top:0;padding-left:0}'
1966
+ ]
1967
+ }),
1968
+ 'remove defaults from shorthands': cssContext({
1969
+ 'all-default background should be changed to shortest possible default value': [
1970
+ 'div{background:transparent none repeat 0 0 scroll}',
1971
+ 'div{background:0 0}'
1972
+ ],
1973
+ 'default background components should be removed #1': [
1974
+ 'body{background:#9fce00 none repeat scroll}',
1975
+ 'body{background:#9fce00}'
1976
+ ],
1977
+ 'default background components should be removed #2': [
1978
+ 'body{background:transparent none 1px 5px scroll}',
1979
+ 'body{background:1px 5px}'
1980
+ ],
1981
+ 'default background components should be removed #3': [
1982
+ 'body{background:none repeat scroll 0 0 #000}',
1983
+ 'body{background:#000}'
1984
+ ]
1985
+ }),
1986
+ 'complex granular properties': cssContext({
1987
+ 'two granular properties': 'a{border-bottom:1px solid red;border-color:red}',
1988
+ 'more understandable granular property should override less understandable': [
1989
+ 'a{border-color:rgba(0,0,0,.5);border-color:red}',
1990
+ 'a{border-color:red}'
1991
+ ],
1992
+ 'less understandable granular property should NOT override more understandable': [
1993
+ 'a{border-color:red;border-color:rgba(0,0,0,.5)}',
1994
+ 'a{border-color:red;border-color:rgba(0,0,0,.5)}'
1995
+ ],
1996
+ 'two same granular properties redefined': [
1997
+ 'a{border-color:rgba(0,0,0,.5);border-color:red;border:0}',
1998
+ 'a{border:0}'
1999
+ ],
2000
+ 'important granular property redefined': 'a{border-color:red!important;border:0}',
2001
+ 'important granular property redefined with important': [
2002
+ 'a{border-color:red!important;border:0!important}',
2003
+ 'a{border:0!important}'
2004
+ ],
2005
+ 'mix of border properties': [
2006
+ 'a{border-top:1px solid red;border-top-color:#0f0;color:red;border-top-width:2px;border-bottom-width:1px;border:0;border-left:1px solid red}',
2007
+ 'a{color:red;border:0;border-left:1px solid red}'
2008
+ ]
2009
+ }),
2010
+ 'grouping with advanced optimizations': cssContext({
2011
+ '@-moz-document': '@-moz-document domain(mozilla.org){a{color:red}}',
2012
+ '@media': '@media{a{color:red}}',
2013
+ '@page': '@page{margin:.5em}',
2014
+ '@supports': '@supports (display:flexbox){.flex{display:flexbox}}',
2015
+ '@-ms-viewport': '@-ms-viewport{width:device-width}',
2016
+ '@-o-viewport': '@-o-viewport{width:device-width}',
2017
+ '@viewport': '@viewport{width:device-width}'
2018
+ }),
2019
+ 'background size': cssContext({
2020
+ 'with background-position': 'a{background:url(top.jpg) 50% 0/auto 25% no-repeat}',
2021
+ 'with background-position and spaces': [
2022
+ 'a{background:url(top.jpg) 50% 0 / auto 25% no-repeat}',
2023
+ 'a{background:url(top.jpg) 50% 0/auto 25% no-repeat}'
2024
+ ],
2025
+ 'with background-position shorthands': 'a{background:url(top.jpg) 50px/25% no-repeat}',
2026
+ 'with background-position shorthands and spaces': [
2027
+ 'a{background:url(top.jpg) 0 / cover no-repeat}',
2028
+ 'a{background:url(top.jpg) 0/cover no-repeat}'
2029
+ ],
2030
+ 'with background-size property': [
2031
+ 'a{background:none;background-image:url(1.png);background-size:28px 28px}',
2032
+ 'a{background:url(1.png) 0 0/28px 28px}'
2033
+ ]
2034
+ }),
2035
+ 'misc advanced': cssContext({
2036
+ 'outline auto': [
2037
+ 'a{outline:5px auto -webkit-focus-ring-color}',
2038
+ 'a{outline:-webkit-focus-ring-color auto 5px}'
2039
+ ],
2040
+ 'border radius side H+V': [
2041
+ 'a{border-top-left-radius:2em / 1em}',
2042
+ 'a{border-top-left-radius:2em/1em}'
2043
+ ],
2044
+ 'border radius expanded H+V': [
2045
+ 'a{border-radius:1em 1em 1em 1em / 2em 2em 2em 2em}',
2046
+ 'a{border-radius:1em/2em}'
2047
+ ],
2048
+ 'border radius expanded H+V with mixed values #1': [
2049
+ 'a{border-radius:1em 2em 1em 2em / 1em 2em 3em 2em}',
2050
+ 'a{border-radius:1em 2em/1em 2em 3em}'
2051
+ ],
2052
+ 'border radius expanded H+V with mixed values #2': 'a{border-radius:1em/1em 1em 1em 2em}',
2053
+ 'border radius H+V': 'a{border-radius:50%/100%}',
2054
+ 'lost background position': [
2055
+ '.one{background:50% no-repeat}.one{background-image:url(/img.png)}',
2056
+ '.one{background:url(/img.png) 50% no-repeat}'
2057
+ ],
2058
+ 'merging color with backgrounds': [
2059
+ 'p{background:red;background-image:url(1.png),url(2.png)}',
2060
+ 'p{background:url(1.png),url(2.png) red}'
2061
+ ],
2062
+ 'unknown @ rule': '@unknown "test";h1{color:red}'
2063
+ }),
2064
+ 'viewport units': cssContext({
2065
+ 'shorthand margin with viewport width not changed': 'div{margin:5vw}'
2066
+ }),
2067
+ 'variables': cssContext({
2068
+ 'stripping': 'a{--border:#000}.one{border:1px solid var(--border)}',
2069
+ 'all values': 'a{--width:1px;--style:solid;--color:#000}.one{border:var(--width) var(--style) var(--color)}'
2070
+ })
2071
+ }).export(module);