2025-04-02 06:50:39 -04:00

225 lines
5.5 KiB
JavaScript

var isMergeable = require('./is-mergeable');
var optimizeProperties = require('./properties/optimize');
var cloneArray = require('../../utils/clone-array');
var Token = require('../../tokenizer/token');
var serializeBody = require('../../writer/one-time').body;
var serializeRules = require('../../writer/one-time').rules;
function reduceNonAdjacent(tokens, context) {
var options = context.options;
var mergeablePseudoClasses =
options.compatibility.selectors.mergeablePseudoClasses;
var mergeablePseudoElements =
options.compatibility.selectors.mergeablePseudoElements;
var multiplePseudoMerging =
options.compatibility.selectors.multiplePseudoMerging;
var candidates = {};
var repeated = [];
for (var i = tokens.length - 1; i >= 0; i--) {
var token = tokens[i];
if (token[0] != Token.RULE) {
continue;
} else if (token[2].length === 0) {
continue;
}
var selectorAsString = serializeRules(token[1]);
var isComplexAndNotSpecial =
token[1].length > 1 &&
isMergeable(
selectorAsString,
mergeablePseudoClasses,
mergeablePseudoElements,
multiplePseudoMerging
);
var wrappedSelectors = wrappedSelectorsFrom(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, context);
reduceComplexNonAdjacentCases(tokens, candidates, options, context);
}
function wrappedSelectorsFrom(list) {
var wrapped = [];
for (var i = 0; i < list.length; i++) {
wrapped.push([list[i][1]]);
}
return wrapped;
}
function reduceSimpleNonAdjacentCases(
tokens,
repeated,
candidates,
options,
context
) {
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,
data,
{
filterOut: filterOut,
callback: reduceBody,
},
options,
context
);
}
}
function reduceComplexNonAdjacentCases(tokens, candidates, options, context) {
var mergeablePseudoClasses =
options.compatibility.selectors.mergeablePseudoClasses;
var mergeablePseudoElements =
options.compatibility.selectors.mergeablePseudoElements;
var multiplePseudoMerging =
options.compatibility.selectors.multiplePseudoMerging;
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 =
(
isMergeable(
complexSelector,
mergeablePseudoClasses,
mergeablePseudoElements,
multiplePseudoMerging
)
) ?
into[0].list
: [complexSelector];
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,
data,
{
filterOut: filterOut,
callback: collectReducedBodies,
},
options,
context
);
if (
serializeBody(reducedBodies[reducedBodies.length - 1]) !=
serializeBody(reducedBodies[0])
)
continue allSelectors;
}
intoToken[2] = reducedBodies[0];
}
}
function reduceSelector(tokens, data, context, options, outerContext) {
var bodies = [];
var bodiesAsList = [];
var processedTokens = [];
for (var j = data.length - 1; 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);
}
optimizeProperties(bodies, true, false, outerContext);
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;