You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

173 lines
4.8 KiB
JavaScript

var optimizeProperties = require('../properties/optimizer');
var stringifyBody = require('../stringifier/one-time').body;
var stringifySelectors = require('../stringifier/one-time').selectors;
var isSpecial = require('./is-special');
var cloneArray = require('../utils/clone-array');
function reduceNonAdjacent(tokens, options, validator) {
var candidates = {};
var repeated = [];
for (var i = tokens.length - 1; i >= 0; i--) {
var token = tokens[i];
if (token[0] != 'selector')
continue;
if (token[2].length === 0)
continue;
var selectorAsString = stringifySelectors(token[1]);
var isComplexAndNotSpecial = token[1].length > 1 && !isSpecial(options, selectorAsString);
var wrappedSelectors = options.sourceMap ? wrappedSelectorsFrom(token[1]) : token[1];
var selectors = isComplexAndNotSpecial ?
[selectorAsString].concat(wrappedSelectors) :
[selectorAsString];
for (var j = 0, m = selectors.length; j < m; j++) {
var selector = selectors[j];
if (!candidates[selector])
candidates[selector] = [];
else
repeated.push(selector);
candidates[selector].push({
where: i,
list: wrappedSelectors,
isPartial: isComplexAndNotSpecial && j > 0,
isComplex: isComplexAndNotSpecial && j === 0
});
}
}
reduceSimpleNonAdjacentCases(tokens, repeated, candidates, options, validator);
reduceComplexNonAdjacentCases(tokens, candidates, options, validator);
}
function wrappedSelectorsFrom(list) {
var wrapped = [];
for (var i = 0; i < list.length; i++) {
wrapped.push([list[i][0]]);
}
return wrapped;
}
function reduceSimpleNonAdjacentCases(tokens, repeated, candidates, options, validator) {
function filterOut(idx, bodies) {
return data[idx].isPartial && bodies.length === 0;
}
function reduceBody(token, newBody, processedCount, tokenIdx) {
if (!data[processedCount - tokenIdx - 1].isPartial)
token[2] = newBody;
}
for (var i = 0, l = repeated.length; i < l; i++) {
var selector = repeated[i];
var data = candidates[selector];
reduceSelector(tokens, selector, data, {
filterOut: filterOut,
callback: reduceBody
}, options, validator);
}
}
function reduceComplexNonAdjacentCases(tokens, candidates, options, validator) {
var localContext = {};
function filterOut(idx) {
return localContext.data[idx].where < localContext.intoPosition;
}
function collectReducedBodies(token, newBody, processedCount, tokenIdx) {
if (tokenIdx === 0)
localContext.reducedBodies.push(newBody);
}
allSelectors:
for (var complexSelector in candidates) {
var into = candidates[complexSelector];
if (!into[0].isComplex)
continue;
var intoPosition = into[into.length - 1].where;
var intoToken = tokens[intoPosition];
var reducedBodies = [];
var selectors = isSpecial(options, complexSelector) ?
[complexSelector] :
into[0].list;
localContext.intoPosition = intoPosition;
localContext.reducedBodies = reducedBodies;
for (var j = 0, m = selectors.length; j < m; j++) {
var selector = selectors[j];
var data = candidates[selector];
if (data.length < 2)
continue allSelectors;
localContext.data = data;
reduceSelector(tokens, selector, data, {
filterOut: filterOut,
callback: collectReducedBodies
}, options, validator);
if (stringifyBody(reducedBodies[reducedBodies.length - 1]) != stringifyBody(reducedBodies[0]))
continue allSelectors;
}
intoToken[2] = reducedBodies[0];
}
}
function reduceSelector(tokens, selector, data, context, options, validator) {
var bodies = [];
var bodiesAsList = [];
var joinsAt = [];
var processedTokens = [];
for (var j = data.length - 1, m = 0; j >= 0; j--) {
if (context.filterOut(j, bodies))
continue;
var where = data[j].where;
var token = tokens[where];
var clonedBody = cloneArray(token[2]);
bodies = bodies.concat(clonedBody);
bodiesAsList.push(clonedBody);
processedTokens.push(where);
}
for (j = 0, m = bodiesAsList.length; j < m; j++) {
if (bodiesAsList[j].length > 0)
joinsAt.push((joinsAt[j - 1] || 0) + bodiesAsList[j].length);
}
optimizeProperties(selector, bodies, joinsAt, false, options, validator);
var processedCount = processedTokens.length;
var propertyIdx = bodies.length - 1;
var tokenIdx = processedCount - 1;
while (tokenIdx >= 0) {
if ((tokenIdx === 0 || (bodies[propertyIdx] && bodiesAsList[tokenIdx].indexOf(bodies[propertyIdx]) > -1)) && propertyIdx > -1) {
propertyIdx--;
continue;
}
var newBody = bodies.splice(propertyIdx + 1);
context.callback(tokens[processedTokens[tokenIdx]], newBody, processedCount, tokenIdx);
tokenIdx--;
}
}
module.exports = reduceNonAdjacent;