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.
- data/.gitmodules +3 -0
- data/EXAMPLE.md +25 -0
- data/Gemfile +4 -0
- data/Gemfile.lock +30 -0
- data/LICENSE +19 -0
- data/README.md +106 -0
- data/lib/javascript/clean-css/.gitignore +4 -0
- data/lib/javascript/clean-css/.jshintignore +3 -0
- data/lib/javascript/clean-css/.jshintrc +14 -0
- data/lib/javascript/clean-css/.travis.yml +11 -0
- data/lib/javascript/clean-css/History.md +556 -0
- data/lib/javascript/clean-css/LICENSE +19 -0
- data/lib/javascript/clean-css/README.md +218 -0
- data/lib/javascript/clean-css/bin/cleancss +157 -0
- data/lib/javascript/clean-css/index.js +1 -0
- data/lib/javascript/clean-css/lib/clean.js +426 -0
- data/lib/javascript/clean-css/lib/colors/hsl-to-hex.js +64 -0
- data/lib/javascript/clean-css/lib/colors/long-to-short-hex.js +12 -0
- data/lib/javascript/clean-css/lib/colors/rgb-to-hex.js +14 -0
- data/lib/javascript/clean-css/lib/colors/shortener.js +174 -0
- data/lib/javascript/clean-css/lib/images/url-rebase.js +32 -0
- data/lib/javascript/clean-css/lib/images/url-rewriter.js +60 -0
- data/lib/javascript/clean-css/lib/imports/inliner.js +319 -0
- data/lib/javascript/clean-css/lib/properties/optimizer.js +276 -0
- data/lib/javascript/clean-css/lib/properties/override-compactor.js +116 -0
- data/lib/javascript/clean-css/lib/properties/processable.js +859 -0
- data/lib/javascript/clean-css/lib/properties/scanner.js +20 -0
- data/lib/javascript/clean-css/lib/properties/shorthand-compactor.js +244 -0
- data/lib/javascript/clean-css/lib/properties/token.js +184 -0
- data/lib/javascript/clean-css/lib/properties/validator.js +140 -0
- data/lib/javascript/clean-css/lib/selectors/empty-removal.js +30 -0
- data/lib/javascript/clean-css/lib/selectors/optimizer.js +341 -0
- data/lib/javascript/clean-css/lib/selectors/tokenizer.js +168 -0
- data/lib/javascript/clean-css/lib/text/comments.js +83 -0
- data/lib/javascript/clean-css/lib/text/escape-store.js +32 -0
- data/lib/javascript/clean-css/lib/text/expressions.js +73 -0
- data/lib/javascript/clean-css/lib/text/free.js +26 -0
- data/lib/javascript/clean-css/lib/text/name-quotes.js +37 -0
- data/lib/javascript/clean-css/lib/text/quote-scanner.js +91 -0
- data/lib/javascript/clean-css/lib/text/splitter.js +35 -0
- data/lib/javascript/clean-css/lib/text/urls.js +41 -0
- data/lib/javascript/clean-css/package.json +50 -0
- data/lib/javascript/clean-css/test/batch-test.js +56 -0
- data/lib/javascript/clean-css/test/bench.js +11 -0
- data/lib/javascript/clean-css/test/binary-test.js +323 -0
- data/lib/javascript/clean-css/test/data-bench/_partial.css +1 -0
- data/lib/javascript/clean-css/test/data-bench/complex.css +4 -0
- data/lib/javascript/clean-css/test/data/129-assets/assets/ui.css +2 -0
- data/lib/javascript/clean-css/test/data/129-assets/components/bootstrap/css/bootstrap.css +3 -0
- data/lib/javascript/clean-css/test/data/129-assets/components/bootstrap/images/glyphs.gif +0 -0
- data/lib/javascript/clean-css/test/data/129-assets/components/jquery-ui/css/style.css +6 -0
- data/lib/javascript/clean-css/test/data/129-assets/components/jquery-ui/images/next.gif +0 -0
- data/lib/javascript/clean-css/test/data/129-assets/components/jquery-ui/images/prev.gif +0 -0
- data/lib/javascript/clean-css/test/data/960-min.css +125 -0
- data/lib/javascript/clean-css/test/data/960.css +602 -0
- data/lib/javascript/clean-css/test/data/big-min.css +2984 -0
- data/lib/javascript/clean-css/test/data/big.css +13794 -0
- data/lib/javascript/clean-css/test/data/blueprint-min.css +245 -0
- data/lib/javascript/clean-css/test/data/blueprint.css +556 -0
- data/lib/javascript/clean-css/test/data/charset-mixed-with-fonts-min.css +3 -0
- data/lib/javascript/clean-css/test/data/charset-mixed-with-fonts.css +14 -0
- data/lib/javascript/clean-css/test/data/font-awesome-ie7-min.css +392 -0
- data/lib/javascript/clean-css/test/data/font-awesome-ie7.css +1203 -0
- data/lib/javascript/clean-css/test/data/font-awesome-min.css +319 -0
- data/lib/javascript/clean-css/test/data/font-awesome.css +540 -0
- data/lib/javascript/clean-css/test/data/imports-min.css +5 -0
- data/lib/javascript/clean-css/test/data/imports.css +4 -0
- data/lib/javascript/clean-css/test/data/issue-117-snippet-min.css +3 -0
- data/lib/javascript/clean-css/test/data/issue-117-snippet.css +19 -0
- data/lib/javascript/clean-css/test/data/issue-159-snippet-min.css +7 -0
- data/lib/javascript/clean-css/test/data/issue-159-snippet.css +7 -0
- data/lib/javascript/clean-css/test/data/issue-192-min.css +1 -0
- data/lib/javascript/clean-css/test/data/issue-192.css +8 -0
- data/lib/javascript/clean-css/test/data/issue-198-min.css +3 -0
- data/lib/javascript/clean-css/test/data/issue-198.css +4 -0
- data/lib/javascript/clean-css/test/data/issue-232-min.css +2 -0
- data/lib/javascript/clean-css/test/data/issue-232.css +17 -0
- data/lib/javascript/clean-css/test/data/issue-241-min.css +2 -0
- data/lib/javascript/clean-css/test/data/issue-241.css +2 -0
- data/lib/javascript/clean-css/test/data/issue-304-min.css +1 -0
- data/lib/javascript/clean-css/test/data/issue-304.css +4 -0
- data/lib/javascript/clean-css/test/data/issue-305-min.css +1 -0
- data/lib/javascript/clean-css/test/data/issue-305.css +3 -0
- data/lib/javascript/clean-css/test/data/issue-308-min.css +1 -0
- data/lib/javascript/clean-css/test/data/issue-308.css +5 -0
- data/lib/javascript/clean-css/test/data/issue-312-min.css +1 -0
- data/lib/javascript/clean-css/test/data/issue-312.css +7 -0
- data/lib/javascript/clean-css/test/data/issue-337-min.css +1 -0
- data/lib/javascript/clean-css/test/data/issue-337.css +4 -0
- data/lib/javascript/clean-css/test/data/line-breaks-in-attributes-min.css +2 -0
- data/lib/javascript/clean-css/test/data/line-breaks-in-attributes.css +8 -0
- data/lib/javascript/clean-css/test/data/partials-absolute/base.css +3 -0
- data/lib/javascript/clean-css/test/data/partials-absolute/base2.css +1 -0
- data/lib/javascript/clean-css/test/data/partials-absolute/extra/sub.css +3 -0
- data/lib/javascript/clean-css/test/data/partials-relative/base.css +3 -0
- data/lib/javascript/clean-css/test/data/partials-relative/extra/included.css +1 -0
- data/lib/javascript/clean-css/test/data/partials/comment.css +2 -0
- data/lib/javascript/clean-css/test/data/partials/extra/down.gif +0 -0
- data/lib/javascript/clean-css/test/data/partials/extra/four.css +3 -0
- data/lib/javascript/clean-css/test/data/partials/extra/three.css +1 -0
- data/lib/javascript/clean-css/test/data/partials/five.css +1 -0
- data/lib/javascript/clean-css/test/data/partials/four.css +1 -0
- data/lib/javascript/clean-css/test/data/partials/one.css +1 -0
- data/lib/javascript/clean-css/test/data/partials/three.css +1 -0
- data/lib/javascript/clean-css/test/data/partials/two.css +5 -0
- data/lib/javascript/clean-css/test/data/reset-min.css +12 -0
- data/lib/javascript/clean-css/test/data/reset.css +64 -0
- data/lib/javascript/clean-css/test/data/sample1-min.css +4 -0
- data/lib/javascript/clean-css/test/data/sample1.css +12 -0
- data/lib/javascript/clean-css/test/data/unsupported/selectors-ie7.css +20 -0
- data/lib/javascript/clean-css/test/data/unsupported/selectors-ie8.css +17 -0
- data/lib/javascript/clean-css/test/module-test.js +185 -0
- data/lib/javascript/clean-css/test/protocol-imports-test.js +409 -0
- data/lib/javascript/clean-css/test/text/splitter-test.js +20 -0
- data/lib/javascript/clean-css/test/unit-test.js +2071 -0
- data/lib/ruby-clean-css.rb +6 -0
- data/lib/ruby-clean-css/compressor.rb +142 -0
- data/lib/ruby-clean-css/exports.rb +258 -0
- data/lib/ruby-clean-css/railtie.rb +27 -0
- data/lib/ruby-clean-css/sprockets.rb +19 -0
- data/lib/ruby-clean-css/version.rb +5 -0
- data/ruby-clean-css.gemspec +30 -0
- data/test/test_compressor.rb +84 -0
- metadata +203 -0
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
module.exports = function EmptyRemoval(data) {
|
|
2
|
+
var stripEmpty = function(cssData) {
|
|
3
|
+
var tempData = [];
|
|
4
|
+
var nextEmpty = 0;
|
|
5
|
+
var cursor = 0;
|
|
6
|
+
|
|
7
|
+
for (; nextEmpty < cssData.length;) {
|
|
8
|
+
nextEmpty = cssData.indexOf('{}', cursor);
|
|
9
|
+
if (nextEmpty == -1)
|
|
10
|
+
break;
|
|
11
|
+
|
|
12
|
+
var startsAt = nextEmpty - 1;
|
|
13
|
+
while (cssData[startsAt] && cssData[startsAt] != '}' && cssData[startsAt] != '{' && cssData[startsAt] != ';')
|
|
14
|
+
startsAt--;
|
|
15
|
+
|
|
16
|
+
tempData.push(cssData.substring(cursor, startsAt + 1));
|
|
17
|
+
cursor = nextEmpty + 2;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
return tempData.length > 0 ?
|
|
21
|
+
stripEmpty(tempData.join('') + cssData.substring(cursor, cssData.length)) :
|
|
22
|
+
cssData;
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
return {
|
|
26
|
+
process: function() {
|
|
27
|
+
return stripEmpty(data);
|
|
28
|
+
}
|
|
29
|
+
};
|
|
30
|
+
};
|
|
@@ -0,0 +1,341 @@
|
|
|
1
|
+
var Tokenizer = require('./tokenizer');
|
|
2
|
+
var PropertyOptimizer = require('../properties/optimizer');
|
|
3
|
+
|
|
4
|
+
module.exports = function Optimizer(data, context, options) {
|
|
5
|
+
var specialSelectors = {
|
|
6
|
+
'*': /\-(moz|ms|o|webkit)\-/,
|
|
7
|
+
'ie8': /(\-moz\-|\-ms\-|\-o\-|\-webkit\-|:root|:nth|:first\-of|:last|:only|:empty|:target|:checked|::selection|:enabled|:disabled|:not)/,
|
|
8
|
+
'ie7': /(\-moz\-|\-ms\-|\-o\-|\-webkit\-|:focus|:before|:after|:root|:nth|:first\-of|:last|:only|:empty|:target|:checked|::selection|:enabled|:disabled|:not)/
|
|
9
|
+
};
|
|
10
|
+
|
|
11
|
+
var minificationsMade = [];
|
|
12
|
+
|
|
13
|
+
var propertyOptimizer = new PropertyOptimizer(options.compatibility, options.aggressiveMerging);
|
|
14
|
+
|
|
15
|
+
var cleanUpSelector = function(selectors) {
|
|
16
|
+
if (selectors.indexOf(',') == -1)
|
|
17
|
+
return selectors;
|
|
18
|
+
|
|
19
|
+
var plain = [];
|
|
20
|
+
var cursor = 0;
|
|
21
|
+
var lastComma = 0;
|
|
22
|
+
var noBrackets = selectors.indexOf('(') == -1;
|
|
23
|
+
var withinBrackets = function(idx) {
|
|
24
|
+
if (noBrackets)
|
|
25
|
+
return false;
|
|
26
|
+
|
|
27
|
+
var previousOpening = selectors.lastIndexOf('(', idx);
|
|
28
|
+
var previousClosing = selectors.lastIndexOf(')', idx);
|
|
29
|
+
|
|
30
|
+
if (previousOpening == -1)
|
|
31
|
+
return false;
|
|
32
|
+
if (previousClosing > 0 && previousClosing < idx)
|
|
33
|
+
return false;
|
|
34
|
+
|
|
35
|
+
return true;
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
while (true) {
|
|
39
|
+
var nextComma = selectors.indexOf(',', cursor + 1);
|
|
40
|
+
var selector;
|
|
41
|
+
|
|
42
|
+
if (nextComma === -1) {
|
|
43
|
+
nextComma = selectors.length;
|
|
44
|
+
} else if (withinBrackets(nextComma)) {
|
|
45
|
+
cursor = nextComma + 1;
|
|
46
|
+
continue;
|
|
47
|
+
}
|
|
48
|
+
selector = selectors.substring(lastComma, nextComma);
|
|
49
|
+
lastComma = cursor = nextComma + 1;
|
|
50
|
+
|
|
51
|
+
if (plain.indexOf(selector) == -1)
|
|
52
|
+
plain.push(selector);
|
|
53
|
+
|
|
54
|
+
if (nextComma === selectors.length)
|
|
55
|
+
break;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
return plain.sort().join(',');
|
|
59
|
+
};
|
|
60
|
+
|
|
61
|
+
var isSpecial = function(selector) {
|
|
62
|
+
return specialSelectors[options.compatibility || '*'].test(selector);
|
|
63
|
+
};
|
|
64
|
+
|
|
65
|
+
var removeDuplicates = function(tokens) {
|
|
66
|
+
var matched = {};
|
|
67
|
+
var forRemoval = [];
|
|
68
|
+
|
|
69
|
+
for (var i = 0, l = tokens.length; i < l; i++) {
|
|
70
|
+
var token = tokens[i];
|
|
71
|
+
if (typeof token == 'string' || token.block)
|
|
72
|
+
continue;
|
|
73
|
+
|
|
74
|
+
var id = token.body + '@' + token.selector;
|
|
75
|
+
var alreadyMatched = matched[id];
|
|
76
|
+
|
|
77
|
+
if (alreadyMatched) {
|
|
78
|
+
forRemoval.push(alreadyMatched[0]);
|
|
79
|
+
alreadyMatched.unshift(i);
|
|
80
|
+
} else {
|
|
81
|
+
matched[id] = [i];
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
forRemoval = forRemoval.sort(function(a, b) {
|
|
86
|
+
return a > b ? 1 : -1;
|
|
87
|
+
});
|
|
88
|
+
|
|
89
|
+
for (var j = 0, n = forRemoval.length; j < n; j++) {
|
|
90
|
+
tokens.splice(forRemoval[j] - j, 1);
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
minificationsMade.unshift(forRemoval.length > 0);
|
|
94
|
+
};
|
|
95
|
+
|
|
96
|
+
var mergeAdjacent = function(tokens) {
|
|
97
|
+
var forRemoval = [];
|
|
98
|
+
var lastToken = { selector: null, body: null };
|
|
99
|
+
|
|
100
|
+
for (var i = 0, l = tokens.length; i < l; i++) {
|
|
101
|
+
var token = tokens[i];
|
|
102
|
+
|
|
103
|
+
if (typeof token == 'string' || token.block)
|
|
104
|
+
continue;
|
|
105
|
+
|
|
106
|
+
if (token.selector == lastToken.selector) {
|
|
107
|
+
var joinAt = [lastToken.body.split(';').length];
|
|
108
|
+
lastToken.body = propertyOptimizer.process(lastToken.body + ';' + token.body, joinAt);
|
|
109
|
+
forRemoval.push(i);
|
|
110
|
+
} else if (token.body == lastToken.body && !isSpecial(token.selector) && !isSpecial(lastToken.selector)) {
|
|
111
|
+
lastToken.selector = cleanUpSelector(lastToken.selector + ',' + token.selector);
|
|
112
|
+
forRemoval.push(i);
|
|
113
|
+
} else {
|
|
114
|
+
lastToken = token;
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
for (var j = 0, m = forRemoval.length; j < m; j++) {
|
|
119
|
+
tokens.splice(forRemoval[j] - j, 1);
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
minificationsMade.unshift(forRemoval.length > 0);
|
|
123
|
+
};
|
|
124
|
+
|
|
125
|
+
var reduceNonAdjacent = function(tokens) {
|
|
126
|
+
var candidates = {};
|
|
127
|
+
var moreThanOnce = [];
|
|
128
|
+
|
|
129
|
+
for (var i = tokens.length - 1; i >= 0; i--) {
|
|
130
|
+
var token = tokens[i];
|
|
131
|
+
|
|
132
|
+
if (typeof token == 'string' || token.block)
|
|
133
|
+
continue;
|
|
134
|
+
|
|
135
|
+
var complexSelector = token.selector;
|
|
136
|
+
var selectors = complexSelector.indexOf(',') > -1 && !isSpecial(complexSelector) ?
|
|
137
|
+
complexSelector.split(',').concat(complexSelector) : // simplification, as :not() can have commas too
|
|
138
|
+
[complexSelector];
|
|
139
|
+
|
|
140
|
+
for (var j = 0, m = selectors.length; j < m; j++) {
|
|
141
|
+
var selector = selectors[j];
|
|
142
|
+
|
|
143
|
+
if (!candidates[selector])
|
|
144
|
+
candidates[selector] = [];
|
|
145
|
+
else
|
|
146
|
+
moreThanOnce.push(selector);
|
|
147
|
+
|
|
148
|
+
candidates[selector].push({
|
|
149
|
+
where: i,
|
|
150
|
+
partial: selector != complexSelector
|
|
151
|
+
});
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
var reducedInSimple = _reduceSimpleNonAdjacentCases(tokens, moreThanOnce, candidates);
|
|
156
|
+
var reducedInComplex = _reduceComplexNonAdjacentCases(tokens, candidates);
|
|
157
|
+
|
|
158
|
+
minificationsMade.unshift(reducedInSimple || reducedInComplex);
|
|
159
|
+
};
|
|
160
|
+
|
|
161
|
+
var _reduceSimpleNonAdjacentCases = function(tokens, matches, positions) {
|
|
162
|
+
var reduced = false;
|
|
163
|
+
|
|
164
|
+
for (var i = 0, l = matches.length; i < l; i++) {
|
|
165
|
+
var selector = matches[i];
|
|
166
|
+
var data = positions[selector];
|
|
167
|
+
|
|
168
|
+
if (data.length < 2)
|
|
169
|
+
continue;
|
|
170
|
+
|
|
171
|
+
/* jshint loopfunc: true */
|
|
172
|
+
_reduceSelector(tokens, selector, data, {
|
|
173
|
+
filterOut: function(idx, bodies) {
|
|
174
|
+
return data[idx].partial && bodies.length === 0;
|
|
175
|
+
},
|
|
176
|
+
callback: function(token, newBody, processedCount, tokenIdx) {
|
|
177
|
+
if (!data[processedCount - tokenIdx - 1].partial) {
|
|
178
|
+
token.body = newBody.join(';');
|
|
179
|
+
reduced = true;
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
});
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
return reduced;
|
|
186
|
+
};
|
|
187
|
+
|
|
188
|
+
var _reduceComplexNonAdjacentCases = function(tokens, positions) {
|
|
189
|
+
var reduced = false;
|
|
190
|
+
|
|
191
|
+
allSelectors:
|
|
192
|
+
for (var complexSelector in positions) {
|
|
193
|
+
if (complexSelector.indexOf(',') == -1) // simplification, as :not() can have commas too
|
|
194
|
+
continue;
|
|
195
|
+
|
|
196
|
+
var intoPosition = positions[complexSelector].pop().where;
|
|
197
|
+
var intoToken = tokens[intoPosition];
|
|
198
|
+
|
|
199
|
+
var selectors = isSpecial(complexSelector) ?
|
|
200
|
+
[complexSelector] :
|
|
201
|
+
complexSelector.split(',');
|
|
202
|
+
var reducedBodies = [];
|
|
203
|
+
|
|
204
|
+
for (var j = 0, m = selectors.length; j < m; j++) {
|
|
205
|
+
var selector = selectors[j];
|
|
206
|
+
var data = positions[selector];
|
|
207
|
+
|
|
208
|
+
if (data.length < 2)
|
|
209
|
+
continue allSelectors;
|
|
210
|
+
|
|
211
|
+
/* jshint loopfunc: true */
|
|
212
|
+
_reduceSelector(tokens, selector, data, {
|
|
213
|
+
filterOut: function(idx) {
|
|
214
|
+
return data[idx].where < intoPosition;
|
|
215
|
+
},
|
|
216
|
+
callback: function(token, newBody, processedCount, tokenIdx) {
|
|
217
|
+
if (tokenIdx === 0)
|
|
218
|
+
reducedBodies.push(newBody.join(';'));
|
|
219
|
+
}
|
|
220
|
+
});
|
|
221
|
+
|
|
222
|
+
if (reducedBodies[reducedBodies.length - 1] != reducedBodies[0])
|
|
223
|
+
continue allSelectors;
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
intoToken.body = reducedBodies[0];
|
|
227
|
+
reduced = true;
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
return reduced;
|
|
231
|
+
};
|
|
232
|
+
|
|
233
|
+
var _reduceSelector = function(tokens, selector, data, options) {
|
|
234
|
+
var bodies = [];
|
|
235
|
+
var joinsAt = [];
|
|
236
|
+
var splitBodies = [];
|
|
237
|
+
var processedTokens = [];
|
|
238
|
+
|
|
239
|
+
for (var j = data.length - 1, m = 0; j >= 0; j--) {
|
|
240
|
+
if (options.filterOut(j, bodies))
|
|
241
|
+
continue;
|
|
242
|
+
|
|
243
|
+
var where = data[j].where;
|
|
244
|
+
var token = tokens[where];
|
|
245
|
+
var body = token.body;
|
|
246
|
+
bodies.push(body);
|
|
247
|
+
splitBodies.push(body.split(';'));
|
|
248
|
+
processedTokens.push(where);
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
for (j = 0, m = bodies.length; j < m; j++) {
|
|
252
|
+
if (bodies[j].length > 0)
|
|
253
|
+
joinsAt.push((joinsAt[j - 1] || 0) + splitBodies[j].length);
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
var optimizedBody = propertyOptimizer.process(bodies.join(';'), joinsAt, true);
|
|
257
|
+
var optimizedProperties = optimizedBody.split(';');
|
|
258
|
+
|
|
259
|
+
var processedCount = processedTokens.length;
|
|
260
|
+
var propertyIdx = optimizedProperties.length - 1;
|
|
261
|
+
var tokenIdx = processedCount - 1;
|
|
262
|
+
|
|
263
|
+
while (tokenIdx >= 0) {
|
|
264
|
+
if ((tokenIdx === 0 || splitBodies[tokenIdx].indexOf(optimizedProperties[propertyIdx]) > -1) && propertyIdx > -1) {
|
|
265
|
+
propertyIdx--;
|
|
266
|
+
continue;
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
var newBody = optimizedProperties.splice(propertyIdx + 1);
|
|
270
|
+
options.callback(tokens[processedTokens[tokenIdx]], newBody, processedCount, tokenIdx);
|
|
271
|
+
|
|
272
|
+
tokenIdx--;
|
|
273
|
+
}
|
|
274
|
+
};
|
|
275
|
+
|
|
276
|
+
var optimize = function(tokens) {
|
|
277
|
+
var noChanges = function() {
|
|
278
|
+
return minificationsMade.length > 4 &&
|
|
279
|
+
minificationsMade[0] === false &&
|
|
280
|
+
minificationsMade[1] === false;
|
|
281
|
+
};
|
|
282
|
+
|
|
283
|
+
tokens = Array.isArray(tokens) ? tokens : [tokens];
|
|
284
|
+
for (var i = 0, l = tokens.length; i < l; i++) {
|
|
285
|
+
var token = tokens[i];
|
|
286
|
+
|
|
287
|
+
if (token.selector) {
|
|
288
|
+
token.selector = cleanUpSelector(token.selector);
|
|
289
|
+
token.body = propertyOptimizer.process(token.body, false);
|
|
290
|
+
} else if (token.block) {
|
|
291
|
+
optimize(token.body);
|
|
292
|
+
}
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
// Run until 2 last operations do not yield any changes
|
|
296
|
+
minificationsMade = [];
|
|
297
|
+
while (true) {
|
|
298
|
+
if (noChanges())
|
|
299
|
+
break;
|
|
300
|
+
removeDuplicates(tokens);
|
|
301
|
+
|
|
302
|
+
if (noChanges())
|
|
303
|
+
break;
|
|
304
|
+
mergeAdjacent(tokens);
|
|
305
|
+
|
|
306
|
+
if (noChanges())
|
|
307
|
+
break;
|
|
308
|
+
reduceNonAdjacent(tokens);
|
|
309
|
+
}
|
|
310
|
+
};
|
|
311
|
+
|
|
312
|
+
var rebuild = function(tokens) {
|
|
313
|
+
var rebuilt = [];
|
|
314
|
+
|
|
315
|
+
tokens = Array.isArray(tokens) ? tokens : [tokens];
|
|
316
|
+
for (var i = 0, l = tokens.length; i < l; i++) {
|
|
317
|
+
var token = tokens[i];
|
|
318
|
+
|
|
319
|
+
if (typeof token == 'string') {
|
|
320
|
+
rebuilt.push(token);
|
|
321
|
+
continue;
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
var name = token.block || token.selector;
|
|
325
|
+
var body = token.block ? rebuild(token.body) : token.body;
|
|
326
|
+
|
|
327
|
+
if (body.length > 0)
|
|
328
|
+
rebuilt.push(name + '{' + body + '}');
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
return rebuilt.join(options.keepBreaks ? options.lineBreak : '');
|
|
332
|
+
};
|
|
333
|
+
|
|
334
|
+
return {
|
|
335
|
+
process: function() {
|
|
336
|
+
var tokenized = new Tokenizer(data, context).process();
|
|
337
|
+
optimize(tokenized);
|
|
338
|
+
return rebuild(tokenized);
|
|
339
|
+
}
|
|
340
|
+
};
|
|
341
|
+
};
|
|
@@ -0,0 +1,168 @@
|
|
|
1
|
+
module.exports = function Tokenizer(data, minifyContext) {
|
|
2
|
+
var chunker = new Chunker(data, 128);
|
|
3
|
+
var chunk = chunker.next();
|
|
4
|
+
var flatBlock = /(^@(font\-face|page|\-ms\-viewport|\-o\-viewport|viewport)|\\@.+?)/;
|
|
5
|
+
|
|
6
|
+
var whatsNext = function(context) {
|
|
7
|
+
var cursor = context.cursor;
|
|
8
|
+
var mode = context.mode;
|
|
9
|
+
var closest;
|
|
10
|
+
|
|
11
|
+
if (chunk.length == context.cursor) {
|
|
12
|
+
if (chunker.isEmpty())
|
|
13
|
+
return null;
|
|
14
|
+
|
|
15
|
+
chunk = chunker.next();
|
|
16
|
+
context.cursor = 0;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
if (mode == 'body') {
|
|
20
|
+
closest = chunk.indexOf('}', cursor);
|
|
21
|
+
return closest > -1 ?
|
|
22
|
+
[closest, 'bodyEnd'] :
|
|
23
|
+
null;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
var nextSpecial = chunk.indexOf('@', context.cursor);
|
|
27
|
+
var nextEscape = mode == 'top' ? chunk.indexOf('__ESCAPED_COMMENT_CLEAN_CSS', context.cursor) : -1;
|
|
28
|
+
var nextBodyStart = chunk.indexOf('{', context.cursor);
|
|
29
|
+
var nextBodyEnd = chunk.indexOf('}', context.cursor);
|
|
30
|
+
|
|
31
|
+
closest = nextSpecial;
|
|
32
|
+
if (closest == -1 || (nextEscape > -1 && nextEscape < closest))
|
|
33
|
+
closest = nextEscape;
|
|
34
|
+
if (closest == -1 || (nextBodyStart > -1 && nextBodyStart < closest))
|
|
35
|
+
closest = nextBodyStart;
|
|
36
|
+
if (closest == -1 || (nextBodyEnd > -1 && nextBodyEnd < closest))
|
|
37
|
+
closest = nextBodyEnd;
|
|
38
|
+
|
|
39
|
+
if (closest == -1)
|
|
40
|
+
return;
|
|
41
|
+
if (nextEscape === closest)
|
|
42
|
+
return [closest, 'escape'];
|
|
43
|
+
if (nextBodyStart === closest)
|
|
44
|
+
return [closest, 'bodyStart'];
|
|
45
|
+
if (nextBodyEnd === closest)
|
|
46
|
+
return [closest, 'bodyEnd'];
|
|
47
|
+
if (nextSpecial === closest)
|
|
48
|
+
return [closest, 'special'];
|
|
49
|
+
};
|
|
50
|
+
|
|
51
|
+
var tokenize = function(context) {
|
|
52
|
+
var tokenized = [];
|
|
53
|
+
|
|
54
|
+
context = context || { cursor: 0, mode: 'top' };
|
|
55
|
+
|
|
56
|
+
while (true) {
|
|
57
|
+
var next = whatsNext(context);
|
|
58
|
+
if (!next) {
|
|
59
|
+
var whatsLeft = chunk.substring(context.cursor);
|
|
60
|
+
if (whatsLeft.length > 0) {
|
|
61
|
+
tokenized.push(whatsLeft);
|
|
62
|
+
context.cursor += whatsLeft.length;
|
|
63
|
+
}
|
|
64
|
+
break;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
var nextSpecial = next[0];
|
|
68
|
+
var what = next[1];
|
|
69
|
+
var nextEnd;
|
|
70
|
+
var oldMode;
|
|
71
|
+
|
|
72
|
+
if (what == 'special') {
|
|
73
|
+
var firstOpenBraceAt = chunk.indexOf('{', nextSpecial);
|
|
74
|
+
var firstSemicolonAt = chunk.indexOf(';', nextSpecial);
|
|
75
|
+
var isSingle = firstSemicolonAt > -1 && (firstOpenBraceAt == -1 || firstSemicolonAt < firstOpenBraceAt);
|
|
76
|
+
if (isSingle) {
|
|
77
|
+
nextEnd = chunk.indexOf(';', nextSpecial + 1);
|
|
78
|
+
tokenized.push(chunk.substring(context.cursor, nextEnd + 1));
|
|
79
|
+
|
|
80
|
+
context.cursor = nextEnd + 1;
|
|
81
|
+
} else {
|
|
82
|
+
nextEnd = chunk.indexOf('{', nextSpecial + 1);
|
|
83
|
+
var block = chunk.substring(context.cursor, nextEnd).trim();
|
|
84
|
+
|
|
85
|
+
var isFlat = flatBlock.test(block);
|
|
86
|
+
oldMode = context.mode;
|
|
87
|
+
context.cursor = nextEnd + 1;
|
|
88
|
+
context.mode = isFlat ? 'body' : 'block';
|
|
89
|
+
var specialBody = tokenize(context);
|
|
90
|
+
context.mode = oldMode;
|
|
91
|
+
|
|
92
|
+
tokenized.push({ block: block, body: specialBody });
|
|
93
|
+
}
|
|
94
|
+
} else if (what == 'escape') {
|
|
95
|
+
nextEnd = chunk.indexOf('__', nextSpecial + 1);
|
|
96
|
+
var escaped = chunk.substring(context.cursor, nextEnd + 2);
|
|
97
|
+
tokenized.push(escaped);
|
|
98
|
+
|
|
99
|
+
context.cursor = nextEnd + 2;
|
|
100
|
+
} else if (what == 'bodyStart') {
|
|
101
|
+
var selector = chunk.substring(context.cursor, nextSpecial).trim();
|
|
102
|
+
|
|
103
|
+
oldMode = context.mode;
|
|
104
|
+
context.cursor = nextSpecial + 1;
|
|
105
|
+
context.mode = 'body';
|
|
106
|
+
var body = tokenize(context);
|
|
107
|
+
context.mode = oldMode;
|
|
108
|
+
|
|
109
|
+
tokenized.push({ selector: selector, body: body });
|
|
110
|
+
} else if (what == 'bodyEnd') {
|
|
111
|
+
// extra closing brace at the top level can be safely ignored
|
|
112
|
+
if (context.mode == 'top') {
|
|
113
|
+
var at = context.cursor;
|
|
114
|
+
var warning = chunk[context.cursor] == '}' ?
|
|
115
|
+
'Unexpected \'}\' in \'' + chunk.substring(at - 20, at + 20) + '\'. Ignoring.' :
|
|
116
|
+
'Unexpected content: \'' + chunk.substring(at, nextSpecial + 1) + '\'. Ignoring.';
|
|
117
|
+
|
|
118
|
+
minifyContext.warnings.push(warning);
|
|
119
|
+
context.cursor = nextSpecial + 1;
|
|
120
|
+
continue;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
if (context.mode != 'block')
|
|
124
|
+
tokenized = chunk.substring(context.cursor, nextSpecial);
|
|
125
|
+
|
|
126
|
+
context.cursor = nextSpecial + 1;
|
|
127
|
+
|
|
128
|
+
break;
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
return tokenized;
|
|
133
|
+
};
|
|
134
|
+
|
|
135
|
+
return {
|
|
136
|
+
process: function() {
|
|
137
|
+
return tokenize();
|
|
138
|
+
}
|
|
139
|
+
};
|
|
140
|
+
};
|
|
141
|
+
|
|
142
|
+
// Divides `data` into chunks of `chunkSize` for faster processing
|
|
143
|
+
var Chunker = function(data, chunkSize) {
|
|
144
|
+
var chunks = [];
|
|
145
|
+
for (var cursor = 0, dataSize = data.length; cursor < dataSize;) {
|
|
146
|
+
var nextCursor = cursor + chunkSize > dataSize ?
|
|
147
|
+
dataSize - 1 :
|
|
148
|
+
cursor + chunkSize;
|
|
149
|
+
|
|
150
|
+
if (data[nextCursor] != '}')
|
|
151
|
+
nextCursor = data.indexOf('}', nextCursor);
|
|
152
|
+
if (nextCursor == -1)
|
|
153
|
+
nextCursor = data.length - 1;
|
|
154
|
+
|
|
155
|
+
chunks.push(data.substring(cursor, nextCursor + 1));
|
|
156
|
+
cursor = nextCursor + 1;
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
return {
|
|
160
|
+
isEmpty: function() {
|
|
161
|
+
return chunks.length === 0;
|
|
162
|
+
},
|
|
163
|
+
|
|
164
|
+
next: function() {
|
|
165
|
+
return chunks.shift() || '';
|
|
166
|
+
}
|
|
167
|
+
};
|
|
168
|
+
};
|