ruby-clean-css 0.0.2
Sign up to get free protection for your applications and to get access to all the features.
- 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,64 @@
|
|
1
|
+
module.exports = function HSLToHex(data) {
|
2
|
+
// HSL to RGB converter. Both methods adapted from:
|
3
|
+
// http://mjijackson.com/2008/02/rgb-to-hsl-and-rgb-to-hsv-color-model-conversion-algorithms-in-javascript
|
4
|
+
var hslToRgb = function(h, s, l) {
|
5
|
+
var r, g, b;
|
6
|
+
|
7
|
+
// normalize hue orientation b/w 0 and 360 degrees
|
8
|
+
h = h % 360;
|
9
|
+
if (h < 0)
|
10
|
+
h += 360;
|
11
|
+
h = ~~h / 360;
|
12
|
+
|
13
|
+
if (s < 0)
|
14
|
+
s = 0;
|
15
|
+
else if (s > 100)
|
16
|
+
s = 100;
|
17
|
+
s = ~~s / 100;
|
18
|
+
|
19
|
+
if (l < 0)
|
20
|
+
l = 0;
|
21
|
+
else if (l > 100)
|
22
|
+
l = 100;
|
23
|
+
l = ~~l / 100;
|
24
|
+
|
25
|
+
if (s === 0) {
|
26
|
+
r = g = b = l; // achromatic
|
27
|
+
} else {
|
28
|
+
var q = l < 0.5 ?
|
29
|
+
l * (1 + s) :
|
30
|
+
l + s - l * s;
|
31
|
+
var p = 2 * l - q;
|
32
|
+
r = hueToRgb(p, q, h + 1/3);
|
33
|
+
g = hueToRgb(p, q, h);
|
34
|
+
b = hueToRgb(p, q, h - 1/3);
|
35
|
+
}
|
36
|
+
|
37
|
+
return [~~(r * 255), ~~(g * 255), ~~(b * 255)];
|
38
|
+
};
|
39
|
+
|
40
|
+
var hueToRgb = function(p, q, t) {
|
41
|
+
if (t < 0) t += 1;
|
42
|
+
if (t > 1) t -= 1;
|
43
|
+
if (t < 1/6) return p + (q - p) * 6 * t;
|
44
|
+
if (t < 1/2) return q;
|
45
|
+
if (t < 2/3) return p + (q - p) * (2/3 - t) * 6;
|
46
|
+
return p;
|
47
|
+
};
|
48
|
+
|
49
|
+
return {
|
50
|
+
process: function() {
|
51
|
+
return data.replace(/hsl\((-?\d+),(-?\d+)%?,(-?\d+)%?\)/g, function(match, hue, saturation, lightness) {
|
52
|
+
var asRgb = hslToRgb(hue, saturation, lightness);
|
53
|
+
var redAsHex = asRgb[0].toString(16);
|
54
|
+
var greenAsHex = asRgb[1].toString(16);
|
55
|
+
var blueAsHex = asRgb[2].toString(16);
|
56
|
+
|
57
|
+
return '#' +
|
58
|
+
((redAsHex.length == 1 ? '0' : '') + redAsHex) +
|
59
|
+
((greenAsHex.length == 1 ? '0' : '') + greenAsHex) +
|
60
|
+
((blueAsHex.length == 1 ? '0' : '') + blueAsHex);
|
61
|
+
});
|
62
|
+
}
|
63
|
+
};
|
64
|
+
};
|
@@ -0,0 +1,12 @@
|
|
1
|
+
module.exports = function LongToShortHex(data) {
|
2
|
+
return {
|
3
|
+
process: function() {
|
4
|
+
return data.replace(/([,: \(])#([0-9a-f]{6})/gi, function(match, prefix, color) {
|
5
|
+
if (color[0] == color[1] && color[2] == color[3] && color[4] == color[5])
|
6
|
+
return prefix + '#' + color[0] + color[2] + color[4];
|
7
|
+
else
|
8
|
+
return prefix + '#' + color;
|
9
|
+
});
|
10
|
+
}
|
11
|
+
};
|
12
|
+
};
|
@@ -0,0 +1,14 @@
|
|
1
|
+
module.exports = function RGBToHex(data) {
|
2
|
+
return {
|
3
|
+
process: function() {
|
4
|
+
return data.replace(/rgb\((\-?\d+),(\-?\d+),(\-?\d+)\)/g, function(match, red, green, blue) {
|
5
|
+
red = Math.max(0, Math.min(~~red, 255));
|
6
|
+
green = Math.max(0, Math.min(~~green, 255));
|
7
|
+
blue = Math.max(0, Math.min(~~blue, 255));
|
8
|
+
|
9
|
+
// Credit: Asen http://jsbin.com/UPUmaGOc/2/edit?js,console
|
10
|
+
return '#' + ('00000' + (red << 16 | green << 8 | blue).toString(16)).slice(-6);
|
11
|
+
});
|
12
|
+
}
|
13
|
+
};
|
14
|
+
};
|
@@ -0,0 +1,174 @@
|
|
1
|
+
module.exports = function Shortener(data) {
|
2
|
+
var COLORS = {
|
3
|
+
aliceblue: '#f0f8ff',
|
4
|
+
antiquewhite: '#faebd7',
|
5
|
+
aqua: '#0ff',
|
6
|
+
aquamarine: '#7fffd4',
|
7
|
+
azure: '#f0ffff',
|
8
|
+
beige: '#f5f5dc',
|
9
|
+
bisque: '#ffe4c4',
|
10
|
+
black: '#000',
|
11
|
+
blanchedalmond: '#ffebcd',
|
12
|
+
blue: '#00f',
|
13
|
+
blueviolet: '#8a2be2',
|
14
|
+
brown: '#a52a2a',
|
15
|
+
burlywood: '#deb887',
|
16
|
+
cadetblue: '#5f9ea0',
|
17
|
+
chartreuse: '#7fff00',
|
18
|
+
chocolate: '#d2691e',
|
19
|
+
coral: '#ff7f50',
|
20
|
+
cornflowerblue: '#6495ed',
|
21
|
+
cornsilk: '#fff8dc',
|
22
|
+
crimson: '#dc143c',
|
23
|
+
cyan: '#0ff',
|
24
|
+
darkblue: '#00008b',
|
25
|
+
darkcyan: '#008b8b',
|
26
|
+
darkgoldenrod: '#b8860b',
|
27
|
+
darkgray: '#a9a9a9',
|
28
|
+
darkgreen: '#006400',
|
29
|
+
darkkhaki: '#bdb76b',
|
30
|
+
darkmagenta: '#8b008b',
|
31
|
+
darkolivegreen: '#556b2f',
|
32
|
+
darkorange: '#ff8c00',
|
33
|
+
darkorchid: '#9932cc',
|
34
|
+
darkred: '#8b0000',
|
35
|
+
darksalmon: '#e9967a',
|
36
|
+
darkseagreen: '#8fbc8f',
|
37
|
+
darkslateblue: '#483d8b',
|
38
|
+
darkslategray: '#2f4f4f',
|
39
|
+
darkturquoise: '#00ced1',
|
40
|
+
darkviolet: '#9400d3',
|
41
|
+
deeppink: '#ff1493',
|
42
|
+
deepskyblue: '#00bfff',
|
43
|
+
dimgray: '#696969',
|
44
|
+
dodgerblue: '#1e90ff',
|
45
|
+
firebrick: '#b22222',
|
46
|
+
floralwhite: '#fffaf0',
|
47
|
+
forestgreen: '#228b22',
|
48
|
+
fuchsia: '#f0f',
|
49
|
+
gainsboro: '#dcdcdc',
|
50
|
+
ghostwhite: '#f8f8ff',
|
51
|
+
gold: '#ffd700',
|
52
|
+
goldenrod: '#daa520',
|
53
|
+
gray: '#808080',
|
54
|
+
green: '#008000',
|
55
|
+
greenyellow: '#adff2f',
|
56
|
+
honeydew: '#f0fff0',
|
57
|
+
hotpink: '#ff69b4',
|
58
|
+
indianred: '#cd5c5c',
|
59
|
+
indigo: '#4b0082',
|
60
|
+
ivory: '#fffff0',
|
61
|
+
khaki: '#f0e68c',
|
62
|
+
lavender: '#e6e6fa',
|
63
|
+
lavenderblush: '#fff0f5',
|
64
|
+
lawngreen: '#7cfc00',
|
65
|
+
lemonchiffon: '#fffacd',
|
66
|
+
lightblue: '#add8e6',
|
67
|
+
lightcoral: '#f08080',
|
68
|
+
lightcyan: '#e0ffff',
|
69
|
+
lightgoldenrodyellow: '#fafad2',
|
70
|
+
lightgray: '#d3d3d3',
|
71
|
+
lightgreen: '#90ee90',
|
72
|
+
lightpink: '#ffb6c1',
|
73
|
+
lightsalmon: '#ffa07a',
|
74
|
+
lightseagreen: '#20b2aa',
|
75
|
+
lightskyblue: '#87cefa',
|
76
|
+
lightslategray: '#778899',
|
77
|
+
lightsteelblue: '#b0c4de',
|
78
|
+
lightyellow: '#ffffe0',
|
79
|
+
lime: '#0f0',
|
80
|
+
limegreen: '#32cd32',
|
81
|
+
linen: '#faf0e6',
|
82
|
+
magenta: '#ff00ff',
|
83
|
+
maroon: '#800000',
|
84
|
+
mediumaquamarine: '#66cdaa',
|
85
|
+
mediumblue: '#0000cd',
|
86
|
+
mediumorchid: '#ba55d3',
|
87
|
+
mediumpurple: '#9370db',
|
88
|
+
mediumseagreen: '#3cb371',
|
89
|
+
mediumslateblue: '#7b68ee',
|
90
|
+
mediumspringgreen: '#00fa9a',
|
91
|
+
mediumturquoise: '#48d1cc',
|
92
|
+
mediumvioletred: '#c71585',
|
93
|
+
midnightblue: '#191970',
|
94
|
+
mintcream: '#f5fffa',
|
95
|
+
mistyrose: '#ffe4e1',
|
96
|
+
moccasin: '#ffe4b5',
|
97
|
+
navajowhite: '#ffdead',
|
98
|
+
navy: '#000080',
|
99
|
+
oldlace: '#fdf5e6',
|
100
|
+
olive: '#808000',
|
101
|
+
olivedrab: '#6b8e23',
|
102
|
+
orange: '#ffa500',
|
103
|
+
orangered: '#ff4500',
|
104
|
+
orchid: '#da70d6',
|
105
|
+
palegoldenrod: '#eee8aa',
|
106
|
+
palegreen: '#98fb98',
|
107
|
+
paleturquoise: '#afeeee',
|
108
|
+
palevioletred: '#db7093',
|
109
|
+
papayawhip: '#ffefd5',
|
110
|
+
peachpuff: '#ffdab9',
|
111
|
+
peru: '#cd853f',
|
112
|
+
pink: '#ffc0cb',
|
113
|
+
plum: '#dda0dd',
|
114
|
+
powderblue: '#b0e0e6',
|
115
|
+
purple: '#800080',
|
116
|
+
red: '#f00',
|
117
|
+
rosybrown: '#bc8f8f',
|
118
|
+
royalblue: '#4169e1',
|
119
|
+
saddlebrown: '#8b4513',
|
120
|
+
salmon: '#fa8072',
|
121
|
+
sandybrown: '#f4a460',
|
122
|
+
seagreen: '#2e8b57',
|
123
|
+
seashell: '#fff5ee',
|
124
|
+
sienna: '#a0522d',
|
125
|
+
silver: '#c0c0c0',
|
126
|
+
skyblue: '#87ceeb',
|
127
|
+
slateblue: '#6a5acd',
|
128
|
+
slategray: '#708090',
|
129
|
+
snow: '#fffafa',
|
130
|
+
springgreen: '#00ff7f',
|
131
|
+
steelblue: '#4682b4',
|
132
|
+
tan: '#d2b48c',
|
133
|
+
teal: '#008080',
|
134
|
+
thistle: '#d8bfd8',
|
135
|
+
tomato: '#ff6347',
|
136
|
+
turquoise: '#40e0d0',
|
137
|
+
violet: '#ee82ee',
|
138
|
+
wheat: '#f5deb3',
|
139
|
+
white: '#fff',
|
140
|
+
whitesmoke: '#f5f5f5',
|
141
|
+
yellow: '#ff0',
|
142
|
+
yellowgreen: '#9acd32'
|
143
|
+
};
|
144
|
+
|
145
|
+
var toHex = {};
|
146
|
+
var toName = {};
|
147
|
+
|
148
|
+
for (var name in COLORS) {
|
149
|
+
var color = COLORS[name];
|
150
|
+
if (name.length < color.length)
|
151
|
+
toName[color] = name;
|
152
|
+
else
|
153
|
+
toHex[name] = color;
|
154
|
+
}
|
155
|
+
|
156
|
+
return {
|
157
|
+
toHex: toHex,
|
158
|
+
toName: toName,
|
159
|
+
|
160
|
+
// replace color name with hex values if shorter (or the other way around)
|
161
|
+
process: function() {
|
162
|
+
[toHex, toName].forEach(function(conversion) {
|
163
|
+
var pattern = '(' + Object.keys(conversion).join('|') + ')';
|
164
|
+
var colorSwitcher = function(match, prefix, colorValue, suffix) {
|
165
|
+
return prefix + conversion[colorValue.toLowerCase()] + suffix;
|
166
|
+
};
|
167
|
+
data = data.replace(new RegExp('([ :,\\(])' + pattern + '([;\\}!\\) ])', 'ig'), colorSwitcher);
|
168
|
+
data = data.replace(new RegExp('(,)' + pattern + '(,)', 'ig'), colorSwitcher);
|
169
|
+
});
|
170
|
+
|
171
|
+
return data;
|
172
|
+
}
|
173
|
+
};
|
174
|
+
};
|
@@ -0,0 +1,32 @@
|
|
1
|
+
var path = require('path');
|
2
|
+
|
3
|
+
var UrlRewriter = require('./url-rewriter');
|
4
|
+
|
5
|
+
module.exports = function UrlRebase(options, context) {
|
6
|
+
var process = function(data) {
|
7
|
+
var rebaseOpts = {
|
8
|
+
absolute: !!options.root,
|
9
|
+
relative: !options.root && !!options.target,
|
10
|
+
fromBase: options.relativeTo
|
11
|
+
};
|
12
|
+
|
13
|
+
if (!rebaseOpts.absolute && !rebaseOpts.relative)
|
14
|
+
return data;
|
15
|
+
|
16
|
+
if (rebaseOpts.absolute && !!options.target)
|
17
|
+
context.warnings.push('Both \'root\' and output file given so rebasing URLs as absolute paths');
|
18
|
+
|
19
|
+
if (rebaseOpts.absolute)
|
20
|
+
rebaseOpts.toBase = path.resolve(options.root);
|
21
|
+
|
22
|
+
if (rebaseOpts.relative)
|
23
|
+
rebaseOpts.toBase = path.resolve(path.dirname(options.target));
|
24
|
+
|
25
|
+
if (!rebaseOpts.fromBase || !rebaseOpts.toBase)
|
26
|
+
return data;
|
27
|
+
|
28
|
+
return UrlRewriter.process(data, rebaseOpts);
|
29
|
+
};
|
30
|
+
|
31
|
+
return { process: process };
|
32
|
+
};
|
@@ -0,0 +1,60 @@
|
|
1
|
+
var path = require('path');
|
2
|
+
var url = require('url');
|
3
|
+
|
4
|
+
module.exports = {
|
5
|
+
process: function(data, options) {
|
6
|
+
var tempData = [];
|
7
|
+
var nextStart = 0;
|
8
|
+
var nextEnd = 0;
|
9
|
+
var cursor = 0;
|
10
|
+
|
11
|
+
for (; nextEnd < data.length;) {
|
12
|
+
nextStart = data.indexOf('url(', nextEnd);
|
13
|
+
if (nextStart == -1)
|
14
|
+
break;
|
15
|
+
|
16
|
+
nextEnd = data.indexOf(')', nextStart + 4);
|
17
|
+
if (nextEnd == -1)
|
18
|
+
break;
|
19
|
+
|
20
|
+
tempData.push(data.substring(cursor, nextStart));
|
21
|
+
var url = data.substring(nextStart + 4, nextEnd);
|
22
|
+
if (!/\/\*|\*\//.test(url))
|
23
|
+
url = url.replace(/['"]/g, '');
|
24
|
+
|
25
|
+
tempData.push('url(' + this._rebased(url, options) + ')');
|
26
|
+
cursor = nextEnd + 1;
|
27
|
+
}
|
28
|
+
|
29
|
+
return tempData.length > 0 ?
|
30
|
+
tempData.join('') + data.substring(cursor, data.length) :
|
31
|
+
data;
|
32
|
+
},
|
33
|
+
|
34
|
+
_rebased: function(resource, options) {
|
35
|
+
var specialUrl = resource[0] == '/' ||
|
36
|
+
resource.substring(resource.length - 4) == '.css' ||
|
37
|
+
resource.indexOf('data:') === 0 ||
|
38
|
+
/^https?:\/\//.exec(resource) !== null ||
|
39
|
+
/__\w+__/.exec(resource) !== null;
|
40
|
+
var rebased;
|
41
|
+
|
42
|
+
if (specialUrl)
|
43
|
+
return resource;
|
44
|
+
|
45
|
+
if (/https?:\/\//.test(options.toBase))
|
46
|
+
return url.resolve(options.toBase, resource);
|
47
|
+
|
48
|
+
if (options.absolute) {
|
49
|
+
rebased = path
|
50
|
+
.resolve(path.join(options.fromBase, resource))
|
51
|
+
.replace(options.toBase, '');
|
52
|
+
} else {
|
53
|
+
rebased = path.relative(options.toBase, path.join(options.fromBase, resource));
|
54
|
+
}
|
55
|
+
|
56
|
+
return process.platform == 'win32' ?
|
57
|
+
rebased.replace(/\\/g, '/') :
|
58
|
+
rebased;
|
59
|
+
}
|
60
|
+
};
|
@@ -0,0 +1,319 @@
|
|
1
|
+
var fs = require('fs');
|
2
|
+
var path = require('path');
|
3
|
+
var http = require('http');
|
4
|
+
var https = require('https');
|
5
|
+
var url = require('url');
|
6
|
+
|
7
|
+
var UrlRewriter = require('../images/url-rewriter');
|
8
|
+
|
9
|
+
var merge = function(source1, source2) {
|
10
|
+
var target = {};
|
11
|
+
for (var key1 in source1)
|
12
|
+
target[key1] = source1[key1];
|
13
|
+
for (var key2 in source2)
|
14
|
+
target[key2] = source2[key2];
|
15
|
+
|
16
|
+
return target;
|
17
|
+
};
|
18
|
+
|
19
|
+
module.exports = function Inliner(context, options) {
|
20
|
+
var defaultOptions = {
|
21
|
+
timeout: 5000,
|
22
|
+
request: {}
|
23
|
+
};
|
24
|
+
var inlinerOptions = merge(defaultOptions, options || {});
|
25
|
+
|
26
|
+
var process = function(data, options) {
|
27
|
+
if (options.shallow) {
|
28
|
+
options.shallow = false;
|
29
|
+
options._shared.done.push(data);
|
30
|
+
return processNext(options);
|
31
|
+
}
|
32
|
+
|
33
|
+
options._shared = options._shared || {
|
34
|
+
done: [],
|
35
|
+
left: []
|
36
|
+
};
|
37
|
+
var shared = options._shared;
|
38
|
+
|
39
|
+
var nextStart = 0;
|
40
|
+
var nextEnd = 0;
|
41
|
+
var cursor = 0;
|
42
|
+
var isComment = commentScanner(data);
|
43
|
+
var afterContent = contentScanner(data);
|
44
|
+
|
45
|
+
options.relativeTo = options.relativeTo || options.root;
|
46
|
+
options._baseRelativeTo = options._baseRelativeTo || options.relativeTo;
|
47
|
+
options.visited = options.visited || [];
|
48
|
+
|
49
|
+
for (; nextEnd < data.length;) {
|
50
|
+
nextStart = data.indexOf('@import', cursor);
|
51
|
+
if (nextStart == -1)
|
52
|
+
break;
|
53
|
+
|
54
|
+
if (isComment(nextStart)) {
|
55
|
+
cursor = nextStart + 1;
|
56
|
+
continue;
|
57
|
+
}
|
58
|
+
|
59
|
+
nextEnd = data.indexOf(';', nextStart);
|
60
|
+
if (nextEnd == -1) {
|
61
|
+
cursor = data.length;
|
62
|
+
data = '';
|
63
|
+
break;
|
64
|
+
}
|
65
|
+
|
66
|
+
shared.done.push(data.substring(0, nextStart));
|
67
|
+
shared.left.unshift([data.substring(nextEnd + 1), options]);
|
68
|
+
|
69
|
+
return afterContent(nextStart) ?
|
70
|
+
processNext(options) :
|
71
|
+
inline(data, nextStart, nextEnd, options);
|
72
|
+
}
|
73
|
+
|
74
|
+
// no @import matched in current data
|
75
|
+
shared.done.push(data);
|
76
|
+
return processNext(options);
|
77
|
+
};
|
78
|
+
|
79
|
+
var processNext = function(options) {
|
80
|
+
if (options._shared.left.length > 0)
|
81
|
+
return process.apply(null, options._shared.left.shift());
|
82
|
+
else
|
83
|
+
return options.whenDone(options._shared.done.join(''));
|
84
|
+
};
|
85
|
+
|
86
|
+
var commentScanner = function(data) {
|
87
|
+
var commentRegex = /(\/\*(?!\*\/)[\s\S]*?\*\/)/;
|
88
|
+
var lastStartIndex = 0;
|
89
|
+
var lastEndIndex = 0;
|
90
|
+
var noComments = false;
|
91
|
+
|
92
|
+
// test whether an index is located within a comment
|
93
|
+
var scanner = function(idx) {
|
94
|
+
var comment;
|
95
|
+
var localStartIndex = 0;
|
96
|
+
var localEndIndex = 0;
|
97
|
+
var globalStartIndex = 0;
|
98
|
+
var globalEndIndex = 0;
|
99
|
+
|
100
|
+
// return if we know there are no more comments
|
101
|
+
if (noComments)
|
102
|
+
return false;
|
103
|
+
|
104
|
+
// idx can be still within last matched comment (many @import statements inside one comment)
|
105
|
+
if (idx > lastStartIndex && idx < lastEndIndex)
|
106
|
+
return true;
|
107
|
+
|
108
|
+
comment = data.match(commentRegex);
|
109
|
+
|
110
|
+
if (!comment) {
|
111
|
+
noComments = true;
|
112
|
+
return false;
|
113
|
+
}
|
114
|
+
|
115
|
+
// get the indexes relative to the current data chunk
|
116
|
+
lastStartIndex = localStartIndex = comment.index;
|
117
|
+
localEndIndex = localStartIndex + comment[0].length;
|
118
|
+
|
119
|
+
// calculate the indexes relative to the full original data
|
120
|
+
globalEndIndex = localEndIndex + lastEndIndex;
|
121
|
+
globalStartIndex = globalEndIndex - comment[0].length;
|
122
|
+
|
123
|
+
// chop off data up to and including current comment block
|
124
|
+
data = data.substring(localEndIndex);
|
125
|
+
lastEndIndex = globalEndIndex;
|
126
|
+
|
127
|
+
// re-run scan if comment ended before the idx
|
128
|
+
if (globalEndIndex < idx)
|
129
|
+
return scanner(idx);
|
130
|
+
|
131
|
+
return globalEndIndex > idx && idx > globalStartIndex;
|
132
|
+
};
|
133
|
+
|
134
|
+
return scanner;
|
135
|
+
};
|
136
|
+
|
137
|
+
var contentScanner = function(data) {
|
138
|
+
var isComment = commentScanner(data);
|
139
|
+
var firstContentIdx = -1;
|
140
|
+
while (true) {
|
141
|
+
firstContentIdx = data.indexOf('{', firstContentIdx + 1);
|
142
|
+
if (firstContentIdx == -1 || !isComment(firstContentIdx))
|
143
|
+
break;
|
144
|
+
}
|
145
|
+
|
146
|
+
return function(idx) {
|
147
|
+
return firstContentIdx > -1 ?
|
148
|
+
idx > firstContentIdx :
|
149
|
+
false;
|
150
|
+
};
|
151
|
+
};
|
152
|
+
|
153
|
+
var inline = function(data, nextStart, nextEnd, options) {
|
154
|
+
options.shallow = data.indexOf('@shallow') > 0;
|
155
|
+
|
156
|
+
var importDeclaration = data
|
157
|
+
.substring(data.indexOf(' ', nextStart) + 1, nextEnd)
|
158
|
+
.replace(/@shallow\)$/, ')')
|
159
|
+
.trim();
|
160
|
+
|
161
|
+
var viaUrl = importDeclaration.indexOf('url(') === 0;
|
162
|
+
var urlStartsAt = viaUrl ? 4 : 0;
|
163
|
+
var isQuoted = /^['"]/.exec(importDeclaration.substring(urlStartsAt, urlStartsAt + 2));
|
164
|
+
var urlEndsAt = isQuoted ?
|
165
|
+
importDeclaration.indexOf(isQuoted[0], urlStartsAt + 1) :
|
166
|
+
importDeclaration.split(' ')[0].length;
|
167
|
+
|
168
|
+
var importedFile = importDeclaration
|
169
|
+
.substring(urlStartsAt, urlEndsAt)
|
170
|
+
.replace(/['"]/g, '')
|
171
|
+
.replace(/\)$/, '')
|
172
|
+
.trim();
|
173
|
+
|
174
|
+
var mediaQuery = importDeclaration
|
175
|
+
.substring(urlEndsAt + 1)
|
176
|
+
.replace(/^\)/, '')
|
177
|
+
.trim();
|
178
|
+
|
179
|
+
var isRemote = options.isRemote ||
|
180
|
+
/^(http|https):\/\//.test(importedFile) ||
|
181
|
+
/^\/\//.test(importedFile);
|
182
|
+
|
183
|
+
if (options.localOnly && isRemote) {
|
184
|
+
context.warnings.push('Ignoring remote @import declaration of "' + importedFile + '" as no callback given.');
|
185
|
+
restoreImport(importedFile, mediaQuery, options);
|
186
|
+
|
187
|
+
return processNext(options);
|
188
|
+
}
|
189
|
+
|
190
|
+
var method = isRemote ? inlineRemoteResource : inlineLocalResource;
|
191
|
+
return method(importedFile, mediaQuery, options);
|
192
|
+
};
|
193
|
+
|
194
|
+
var inlineRemoteResource = function(importedFile, mediaQuery, options) {
|
195
|
+
var importedUrl = /^https?:\/\//.test(importedFile) ?
|
196
|
+
importedFile :
|
197
|
+
url.resolve(options.relativeTo, importedFile);
|
198
|
+
|
199
|
+
if (importedUrl.indexOf('//') === 0)
|
200
|
+
importedUrl = 'http:' + importedUrl;
|
201
|
+
|
202
|
+
if (options.visited.indexOf(importedUrl) > -1)
|
203
|
+
return processNext(options);
|
204
|
+
|
205
|
+
|
206
|
+
if (context.debug)
|
207
|
+
console.error('Inlining remote stylesheet: ' + importedUrl);
|
208
|
+
|
209
|
+
options.visited.push(importedUrl);
|
210
|
+
|
211
|
+
var get = importedUrl.indexOf('http://') === 0 ?
|
212
|
+
http.get :
|
213
|
+
https.get;
|
214
|
+
|
215
|
+
var timedOut = false;
|
216
|
+
var handleError = function(message) {
|
217
|
+
context.errors.push('Broken @import declaration of "' + importedUrl + '" - ' + message);
|
218
|
+
restoreImport(importedUrl, mediaQuery, options);
|
219
|
+
|
220
|
+
processNext(options);
|
221
|
+
};
|
222
|
+
var requestOptions = merge(url.parse(importedUrl), inlinerOptions.request);
|
223
|
+
|
224
|
+
get(requestOptions, function(res) {
|
225
|
+
if (res.statusCode < 200 || res.statusCode > 399) {
|
226
|
+
return handleError('error ' + res.statusCode);
|
227
|
+
} else if (res.statusCode > 299) {
|
228
|
+
var movedUrl = url.resolve(importedUrl, res.headers.location);
|
229
|
+
return inlineRemoteResource(movedUrl, mediaQuery, options);
|
230
|
+
}
|
231
|
+
|
232
|
+
var chunks = [];
|
233
|
+
var parsedUrl = url.parse(importedUrl);
|
234
|
+
res.on('data', function(chunk) {
|
235
|
+
chunks.push(chunk.toString());
|
236
|
+
});
|
237
|
+
res.on('end', function() {
|
238
|
+
var importedData = chunks.join('');
|
239
|
+
importedData = UrlRewriter.process(importedData, { toBase: importedUrl });
|
240
|
+
|
241
|
+
if (mediaQuery.length > 0)
|
242
|
+
importedData = '@media ' + mediaQuery + '{' + importedData + '}';
|
243
|
+
|
244
|
+
process(importedData, {
|
245
|
+
isRemote: true,
|
246
|
+
relativeTo: parsedUrl.protocol + '//' + parsedUrl.host,
|
247
|
+
_shared: options._shared,
|
248
|
+
whenDone: options.whenDone,
|
249
|
+
visited: options.visited,
|
250
|
+
shallow: options.shallow
|
251
|
+
});
|
252
|
+
});
|
253
|
+
})
|
254
|
+
.on('error', function(res) {
|
255
|
+
handleError(res.message);
|
256
|
+
})
|
257
|
+
.on('timeout', function() {
|
258
|
+
// FIX: node 0.8 fires this event twice
|
259
|
+
if (timedOut)
|
260
|
+
return;
|
261
|
+
|
262
|
+
handleError('timeout');
|
263
|
+
timedOut = true;
|
264
|
+
})
|
265
|
+
.setTimeout(inlinerOptions.timeout);
|
266
|
+
};
|
267
|
+
|
268
|
+
var inlineLocalResource = function(importedFile, mediaQuery, options) {
|
269
|
+
var relativeTo = importedFile[0] == '/' ?
|
270
|
+
options.root :
|
271
|
+
options.relativeTo;
|
272
|
+
|
273
|
+
var fullPath = path.resolve(path.join(relativeTo, importedFile));
|
274
|
+
|
275
|
+
if (!fs.existsSync(fullPath) || !fs.statSync(fullPath).isFile()) {
|
276
|
+
context.errors.push('Broken @import declaration of "' + importedFile + '"');
|
277
|
+
return processNext(options);
|
278
|
+
}
|
279
|
+
|
280
|
+
if (options.visited.indexOf(fullPath) > -1)
|
281
|
+
return processNext(options);
|
282
|
+
|
283
|
+
|
284
|
+
if (context.debug)
|
285
|
+
console.error('Inlining local stylesheet: ' + fullPath);
|
286
|
+
|
287
|
+
options.visited.push(fullPath);
|
288
|
+
|
289
|
+
var importedData = fs.readFileSync(fullPath, 'utf8');
|
290
|
+
var importRelativeTo = path.dirname(fullPath);
|
291
|
+
importedData = UrlRewriter.process(importedData, {
|
292
|
+
relative: true,
|
293
|
+
fromBase: importRelativeTo,
|
294
|
+
toBase: options._baseRelativeTo
|
295
|
+
});
|
296
|
+
|
297
|
+
if (mediaQuery.length > 0)
|
298
|
+
importedData = '@media ' + mediaQuery + '{' + importedData + '}';
|
299
|
+
|
300
|
+
return process(importedData, {
|
301
|
+
root: options.root,
|
302
|
+
relativeTo: importRelativeTo,
|
303
|
+
_baseRelativeTo: options._baseRelativeTo,
|
304
|
+
_shared: options._shared,
|
305
|
+
visited: options.visited,
|
306
|
+
whenDone: options.whenDone,
|
307
|
+
localOnly: options.localOnly,
|
308
|
+
shallow: options.shallow
|
309
|
+
});
|
310
|
+
};
|
311
|
+
|
312
|
+
var restoreImport = function(importedUrl, mediaQuery, options) {
|
313
|
+
var restoredImport = '@import url(' + importedUrl + ')' + (mediaQuery.length > 0 ? ' ' + mediaQuery : '') + ';';
|
314
|
+
options._shared.done.push(restoredImport);
|
315
|
+
};
|
316
|
+
|
317
|
+
// Inlines all imports taking care of repetitions, unknown files, and circular dependencies
|
318
|
+
return { process: process };
|
319
|
+
};
|