source: imaps-frontend/node_modules/clean-css/lib/optimizer/level-2/restructure.js@ 79a0317

main
Last change on this file since 79a0317 was 79a0317, checked in by stefan toskovski <stefantoska84@…>, 4 days ago

F4 Finalna Verzija

  • Property mode set to 100644
File size: 12.4 KB
Line 
1var canReorderSingle = require('./reorderable').canReorderSingle;
2var extractProperties = require('./extract-properties');
3var isMergeable = require('./is-mergeable');
4var tidyRuleDuplicates = require('./tidy-rule-duplicates');
5
6var Token = require('../../tokenizer/token');
7
8var cloneArray = require('../../utils/clone-array');
9
10var serializeBody = require('../../writer/one-time').body;
11var serializeRules = require('../../writer/one-time').rules;
12
13function naturalSorter(a, b) {
14 return a > b ? 1 : -1;
15}
16
17function cloneAndMergeSelectors(propertyA, propertyB) {
18 var cloned = cloneArray(propertyA);
19 cloned[5] = cloned[5].concat(propertyB[5]);
20
21 return cloned;
22}
23
24function restructure(tokens, context) {
25 var options = context.options;
26 var mergeablePseudoClasses = options.compatibility.selectors.mergeablePseudoClasses;
27 var mergeablePseudoElements = options.compatibility.selectors.mergeablePseudoElements;
28 var mergeLimit = options.compatibility.selectors.mergeLimit;
29 var multiplePseudoMerging = options.compatibility.selectors.multiplePseudoMerging;
30 var specificityCache = context.cache.specificity;
31 var movableTokens = {};
32 var movedProperties = [];
33 var multiPropertyMoveCache = {};
34 var movedToBeDropped = [];
35 var maxCombinationsLevel = 2;
36 var ID_JOIN_CHARACTER = '%';
37
38 function sendToMultiPropertyMoveCache(position, movedProperty, allFits) {
39 for (var i = allFits.length - 1; i >= 0; i--) {
40 var fit = allFits[i][0];
41 var id = addToCache(movedProperty, fit);
42
43 if (multiPropertyMoveCache[id].length > 1 && processMultiPropertyMove(position, multiPropertyMoveCache[id])) {
44 removeAllMatchingFromCache(id);
45 break;
46 }
47 }
48 }
49
50 function addToCache(movedProperty, fit) {
51 var id = cacheId(fit);
52 multiPropertyMoveCache[id] = multiPropertyMoveCache[id] || [];
53 multiPropertyMoveCache[id].push([movedProperty, fit]);
54 return id;
55 }
56
57 function removeAllMatchingFromCache(matchId) {
58 var matchSelectors = matchId.split(ID_JOIN_CHARACTER);
59 var forRemoval = [];
60 var i;
61
62 for (var id in multiPropertyMoveCache) {
63 var selectors = id.split(ID_JOIN_CHARACTER);
64 for (i = selectors.length - 1; i >= 0; i--) {
65 if (matchSelectors.indexOf(selectors[i]) > -1) {
66 forRemoval.push(id);
67 break;
68 }
69 }
70 }
71
72 for (i = forRemoval.length - 1; i >= 0; i--) {
73 delete multiPropertyMoveCache[forRemoval[i]];
74 }
75 }
76
77 function cacheId(cachedTokens) {
78 var id = [];
79 for (var i = 0, l = cachedTokens.length; i < l; i++) {
80 id.push(serializeRules(cachedTokens[i][1]));
81 }
82 return id.join(ID_JOIN_CHARACTER);
83 }
84
85 function tokensToMerge(sourceTokens) {
86 var uniqueTokensWithBody = [];
87 var mergeableTokens = [];
88
89 for (var i = sourceTokens.length - 1; i >= 0; i--) {
90 if (!isMergeable(
91 serializeRules(sourceTokens[i][1]),
92 mergeablePseudoClasses,
93 mergeablePseudoElements,
94 multiplePseudoMerging
95 )) {
96 continue;
97 }
98
99 mergeableTokens.unshift(sourceTokens[i]);
100 if (sourceTokens[i][2].length > 0
101 && uniqueTokensWithBody.indexOf(sourceTokens[i]) == -1) {
102 uniqueTokensWithBody.push(sourceTokens[i]);
103 }
104 }
105
106 return uniqueTokensWithBody.length > 1
107 ? mergeableTokens
108 : [];
109 }
110
111 function shortenIfPossible(position, movedProperty) {
112 var name = movedProperty[0];
113 var value = movedProperty[1];
114 var key = movedProperty[4];
115 var valueSize = name.length + value.length + 1;
116 var allSelectors = [];
117 var qualifiedTokens = [];
118
119 var mergeableTokens = tokensToMerge(movableTokens[key]);
120 if (mergeableTokens.length < 2) { return; }
121
122 var allFits = findAllFits(mergeableTokens, valueSize, 1);
123 var bestFit = allFits[0];
124 if (bestFit[1] > 0) { return sendToMultiPropertyMoveCache(position, movedProperty, allFits); }
125
126 for (var i = bestFit[0].length - 1; i >= 0; i--) {
127 allSelectors = bestFit[0][i][1].concat(allSelectors);
128 qualifiedTokens.unshift(bestFit[0][i]);
129 }
130
131 allSelectors = tidyRuleDuplicates(allSelectors);
132 dropAsNewTokenAt(position, [movedProperty], allSelectors, qualifiedTokens);
133 }
134
135 function fitSorter(fit1, fit2) {
136 return fit1[1] > fit2[1] ? 1 : (fit1[1] == fit2[1] ? 0 : -1);
137 }
138
139 function findAllFits(mergeableTokens, propertySize, propertiesCount) {
140 var combinations = allCombinations(mergeableTokens, propertySize, propertiesCount, maxCombinationsLevel - 1);
141 return combinations.sort(fitSorter);
142 }
143
144 function allCombinations(tokensVariant, propertySize, propertiesCount, level) {
145 var differenceVariants = [[tokensVariant, sizeDifference(tokensVariant, propertySize, propertiesCount)]];
146 if (tokensVariant.length > 2 && level > 0) {
147 for (var i = tokensVariant.length - 1; i >= 0; i--) {
148 var subVariant = Array.prototype.slice.call(tokensVariant, 0);
149 subVariant.splice(i, 1);
150 differenceVariants = differenceVariants.concat(
151 allCombinations(subVariant, propertySize, propertiesCount, level - 1)
152 );
153 }
154 }
155
156 return differenceVariants;
157 }
158
159 function sizeDifference(tokensVariant, propertySize, propertiesCount) {
160 var allSelectorsSize = 0;
161 for (var i = tokensVariant.length - 1; i >= 0; i--) {
162 allSelectorsSize += tokensVariant[i][2].length > propertiesCount
163 ? serializeRules(tokensVariant[i][1]).length
164 : -1;
165 }
166 return allSelectorsSize - (tokensVariant.length - 1) * propertySize + 1;
167 }
168
169 function dropAsNewTokenAt(position, properties, allSelectors, mergeableTokens) {
170 var i, j, k, m;
171 var allProperties = [];
172
173 for (i = mergeableTokens.length - 1; i >= 0; i--) {
174 var mergeableToken = mergeableTokens[i];
175
176 for (j = mergeableToken[2].length - 1; j >= 0; j--) {
177 var mergeableProperty = mergeableToken[2][j];
178
179 for (k = 0, m = properties.length; k < m; k++) {
180 var property = properties[k];
181
182 var mergeablePropertyName = mergeableProperty[1][1];
183 var propertyName = property[0];
184 var propertyBody = property[4];
185 if (mergeablePropertyName == propertyName && serializeBody([mergeableProperty]) == propertyBody) {
186 mergeableToken[2].splice(j, 1);
187 break;
188 }
189 }
190 }
191 }
192
193 for (i = properties.length - 1; i >= 0; i--) {
194 allProperties.unshift(properties[i][3]);
195 }
196
197 var newToken = [Token.RULE, allSelectors, allProperties];
198 tokens.splice(position, 0, newToken);
199 }
200
201 function dropPropertiesAt(position, movedProperty) {
202 var key = movedProperty[4];
203 var toMove = movableTokens[key];
204
205 if (toMove && toMove.length > 1) {
206 if (!shortenMultiMovesIfPossible(position, movedProperty)) { shortenIfPossible(position, movedProperty); }
207 }
208 }
209
210 function shortenMultiMovesIfPossible(position, movedProperty) {
211 var candidates = [];
212 var propertiesAndMergableTokens = [];
213 var key = movedProperty[4];
214 var j, k;
215
216 var mergeableTokens = tokensToMerge(movableTokens[key]);
217 if (mergeableTokens.length < 2) { return; }
218
219 movableLoop:
220 for (var value in movableTokens) {
221 var tokensList = movableTokens[value];
222
223 for (j = mergeableTokens.length - 1; j >= 0; j--) {
224 if (tokensList.indexOf(mergeableTokens[j]) == -1) { continue movableLoop; }
225 }
226
227 candidates.push(value);
228 }
229
230 if (candidates.length < 2) { return false; }
231
232 for (j = candidates.length - 1; j >= 0; j--) {
233 for (k = movedProperties.length - 1; k >= 0; k--) {
234 if (movedProperties[k][4] == candidates[j]) {
235 propertiesAndMergableTokens.unshift([movedProperties[k], mergeableTokens]);
236 break;
237 }
238 }
239 }
240
241 return processMultiPropertyMove(position, propertiesAndMergableTokens);
242 }
243
244 function processMultiPropertyMove(position, propertiesAndMergableTokens) {
245 var valueSize = 0;
246 var properties = [];
247 var property;
248
249 for (var i = propertiesAndMergableTokens.length - 1; i >= 0; i--) {
250 property = propertiesAndMergableTokens[i][0];
251 var fullValue = property[4];
252 valueSize += fullValue.length + (i > 0 ? 1 : 0);
253
254 properties.push(property);
255 }
256
257 var mergeableTokens = propertiesAndMergableTokens[0][1];
258 var bestFit = findAllFits(mergeableTokens, valueSize, properties.length)[0];
259 if (bestFit[1] > 0) { return false; }
260
261 var allSelectors = [];
262 var qualifiedTokens = [];
263 for (i = bestFit[0].length - 1; i >= 0; i--) {
264 allSelectors = bestFit[0][i][1].concat(allSelectors);
265 qualifiedTokens.unshift(bestFit[0][i]);
266 }
267
268 allSelectors = tidyRuleDuplicates(allSelectors);
269 dropAsNewTokenAt(position, properties, allSelectors, qualifiedTokens);
270
271 for (i = properties.length - 1; i >= 0; i--) {
272 property = properties[i];
273 var index = movedProperties.indexOf(property);
274
275 delete movableTokens[property[4]];
276
277 if (index > -1 && movedToBeDropped.indexOf(index) == -1) { movedToBeDropped.push(index); }
278 }
279
280 return true;
281 }
282
283 function boundToAnotherPropertyInCurrrentToken(property, movedProperty, token) {
284 var propertyName = property[0];
285 var movedPropertyName = movedProperty[0];
286 if (propertyName != movedPropertyName) { return false; }
287
288 var key = movedProperty[4];
289 var toMove = movableTokens[key];
290 return toMove && toMove.indexOf(token) > -1;
291 }
292
293 for (var i = tokens.length - 1; i >= 0; i--) {
294 var token = tokens[i];
295 var isRule;
296 var j, k, m;
297 var samePropertyAt;
298
299 if (token[0] == Token.RULE) {
300 isRule = true;
301 } else if (token[0] == Token.NESTED_BLOCK) {
302 isRule = false;
303 } else {
304 continue;
305 }
306
307 // We cache movedProperties.length as it may change in the loop
308 var movedCount = movedProperties.length;
309
310 var properties = extractProperties(token);
311 movedToBeDropped = [];
312
313 var unmovableInCurrentToken = [];
314 for (j = properties.length - 1; j >= 0; j--) {
315 for (k = j - 1; k >= 0; k--) {
316 if (!canReorderSingle(properties[j], properties[k], specificityCache)) {
317 unmovableInCurrentToken.push(j);
318 break;
319 }
320 }
321 }
322
323 for (j = properties.length - 1; j >= 0; j--) {
324 var property = properties[j];
325 var movedSameProperty = false;
326
327 for (k = 0; k < movedCount; k++) {
328 var movedProperty = movedProperties[k];
329
330 if (movedToBeDropped.indexOf(k) == -1 && (
331 !canReorderSingle(property, movedProperty, specificityCache)
332 && !boundToAnotherPropertyInCurrrentToken(property, movedProperty, token)
333 || movableTokens[movedProperty[4]] && movableTokens[movedProperty[4]].length === mergeLimit)
334 ) {
335 dropPropertiesAt(i + 1, movedProperty);
336
337 if (movedToBeDropped.indexOf(k) == -1) {
338 movedToBeDropped.push(k);
339 delete movableTokens[movedProperty[4]];
340 }
341 }
342
343 if (!movedSameProperty) {
344 movedSameProperty = property[0] == movedProperty[0] && property[1] == movedProperty[1];
345
346 if (movedSameProperty) {
347 samePropertyAt = k;
348 }
349 }
350 }
351
352 if (!isRule || unmovableInCurrentToken.indexOf(j) > -1) { continue; }
353
354 var key = property[4];
355
356 if (movedSameProperty && movedProperties[samePropertyAt][5].length + property[5].length > mergeLimit) {
357 dropPropertiesAt(i + 1, movedProperties[samePropertyAt]);
358 movedProperties.splice(samePropertyAt, 1);
359 movableTokens[key] = [token];
360 movedSameProperty = false;
361 } else {
362 movableTokens[key] = movableTokens[key] || [];
363 movableTokens[key].push(token);
364 }
365
366 if (movedSameProperty) {
367 movedProperties[samePropertyAt] = cloneAndMergeSelectors(movedProperties[samePropertyAt], property);
368 } else {
369 movedProperties.push(property);
370 }
371 }
372
373 movedToBeDropped = movedToBeDropped.sort(naturalSorter);
374 for (j = 0, m = movedToBeDropped.length; j < m; j++) {
375 var dropAt = movedToBeDropped[j] - j;
376 movedProperties.splice(dropAt, 1);
377 }
378 }
379
380 var position = tokens[0] && tokens[0][0] == Token.AT_RULE && tokens[0][1].indexOf('@charset') === 0 ? 1 : 0;
381 for (; position < tokens.length - 1; position++) {
382 var isImportRule = tokens[position][0] === Token.AT_RULE && tokens[position][1].indexOf('@import') === 0;
383 var isComment = tokens[position][0] === Token.COMMENT;
384 if (!(isImportRule || isComment)) { break; }
385 }
386
387 for (i = 0; i < movedProperties.length; i++) {
388 dropPropertiesAt(position, movedProperties[i]);
389 }
390}
391
392module.exports = restructure;
Note: See TracBrowser for help on using the repository browser.