};
var reduceNonAdjacent = function(tokens) {
- var matched = {};
- var matchedMoreThanOnce = [];
- var partiallyReduced = [];
- var reduced = false;
- var token, selector, selectors;
+ var candidates = {};
+ var moreThanOnce = [];
- for (var i = 0, l = tokens.length; i < l; i++) {
- token = tokens[i];
- selector = token.selector;
+ for (var i = tokens.length - 1; i >= 0; i--) {
+ var token = tokens[i];
if (typeof token == 'string' || token.block)
continue;
- selectors = selector.indexOf(',') > 0 ?
- selector.split(',').concat(selector) :
- [selector];
+ var complexSelector = token.selector;
+ var selectors = complexSelector.split(','); // simplification, as :not() can have commas too
+
+ if (selectors.length > 1)
+ selectors.unshift(complexSelector);
for (var j = 0, m = selectors.length; j < m; j++) {
- var sel = selectors[j];
- var alreadyMatched = matched[sel];
- if (alreadyMatched) {
- if (alreadyMatched.length == 1)
- matchedMoreThanOnce.push(sel);
- alreadyMatched.push(i);
- } else {
- matched[sel] = [i];
- }
+ var selector = selectors[j];
+
+ if (!candidates[selector])
+ candidates[selector] = [];
+ else
+ moreThanOnce.push(selector);
+
+ candidates[selector].push({
+ where: i,
+ partial: selector != complexSelector
+ });
}
}
- matchedMoreThanOnce.forEach(function(selector) {
- var matchPositions = matched[selector];
+ _reduceSimpleNonAdjacentCases(tokens, moreThanOnce, candidates);
+ _reduceComplexNonAdjacentCases(tokens, candidates);
+ };
+
+ var _reduceSimpleNonAdjacentCases = function(tokens, matches, positions) {
+ for (var i = 0, l = matches.length; i < l; i++) {
+ var selector = matches[i];
+ var data = positions[selector];
var bodies = [];
var joinsAt = [];
- var j;
+ var processedTokens = [];
+
+ for (var j = data.length - 1, m = 0; j >= 0; j--) {
+ if (data[j].partial && bodies.length === 0)
+ continue;
- for (j = 0, m = matchPositions.length; j < m; j++) {
- var body = tokens[matchPositions[j]].body;
+ var where = data[j].where;
+ var token = tokens[where];
+ var body = token.body;
bodies.push(body);
- joinsAt.push((joinsAt[j - 1] || 0) + body.split(';').length);
+ processedTokens.push(where);
+ }
+
+ for (j = 0, m = bodies.length; j < m; j++) {
+ if (bodies[j].length > 0)
+ joinsAt.push((joinsAt[j - 1] || 0) + bodies[j].split(';').length);
}
var optimizedBody = propertyOptimizer.process(bodies.join(';'), joinsAt);
- var optimizedTokens = optimizedBody.split(';');
+ var optimizedProperties = optimizedBody.split(';');
- j = optimizedTokens.length - 1;
- var currentMatch = matchPositions.length - 1;
+ var processedCount = processedTokens.length;
+ var propertyIdx = optimizedProperties.length - 1;
+ var tokenIdx = processedCount - 1;
- while (currentMatch >= 0) {
- if (bodies[currentMatch].indexOf(optimizedTokens[j]) > -1 && j > -1) {
- j -= 1;
+ while (tokenIdx >= 0) {
+ if ((tokenIdx === 0 || bodies[tokenIdx].indexOf(optimizedProperties[propertyIdx]) > -1) && propertyIdx > -1) {
+ propertyIdx--;
continue;
}
- var tokenIndex = matchPositions[currentMatch];
- var token = tokens[tokenIndex];
- var newBody = optimizedTokens.splice(j + 1);
- var reducedBody = [];
- for (var k = 0, n = newBody.length; k < n; k++) {
- if (newBody[k].length > 0)
- reducedBody.push(newBody[k]);
- }
+ var newBody = optimizedProperties.splice(propertyIdx + 1);
+ if (!data[processedCount - tokenIdx - 1].partial)
+ tokens[processedTokens[tokenIdx]].body = newBody.join(';');
+
+ tokenIdx--;
+ }
+ }
+ };
- if (token.selector == selector) {
- var joinedBody = reducedBody.join(';');
- reduced = reduced || (token.body != joinedBody);
- token.body = joinedBody;
- } else {
- token._partials = token._partials || [];
- token._partials.push(reducedBody.join(';'));
+ var _reduceComplexNonAdjacentCases = function(tokens, positions) {
+ for (var i = 0, matches = Object.keys(positions), l = matches.length; i < l; i++) {
+ var complexSelector = matches[i];
+ var allSame = true;
- if (partiallyReduced.indexOf(tokenIndex) == -1)
- partiallyReduced.push(tokenIndex);
- }
+ if (complexSelector.indexOf(',') == -1) // another assumption which is wrong in case of :not() selector
+ continue;
- currentMatch -= 1;
- }
- });
+ var intoPosition = positions[complexSelector].pop().where;
+ var intoToken = tokens[intoPosition];
+
+ var selectors = complexSelector.split(',');
+ var reducedBodies = [];
+
+ for (var j = 0, m = selectors.length; j < m; j++) {
+ var selector = selectors[j];
+ var data = positions[selector];
+ var bodies = [];
+ var joinsAt = [];
+ var processedTokens = [];
+
+ for (var k = data.length - 1, n = 0; k >= 0; k--) {
+ var where = data[k].where;
+ if (where < intoPosition)
+ continue;
+
+ var token = tokens[where];
+ var body = token.body;
+ bodies.push(body);
+ processedTokens.push(where);
+ }
- // process those tokens which were partially reduced
- // i.e. at least one of token's selectors saw reduction
- // if all selectors were reduced to same value we can override it
- for (i = 0, l = partiallyReduced.length; i < l; i++) {
- token = tokens[partiallyReduced[i]];
-
- if (token.body != token._partials[0] && token._partials.length == token.selector.split(',').length) {
- var newBody = token._partials[0];
- for (var k = 1, n = token._partials.length; k < n; k++) {
- if (token._partials[k] != newBody)
- break;
+ for (k = 0, n = bodies.length; k < n; k++) {
+ if (bodies[k].length > 0)
+ joinsAt.push((joinsAt[k - 1] || 0) + bodies[k].split(';').length);
}
- if (k == n) {
- token.body = newBody;
- reduced = reduced || true;
+ var optimizedBody = propertyOptimizer.process(bodies.join(';'), joinsAt);
+ var optimizedProperties = optimizedBody.split(';');
+
+ var processedCount = processedTokens.length;
+ var propertyIdx = optimizedProperties.length - 1;
+ var tokenIdx = processedCount - 1;
+
+ while (tokenIdx >= 0) {
+ if ((tokenIdx === 0 || bodies[tokenIdx].indexOf(optimizedProperties[propertyIdx]) > -1) && propertyIdx > -1) {
+ propertyIdx--;
+ continue;
+ }
+
+ var newBody = optimizedProperties.splice(propertyIdx + 1);
+ if (tokenIdx === 0)
+ reducedBodies.push(newBody.join(';'));
+
+ tokenIdx--;
}
+
+ allSame = allSame && reducedBodies[reducedBodies.length - 1] == reducedBodies[0];
}
- delete token._partials;
+ if (allSame)
+ intoToken.body = reducedBodies[0];
}
-
- minificationsMade.unshift(reduced);
};
var optimize = function(tokens) {