reduce.js
4.57 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
var split = require('../utils/split');
var URL_PREFIX = 'url(';
var UPPERCASE_URL_PREFIX = 'URL(';
var URL_SUFFIX = ')';
var SINGLE_QUOTE_URL_SUFFIX = '\')';
var DOUBLE_QUOTE_URL_SUFFIX = '")';
var DATA_URI_PREFIX_PATTERN = /^\s*['"]?\s*data:/;
var DATA_URI_TRAILER_PATTERN = /[\s\};,\/!]/;
var IMPORT_URL_PREFIX = '@import';
var UPPERCASE_IMPORT_URL_PREFIX = '@IMPORT';
var COMMENT_END_MARKER = /\*\//;
function byUrl(data, context, callback) {
var nextStart = 0;
var nextStartUpperCase = 0;
var nextEnd = 0;
var firstMatch;
var isDataURI = false;
var cursor = 0;
var tempData = [];
var hasUppercaseUrl = data.indexOf(UPPERCASE_URL_PREFIX) > -1;
for (; nextEnd < data.length;) {
nextStart = data.indexOf(URL_PREFIX, nextEnd);
nextStartUpperCase = hasUppercaseUrl ? data.indexOf(UPPERCASE_URL_PREFIX, nextEnd) : -1;
if (nextStart == -1 && nextStartUpperCase == -1)
break;
if (nextStart == -1 && nextStartUpperCase > -1)
nextStart = nextStartUpperCase;
if (data[nextStart + URL_PREFIX.length] == '"') {
nextEnd = data.indexOf(DOUBLE_QUOTE_URL_SUFFIX, nextStart);
} else if (data[nextStart + URL_PREFIX.length] == '\'') {
nextEnd = data.indexOf(SINGLE_QUOTE_URL_SUFFIX, nextStart);
} else {
isDataURI = DATA_URI_PREFIX_PATTERN.test(data.substring(nextStart + URL_PREFIX.length));
if (isDataURI) {
firstMatch = split(data.substring(nextStart), DATA_URI_TRAILER_PATTERN, false, '(', ')', true).pop();
if (firstMatch && firstMatch[firstMatch.length - 1] == URL_SUFFIX) {
nextEnd = nextStart + firstMatch.length - URL_SUFFIX.length;
} else {
nextEnd = -1;
}
} else {
nextEnd = data.indexOf(URL_SUFFIX, nextStart);
}
}
// Following lines are a safety mechanism to ensure
// incorrectly terminated urls are processed correctly.
if (nextEnd == -1) {
nextEnd = data.indexOf('}', nextStart);
if (nextEnd == -1)
nextEnd = data.length;
else
nextEnd--;
context.warnings.push('Broken URL declaration: \'' + data.substring(nextStart, nextEnd + 1) + '\'.');
} else {
if (data[nextEnd] != URL_SUFFIX)
nextEnd = data.indexOf(URL_SUFFIX, nextEnd);
}
tempData.push(data.substring(cursor, nextStart));
var url = data.substring(nextStart, nextEnd + 1);
callback(url, tempData);
cursor = nextEnd + 1;
}
return tempData.length > 0 ?
tempData.join('') + data.substring(cursor, data.length) :
data;
}
function byImport(data, context, callback) {
var nextImport = 0;
var nextImportUpperCase = 0;
var nextStart = 0;
var nextEnd = 0;
var cursor = 0;
var tempData = [];
var nextSingleQuote = 0;
var nextDoubleQuote = 0;
var untilNextQuote;
var withQuote;
var SINGLE_QUOTE = '\'';
var DOUBLE_QUOTE = '"';
for (; nextEnd < data.length;) {
nextImport = data.indexOf(IMPORT_URL_PREFIX, nextEnd);
nextImportUpperCase = data.indexOf(UPPERCASE_IMPORT_URL_PREFIX, nextEnd);
if (nextImport == -1 && nextImportUpperCase == -1)
break;
if (nextImport > -1 && nextImportUpperCase > -1 && nextImportUpperCase < nextImport)
nextImport = nextImportUpperCase;
nextSingleQuote = data.indexOf(SINGLE_QUOTE, nextImport);
nextDoubleQuote = data.indexOf(DOUBLE_QUOTE, nextImport);
if (nextSingleQuote > -1 && nextDoubleQuote > -1 && nextSingleQuote < nextDoubleQuote) {
nextStart = nextSingleQuote;
withQuote = SINGLE_QUOTE;
} else if (nextSingleQuote > -1 && nextDoubleQuote > -1 && nextSingleQuote > nextDoubleQuote) {
nextStart = nextDoubleQuote;
withQuote = DOUBLE_QUOTE;
} else if (nextSingleQuote > -1) {
nextStart = nextSingleQuote;
withQuote = SINGLE_QUOTE;
} else if (nextDoubleQuote > -1) {
nextStart = nextDoubleQuote;
withQuote = DOUBLE_QUOTE;
} else {
break;
}
tempData.push(data.substring(cursor, nextStart));
nextEnd = data.indexOf(withQuote, nextStart + 1);
untilNextQuote = data.substring(nextImport, nextEnd);
if (nextEnd == -1 || /^@import\s+(url\(|__ESCAPED)/i.test(untilNextQuote) || COMMENT_END_MARKER.test(untilNextQuote)) {
cursor = nextStart;
break;
}
var url = data.substring(nextStart, nextEnd + 1);
callback(url, tempData);
cursor = nextEnd + 1;
}
return tempData.length > 0 ?
tempData.join('') + data.substring(cursor, data.length) :
data;
}
function reduceAll(data, context, callback) {
data = byUrl(data, context, callback);
data = byImport(data, context, callback);
return data;
}
module.exports = reduceAll;