var optimizeProperties = require('../properties/optimizer');
-var CleanUp = require('./clean-up');
var extractProperties = require('./extractor');
var canReorder = require('./reorderable').canReorder;
-var canReorderSingle = require('./reorderable').canReorderSingle;
var stringifyAll = require('../stringifier/one-time').all;
-var stringifyBody = require('../stringifier/one-time').body;
-var stringifySelectors = require('../stringifier/one-time').selectors;
var removeDuplicates = require('./remove-duplicates');
var mergeAdjacent = require('./merge-adjacent');
var reduceNonAdjacent = require('./reduce-non-adjacent');
var mergeNonAdjacentBySelector = require('./merge-non-adjacent-by-selector');
var mergeNonAdjacentByBody = require('./merge-non-adjacent-by-body');
+var restructure = require('./restructure');
function AdvancedOptimizer(options, context) {
this.options = options;
this.validator = context.validator;
}
-function naturalSorter(a, b) {
- return a > b;
-}
-
-AdvancedOptimizer.prototype.isSpecial = function (selector) {
- return this.options.compatibility.selectors.special.test(selector);
-};
-
-AdvancedOptimizer.prototype.restructure = function (tokens) {
- var movableTokens = {};
- var movedProperties = [];
- var multiPropertyMoveCache = {};
- var movedToBeDropped = [];
- var self = this;
- var maxCombinationsLevel = 2;
- var ID_JOIN_CHARACTER = '%';
-
- function sendToMultiPropertyMoveCache(position, movedProperty, allFits) {
- for (var i = allFits.length - 1; i >= 0; i--) {
- var fit = allFits[i][0];
- var id = addToCache(movedProperty, fit);
-
- if (multiPropertyMoveCache[id].length > 1 && processMultiPropertyMove(position, multiPropertyMoveCache[id])) {
- removeAllMatchingFromCache(id);
- break;
- }
- }
- }
-
- function addToCache(movedProperty, fit) {
- var id = cacheId(fit);
- multiPropertyMoveCache[id] = multiPropertyMoveCache[id] || [];
- multiPropertyMoveCache[id].push([movedProperty, fit]);
- return id;
- }
-
- function removeAllMatchingFromCache(matchId) {
- var matchSelectors = matchId.split(ID_JOIN_CHARACTER);
- var forRemoval = [];
- var i;
-
- for (var id in multiPropertyMoveCache) {
- var selectors = id.split(ID_JOIN_CHARACTER);
- for (i = selectors.length - 1; i >= 0; i--) {
- if (matchSelectors.indexOf(selectors[i]) > -1) {
- forRemoval.push(id);
- break;
- }
- }
- }
-
- for (i = forRemoval.length - 1; i >= 0; i--) {
- delete multiPropertyMoveCache[forRemoval[i]];
- }
- }
-
- function cacheId(cachedTokens) {
- var id = [];
- for (var i = 0, l = cachedTokens.length; i < l; i++) {
- id.push(stringifySelectors(cachedTokens[i][1]));
- }
- return id.join(ID_JOIN_CHARACTER);
- }
-
- function tokensToMerge(sourceTokens) {
- var uniqueTokensWithBody = [];
- var mergeableTokens = [];
-
- for (var i = sourceTokens.length - 1; i >= 0; i--) {
- if (self.isSpecial(stringifySelectors(sourceTokens[i][1])))
- continue;
-
- mergeableTokens.unshift(sourceTokens[i]);
- if (sourceTokens[i][2].length > 0 && uniqueTokensWithBody.indexOf(sourceTokens[i]) == -1)
- uniqueTokensWithBody.push(sourceTokens[i]);
- }
-
- return uniqueTokensWithBody.length > 1 ?
- mergeableTokens :
- [];
- }
-
- function shortenIfPossible(position, movedProperty) {
- var name = movedProperty[0];
- var value = movedProperty[1];
- var key = movedProperty[4];
- var valueSize = name.length + value.length + 1;
- var allSelectors = [];
- var qualifiedTokens = [];
-
- var mergeableTokens = tokensToMerge(movableTokens[key]);
- if (mergeableTokens.length < 2)
- return;
-
- var allFits = findAllFits(mergeableTokens, valueSize, 1);
- var bestFit = allFits[0];
- if (bestFit[1] > 0)
- return sendToMultiPropertyMoveCache(position, movedProperty, allFits);
-
- for (var i = bestFit[0].length - 1; i >=0; i--) {
- allSelectors = bestFit[0][i][1].concat(allSelectors);
- qualifiedTokens.unshift(bestFit[0][i]);
- }
-
- allSelectors = CleanUp.selectorDuplicates(allSelectors);
- dropAsNewTokenAt(position, [movedProperty], allSelectors, qualifiedTokens);
- }
-
- function fitSorter(fit1, fit2) {
- return fit1[1] > fit2[1];
- }
-
- function findAllFits(mergeableTokens, propertySize, propertiesCount) {
- var combinations = allCombinations(mergeableTokens, propertySize, propertiesCount, maxCombinationsLevel - 1);
- return combinations.sort(fitSorter);
- }
-
- function allCombinations(tokensVariant, propertySize, propertiesCount, level) {
- var differenceVariants = [[tokensVariant, sizeDifference(tokensVariant, propertySize, propertiesCount)]];
- if (tokensVariant.length > 2 && level > 0) {
- for (var i = tokensVariant.length - 1; i >= 0; i--) {
- var subVariant = Array.prototype.slice.call(tokensVariant, 0);
- subVariant.splice(i, 1);
- differenceVariants = differenceVariants.concat(allCombinations(subVariant, propertySize, propertiesCount, level - 1));
- }
- }
-
- return differenceVariants;
- }
-
- function sizeDifference(tokensVariant, propertySize, propertiesCount) {
- var allSelectorsSize = 0;
- for (var i = tokensVariant.length - 1; i >= 0; i--) {
- allSelectorsSize += tokensVariant[i][2].length > propertiesCount ? stringifySelectors(tokensVariant[i][1]).length : -1;
- }
- return allSelectorsSize - (tokensVariant.length - 1) * propertySize + 1;
- }
-
- function dropAsNewTokenAt(position, properties, allSelectors, mergeableTokens) {
- var i, j, k, m;
- var allProperties = [];
-
- for (i = mergeableTokens.length - 1; i >= 0; i--) {
- var mergeableToken = mergeableTokens[i];
-
- for (j = mergeableToken[2].length - 1; j >= 0; j--) {
- var mergeableProperty = mergeableToken[2][j];
-
- for (k = 0, m = properties.length; k < m; k++) {
- var property = properties[k];
-
- var mergeablePropertyName = mergeableProperty[0][0];
- var propertyName = property[0];
- var propertyBody = property[4];
- if (mergeablePropertyName == propertyName && stringifyBody([mergeableProperty]) == propertyBody) {
- mergeableToken[2].splice(j, 1);
- break;
- }
- }
- }
- }
-
- for (i = properties.length - 1; i >= 0; i--) {
- allProperties.push(properties[i][3]);
- }
-
- var newToken = ['selector', allSelectors, allProperties];
- tokens.splice(position, 0, newToken);
- }
-
- function dropPropertiesAt(position, movedProperty) {
- var key = movedProperty[4];
- var toMove = movableTokens[key];
-
- if (toMove && toMove.length > 1) {
- if (!shortenMultiMovesIfPossible(position, movedProperty))
- shortenIfPossible(position, movedProperty);
- }
- }
-
- function shortenMultiMovesIfPossible(position, movedProperty) {
- var candidates = [];
- var propertiesAndMergableTokens = [];
- var key = movedProperty[4];
- var j, k;
-
- var mergeableTokens = tokensToMerge(movableTokens[key]);
- if (mergeableTokens.length < 2)
- return;
-
- movableLoop:
- for (var value in movableTokens) {
- var tokensList = movableTokens[value];
-
- for (j = mergeableTokens.length - 1; j >= 0; j--) {
- if (tokensList.indexOf(mergeableTokens[j]) == -1)
- continue movableLoop;
- }
-
- candidates.push(value);
- }
-
- if (candidates.length < 2)
- return false;
-
- for (j = candidates.length - 1; j >= 0; j--) {
- for (k = movedProperties.length - 1; k >= 0; k--) {
- if (movedProperties[k][4] == candidates[j]) {
- propertiesAndMergableTokens.unshift([movedProperties[k], mergeableTokens]);
- break;
- }
- }
- }
-
- return processMultiPropertyMove(position, propertiesAndMergableTokens);
- }
-
- function processMultiPropertyMove(position, propertiesAndMergableTokens) {
- var valueSize = 0;
- var properties = [];
- var property;
-
- for (var i = propertiesAndMergableTokens.length - 1; i >= 0; i--) {
- property = propertiesAndMergableTokens[i][0];
- var fullValue = property[4];
- valueSize += fullValue.length + (i > 0 ? 1 : 0);
-
- properties.push(property);
- }
-
- var mergeableTokens = propertiesAndMergableTokens[0][1];
- var bestFit = findAllFits(mergeableTokens, valueSize, properties.length)[0];
- if (bestFit[1] > 0)
- return false;
-
- var allSelectors = [];
- var qualifiedTokens = [];
- for (i = bestFit[0].length - 1; i >= 0; i--) {
- allSelectors = bestFit[0][i][1].concat(allSelectors);
- qualifiedTokens.unshift(bestFit[0][i]);
- }
-
- allSelectors = CleanUp.selectorDuplicates(allSelectors);
- dropAsNewTokenAt(position, properties, allSelectors, qualifiedTokens);
-
- for (i = properties.length - 1; i >= 0; i--) {
- property = properties[i];
- var index = movedProperties.indexOf(property);
-
- delete movableTokens[property[4]];
-
- if (index > -1 && movedToBeDropped.indexOf(index) == -1)
- movedToBeDropped.push(index);
- }
-
- return true;
- }
-
- function boundToAnotherPropertyInCurrrentToken(property, movedProperty, token) {
- var propertyName = property[0];
- var movedPropertyName = movedProperty[0];
- if (propertyName != movedPropertyName)
- return false;
-
- var key = movedProperty[4];
- var toMove = movableTokens[key];
- return toMove && toMove.indexOf(token) > -1;
- }
-
- for (var i = tokens.length - 1; i >= 0; i--) {
- var token = tokens[i];
- var isSelector;
- var j, k, m;
-
- if (token[0] == 'selector') {
- isSelector = true;
- } else if (token[0] == 'block') {
- isSelector = false;
- } else {
- continue;
- }
-
- // We cache movedProperties.length as it may change in the loop
- var movedCount = movedProperties.length;
-
- var properties = extractProperties(token);
- movedToBeDropped = [];
-
- var unmovableInCurrentToken = [];
- for (j = properties.length - 1; j >= 0; j--) {
- for (k = j - 1; k >= 0; k--) {
- if (!canReorderSingle(properties[j], properties[k])) {
- unmovableInCurrentToken.push(j);
- break;
- }
- }
- }
-
- for (j = 0, m = properties.length; j < m; j++) {
- var property = properties[j];
- var movedSameProperty = false;
-
- for (k = 0; k < movedCount; k++) {
- var movedProperty = movedProperties[k];
-
- if (movedToBeDropped.indexOf(k) == -1 && !canReorderSingle(property, movedProperty) && !boundToAnotherPropertyInCurrrentToken(property, movedProperty, token)) {
- dropPropertiesAt(i + 1, movedProperty, token);
-
- if (movedToBeDropped.indexOf(k) == -1) {
- movedToBeDropped.push(k);
- delete movableTokens[movedProperty[4]];
- }
- }
-
- if (!movedSameProperty)
- movedSameProperty = property[0] == movedProperty[0] && property[1] == movedProperty[1];
- }
-
- if (!isSelector || unmovableInCurrentToken.indexOf(j) > -1)
- continue;
-
- var key = property[4];
- movableTokens[key] = movableTokens[key] || [];
- movableTokens[key].push(token);
-
- if (!movedSameProperty)
- movedProperties.push(property);
- }
-
- movedToBeDropped = movedToBeDropped.sort(naturalSorter);
- for (j = 0, m = movedToBeDropped.length; j < m; j++) {
- var dropAt = movedToBeDropped[j] - j;
- movedProperties.splice(dropAt, 1);
- }
- }
-
- var position = tokens[0] && tokens[0][0] == 'at-rule' && tokens[0][1][0].indexOf('@charset') === 0 ? 1 : 0;
- for (; position < tokens.length - 1; position++) {
- var isImportRule = tokens[position][0] === 'at-rule' && tokens[position][1][0].indexOf('@import') === 0;
- var isEscapedCommentSpecial = tokens[position][0] === 'text' && tokens[position][1][0].indexOf('__ESCAPED_COMMENT_SPECIAL') === 0;
- if (!(isImportRule || isEscapedCommentSpecial))
- break;
- }
-
- for (i = 0; i < movedProperties.length; i++) {
- dropPropertiesAt(position, movedProperties[i]);
- }
-};
-
AdvancedOptimizer.prototype.removeDuplicateMediaQueries = function (tokens) {
var candidates = {};
mergeNonAdjacentByBody(tokens, self.options);
if (self.options.restructuring && withRestructuring) {
- self.restructure(tokens);
+ restructure(tokens, self.options);
mergeAdjacent(tokens, self.options, self.validator);
}
--- /dev/null
+var extractProperties = require('./extractor');
+var canReorderSingle = require('./reorderable').canReorderSingle;
+var stringifyBody = require('../stringifier/one-time').body;
+var stringifySelectors = require('../stringifier/one-time').selectors;
+var cleanUpSelectorDuplicates = require('./clean-up').selectorDuplicates;
+var isSpecial = require('./is-special');
+
+function naturalSorter(a, b) {
+ return a > b;
+}
+
+function restructure(tokens, options) {
+ var movableTokens = {};
+ var movedProperties = [];
+ var multiPropertyMoveCache = {};
+ var movedToBeDropped = [];
+ var maxCombinationsLevel = 2;
+ var ID_JOIN_CHARACTER = '%';
+
+ function sendToMultiPropertyMoveCache(position, movedProperty, allFits) {
+ for (var i = allFits.length - 1; i >= 0; i--) {
+ var fit = allFits[i][0];
+ var id = addToCache(movedProperty, fit);
+
+ if (multiPropertyMoveCache[id].length > 1 && processMultiPropertyMove(position, multiPropertyMoveCache[id])) {
+ removeAllMatchingFromCache(id);
+ break;
+ }
+ }
+ }
+
+ function addToCache(movedProperty, fit) {
+ var id = cacheId(fit);
+ multiPropertyMoveCache[id] = multiPropertyMoveCache[id] || [];
+ multiPropertyMoveCache[id].push([movedProperty, fit]);
+ return id;
+ }
+
+ function removeAllMatchingFromCache(matchId) {
+ var matchSelectors = matchId.split(ID_JOIN_CHARACTER);
+ var forRemoval = [];
+ var i;
+
+ for (var id in multiPropertyMoveCache) {
+ var selectors = id.split(ID_JOIN_CHARACTER);
+ for (i = selectors.length - 1; i >= 0; i--) {
+ if (matchSelectors.indexOf(selectors[i]) > -1) {
+ forRemoval.push(id);
+ break;
+ }
+ }
+ }
+
+ for (i = forRemoval.length - 1; i >= 0; i--) {
+ delete multiPropertyMoveCache[forRemoval[i]];
+ }
+ }
+
+ function cacheId(cachedTokens) {
+ var id = [];
+ for (var i = 0, l = cachedTokens.length; i < l; i++) {
+ id.push(stringifySelectors(cachedTokens[i][1]));
+ }
+ return id.join(ID_JOIN_CHARACTER);
+ }
+
+ function tokensToMerge(sourceTokens) {
+ var uniqueTokensWithBody = [];
+ var mergeableTokens = [];
+
+ for (var i = sourceTokens.length - 1; i >= 0; i--) {
+ if (isSpecial(options, stringifySelectors(sourceTokens[i][1])))
+ continue;
+
+ mergeableTokens.unshift(sourceTokens[i]);
+ if (sourceTokens[i][2].length > 0 && uniqueTokensWithBody.indexOf(sourceTokens[i]) == -1)
+ uniqueTokensWithBody.push(sourceTokens[i]);
+ }
+
+ return uniqueTokensWithBody.length > 1 ?
+ mergeableTokens :
+ [];
+ }
+
+ function shortenIfPossible(position, movedProperty) {
+ var name = movedProperty[0];
+ var value = movedProperty[1];
+ var key = movedProperty[4];
+ var valueSize = name.length + value.length + 1;
+ var allSelectors = [];
+ var qualifiedTokens = [];
+
+ var mergeableTokens = tokensToMerge(movableTokens[key]);
+ if (mergeableTokens.length < 2)
+ return;
+
+ var allFits = findAllFits(mergeableTokens, valueSize, 1);
+ var bestFit = allFits[0];
+ if (bestFit[1] > 0)
+ return sendToMultiPropertyMoveCache(position, movedProperty, allFits);
+
+ for (var i = bestFit[0].length - 1; i >=0; i--) {
+ allSelectors = bestFit[0][i][1].concat(allSelectors);
+ qualifiedTokens.unshift(bestFit[0][i]);
+ }
+
+ allSelectors = cleanUpSelectorDuplicates(allSelectors);
+ dropAsNewTokenAt(position, [movedProperty], allSelectors, qualifiedTokens);
+ }
+
+ function fitSorter(fit1, fit2) {
+ return fit1[1] > fit2[1];
+ }
+
+ function findAllFits(mergeableTokens, propertySize, propertiesCount) {
+ var combinations = allCombinations(mergeableTokens, propertySize, propertiesCount, maxCombinationsLevel - 1);
+ return combinations.sort(fitSorter);
+ }
+
+ function allCombinations(tokensVariant, propertySize, propertiesCount, level) {
+ var differenceVariants = [[tokensVariant, sizeDifference(tokensVariant, propertySize, propertiesCount)]];
+ if (tokensVariant.length > 2 && level > 0) {
+ for (var i = tokensVariant.length - 1; i >= 0; i--) {
+ var subVariant = Array.prototype.slice.call(tokensVariant, 0);
+ subVariant.splice(i, 1);
+ differenceVariants = differenceVariants.concat(allCombinations(subVariant, propertySize, propertiesCount, level - 1));
+ }
+ }
+
+ return differenceVariants;
+ }
+
+ function sizeDifference(tokensVariant, propertySize, propertiesCount) {
+ var allSelectorsSize = 0;
+ for (var i = tokensVariant.length - 1; i >= 0; i--) {
+ allSelectorsSize += tokensVariant[i][2].length > propertiesCount ? stringifySelectors(tokensVariant[i][1]).length : -1;
+ }
+ return allSelectorsSize - (tokensVariant.length - 1) * propertySize + 1;
+ }
+
+ function dropAsNewTokenAt(position, properties, allSelectors, mergeableTokens) {
+ var i, j, k, m;
+ var allProperties = [];
+
+ for (i = mergeableTokens.length - 1; i >= 0; i--) {
+ var mergeableToken = mergeableTokens[i];
+
+ for (j = mergeableToken[2].length - 1; j >= 0; j--) {
+ var mergeableProperty = mergeableToken[2][j];
+
+ for (k = 0, m = properties.length; k < m; k++) {
+ var property = properties[k];
+
+ var mergeablePropertyName = mergeableProperty[0][0];
+ var propertyName = property[0];
+ var propertyBody = property[4];
+ if (mergeablePropertyName == propertyName && stringifyBody([mergeableProperty]) == propertyBody) {
+ mergeableToken[2].splice(j, 1);
+ break;
+ }
+ }
+ }
+ }
+
+ for (i = properties.length - 1; i >= 0; i--) {
+ allProperties.push(properties[i][3]);
+ }
+
+ var newToken = ['selector', allSelectors, allProperties];
+ tokens.splice(position, 0, newToken);
+ }
+
+ function dropPropertiesAt(position, movedProperty) {
+ var key = movedProperty[4];
+ var toMove = movableTokens[key];
+
+ if (toMove && toMove.length > 1) {
+ if (!shortenMultiMovesIfPossible(position, movedProperty))
+ shortenIfPossible(position, movedProperty);
+ }
+ }
+
+ function shortenMultiMovesIfPossible(position, movedProperty) {
+ var candidates = [];
+ var propertiesAndMergableTokens = [];
+ var key = movedProperty[4];
+ var j, k;
+
+ var mergeableTokens = tokensToMerge(movableTokens[key]);
+ if (mergeableTokens.length < 2)
+ return;
+
+ movableLoop:
+ for (var value in movableTokens) {
+ var tokensList = movableTokens[value];
+
+ for (j = mergeableTokens.length - 1; j >= 0; j--) {
+ if (tokensList.indexOf(mergeableTokens[j]) == -1)
+ continue movableLoop;
+ }
+
+ candidates.push(value);
+ }
+
+ if (candidates.length < 2)
+ return false;
+
+ for (j = candidates.length - 1; j >= 0; j--) {
+ for (k = movedProperties.length - 1; k >= 0; k--) {
+ if (movedProperties[k][4] == candidates[j]) {
+ propertiesAndMergableTokens.unshift([movedProperties[k], mergeableTokens]);
+ break;
+ }
+ }
+ }
+
+ return processMultiPropertyMove(position, propertiesAndMergableTokens);
+ }
+
+ function processMultiPropertyMove(position, propertiesAndMergableTokens) {
+ var valueSize = 0;
+ var properties = [];
+ var property;
+
+ for (var i = propertiesAndMergableTokens.length - 1; i >= 0; i--) {
+ property = propertiesAndMergableTokens[i][0];
+ var fullValue = property[4];
+ valueSize += fullValue.length + (i > 0 ? 1 : 0);
+
+ properties.push(property);
+ }
+
+ var mergeableTokens = propertiesAndMergableTokens[0][1];
+ var bestFit = findAllFits(mergeableTokens, valueSize, properties.length)[0];
+ if (bestFit[1] > 0)
+ return false;
+
+ var allSelectors = [];
+ var qualifiedTokens = [];
+ for (i = bestFit[0].length - 1; i >= 0; i--) {
+ allSelectors = bestFit[0][i][1].concat(allSelectors);
+ qualifiedTokens.unshift(bestFit[0][i]);
+ }
+
+ allSelectors = cleanUpSelectorDuplicates(allSelectors);
+ dropAsNewTokenAt(position, properties, allSelectors, qualifiedTokens);
+
+ for (i = properties.length - 1; i >= 0; i--) {
+ property = properties[i];
+ var index = movedProperties.indexOf(property);
+
+ delete movableTokens[property[4]];
+
+ if (index > -1 && movedToBeDropped.indexOf(index) == -1)
+ movedToBeDropped.push(index);
+ }
+
+ return true;
+ }
+
+ function boundToAnotherPropertyInCurrrentToken(property, movedProperty, token) {
+ var propertyName = property[0];
+ var movedPropertyName = movedProperty[0];
+ if (propertyName != movedPropertyName)
+ return false;
+
+ var key = movedProperty[4];
+ var toMove = movableTokens[key];
+ return toMove && toMove.indexOf(token) > -1;
+ }
+
+ for (var i = tokens.length - 1; i >= 0; i--) {
+ var token = tokens[i];
+ var isSelector;
+ var j, k, m;
+
+ if (token[0] == 'selector') {
+ isSelector = true;
+ } else if (token[0] == 'block') {
+ isSelector = false;
+ } else {
+ continue;
+ }
+
+ // We cache movedProperties.length as it may change in the loop
+ var movedCount = movedProperties.length;
+
+ var properties = extractProperties(token);
+ movedToBeDropped = [];
+
+ var unmovableInCurrentToken = [];
+ for (j = properties.length - 1; j >= 0; j--) {
+ for (k = j - 1; k >= 0; k--) {
+ if (!canReorderSingle(properties[j], properties[k])) {
+ unmovableInCurrentToken.push(j);
+ break;
+ }
+ }
+ }
+
+ for (j = 0, m = properties.length; j < m; j++) {
+ var property = properties[j];
+ var movedSameProperty = false;
+
+ for (k = 0; k < movedCount; k++) {
+ var movedProperty = movedProperties[k];
+
+ if (movedToBeDropped.indexOf(k) == -1 && !canReorderSingle(property, movedProperty) && !boundToAnotherPropertyInCurrrentToken(property, movedProperty, token)) {
+ dropPropertiesAt(i + 1, movedProperty, token);
+
+ if (movedToBeDropped.indexOf(k) == -1) {
+ movedToBeDropped.push(k);
+ delete movableTokens[movedProperty[4]];
+ }
+ }
+
+ if (!movedSameProperty)
+ movedSameProperty = property[0] == movedProperty[0] && property[1] == movedProperty[1];
+ }
+
+ if (!isSelector || unmovableInCurrentToken.indexOf(j) > -1)
+ continue;
+
+ var key = property[4];
+ movableTokens[key] = movableTokens[key] || [];
+ movableTokens[key].push(token);
+
+ if (!movedSameProperty)
+ movedProperties.push(property);
+ }
+
+ movedToBeDropped = movedToBeDropped.sort(naturalSorter);
+ for (j = 0, m = movedToBeDropped.length; j < m; j++) {
+ var dropAt = movedToBeDropped[j] - j;
+ movedProperties.splice(dropAt, 1);
+ }
+ }
+
+ var position = tokens[0] && tokens[0][0] == 'at-rule' && tokens[0][1][0].indexOf('@charset') === 0 ? 1 : 0;
+ for (; position < tokens.length - 1; position++) {
+ var isImportRule = tokens[position][0] === 'at-rule' && tokens[position][1][0].indexOf('@import') === 0;
+ var isEscapedCommentSpecial = tokens[position][0] === 'text' && tokens[position][1][0].indexOf('__ESCAPED_COMMENT_SPECIAL') === 0;
+ if (!(isImportRule || isEscapedCommentSpecial))
+ break;
+ }
+
+ for (i = 0; i < movedProperties.length; i++) {
+ dropPropertiesAt(position, movedProperties[i]);
+ }
+}
+
+module.exports = restructure;
var optimizerContext = require('../test-helper').optimizerContext;
vows.describe('advanced optimizer')
- .addBatch(
- optimizerContext('selectors - restructuring', {
- 'up until changed': [
- 'a{color:#000}div{color:red}.one{display:block}.two{display:inline;color:red}',
- 'a{color:#000}.two,div{color:red}.one{display:block}.two{display:inline}'
- ],
- 'up until top': [
- 'a{width:100px}div{color:red}.one{display:block}.two{display:inline;color:red}',
- '.two,div{color:red}a{width:100px}.one{display:block}.two{display:inline}'
- ],
- 'up until top with charset': [
- '@charset "utf-8";a{width:100px}div{color:red}.one{display:block}.two{display:inline;color:red}',
- '@charset "utf-8";.two,div{color:red}a{width:100px}.one{display:block}.two{display:inline}'
- ],
- 'two at once': [
- '.one,.two,.three{color:red;display:block}div{margin:0}.four,.five,.six{color:red;display:block}',
- '.five,.four,.one,.six,.three,.two{color:red;display:block}div{margin:0}'
- ],
- 'down until changed': [
- '.one{padding:0}.two{margin:0}.one{margin-bottom:3px}',
- '.two{margin:0}.one{padding:0;margin-bottom:3px}'
- ],
- 'over shorthands': [
- 'div{margin-top:0}.one{margin:0}.two{display:block;margin-top:0}',
- '.two,div{margin-top:0}.one{margin:0}.two{display:block}'
- ],
- 'over shorthands with flush': [
- 'div{margin-top:0}.one{margin:5px}.two{display:block;margin-top:0}.three{color:red}.four{margin-top:0}',
- 'div{margin-top:0}.one{margin:5px}.four,.two{margin-top:0}.two{display:block}.three{color:red}'
- ],
- 'over shorthand - border': [
- '.one{border-color:red}.two{border:1px solid}.three{color:#fff;border-color:red}',
- '.one{border-color:red}.two{border:1px solid}.three{color:#fff;border-color:red}'
- ],
- 'granuar over granular': [
- 'div{margin-top:0}.one{margin-bottom:2px}.two{display:block;margin-top:0}',
- '.two,div{margin-top:0}.one{margin-bottom:2px}.two{display:block}'
- ],
- 'shorthand over granular with different value': [
- 'div{margin:0}.one{margin-bottom:1px}.two{display:block;margin:0}',
- 'div{margin:0}.one{margin-bottom:1px}.two{display:block;margin:0}'
- ],
- 'shorthand over granular with different value for simple tags': [
- 'div{margin:0}body{margin-bottom:1px}p{display:block;margin:0}',
- 'div,p{margin:0}body{margin-bottom:1px}p{display:block}'
- ],
- 'shorthand over granular with different value for simple tags when tag match': [
- 'div{margin:0}body,p{margin-bottom:1px}p{display:block;margin:0}',
- 'div{margin:0}body,p{margin-bottom:1px}p{display:block;margin:0}'
- ],
- 'shorthand over granular with same value': [
- 'div{margin:0}.one{margin-bottom:0}.two{display:block;margin:0}',
- '.two,div{margin:0}.one{margin-bottom:0}.two{display:block}'
- ],
- 'dropping longer content at a right place': [
- '.one,a:hover{color:red}a:hover{color:#000;display:block;border-color:#000}.longer-name{color:#000;border-color:#000}',
- '.one,a:hover{color:red}.longer-name,a:hover{color:#000;border-color:#000}a:hover{display:block}'
- ],
- 'over media without overriding': [
- 'div{margin:0}@media{.one{color:red}}.two{display:block;margin:0}',
- '.two,div{margin:0}@media{.one{color:red}}.two{display:block}'
- ],
- 'over media with overriding by different value': [
- 'div{margin:0}@media{.one{margin:10px}}.two{display:block;margin:0}',
- 'div{margin:0}@media{.one{margin:10px}}.two{display:block;margin:0}'
- ],
- 'over media with overriding by same value': [
- 'div{margin:0}@media{.one{margin:0}}.two{display:block;margin:0}',
- '.two,div{margin:0}@media{.one{margin:0}}.two{display:block}'
- ],
- 'over media with overriding by a granular': [
- 'div{margin:0}@media{.one{margin-bottom:0}}.two{display:block;margin:0}',
- '.two,div{margin:0}@media{.one{margin-bottom:0}}.two{display:block}'
- ],
- 'over media with overriding by a different granular': [
- 'div{margin-top:0}@media{.one{margin-bottom:0}}.two{display:block;margin-top:0}',
- '.two,div{margin-top:0}@media{.one{margin-bottom:0}}.two{display:block}'
- ],
- 'over media with a new property': [
- 'div{margin-top:0}@media{.one{margin-top:0}}.two{display:block;margin:0}',
- 'div{margin-top:0}@media{.one{margin-top:0}}.two{display:block;margin:0}'
- ],
- 'over a property in the same selector': [
- 'div{background-size:100%}a{background:no-repeat;background-size:100%}',
- 'div{background-size:100%}a{background:no-repeat;background-size:100%}'
- ],
- 'multiple granular up to a shorthand': [
- '.one{border:1px solid #bbb}.two{border-color:#666}.three{border-width:1px;border-style:solid}',
- '.one{border:1px solid #bbb}.two{border-color:#666}.three{border-width:1px;border-style:solid}'
- ],
- 'multiple granular - complex case': [
- '.one{background:red;padding:8px 16px}.two{padding-left:16px;padding-right:16px}.three{padding-top:20px}.four{border-left:1px solid #000;border-right:1px solid #000;border-bottom:1px solid #000}.five{background-color:#fff;background-image:-moz-linear-gradient();background-image:-ms-linear-gradient();background-image:-webkit-gradient();background-image:-webkit-linear-gradient()}',
- '.one{background:red;padding:8px 16px}.two{padding-left:16px;padding-right:16px}.three{padding-top:20px}.four{border-left:1px solid #000;border-right:1px solid #000;border-bottom:1px solid #000}.five{background-color:#fff;background-image:-moz-linear-gradient();background-image:-ms-linear-gradient();background-image:-webkit-gradient();background-image:-webkit-linear-gradient()}'
- ],
- 'multiple granular - special': [
- 'input:-ms-input-placeholder{color:red;text-align:center}input::placeholder{color:red;text-align:center}',
- 'input:-ms-input-placeholder{color:red;text-align:center}input::placeholder{color:red;text-align:center}'
- ],
- 'moving one already being moved with different value': [
- '.one{color:red}.two{display:block}.three{color:red;display:inline}.four{display:inline-block}.five{color:#000}',
- '.one,.three{color:red}.two{display:block}.three{display:inline}.four{display:inline-block}.five{color:#000}'
- ],
- 'not in keyframes': [
- '@keyframes test{0%{transform:scale3d(1,1,1);opacity:1}100%{transform:scale3d(.5,.5,.5);opacity:1}}',
- '@keyframes test{0%{transform:scale3d(1,1,1);opacity:1}100%{transform:scale3d(.5,.5,.5);opacity:1}}'
- ],
- 'not in vendored keyframes': [
- '@-moz-keyframes test{0%{transform:scale3d(1,1,1);opacity:1}100%{transform:scale3d(.5,.5,.5);opacity:1}}',
- '@-moz-keyframes test{0%{transform:scale3d(1,1,1);opacity:1}100%{transform:scale3d(.5,.5,.5);opacity:1}}'
- ],
- 'with one important comment': [
- '/*! comment */a{width:100px}div{color:red}.one{display:block}.two{display:inline;color:red}',
- '/*! comment */.two,div{color:red}a{width:100px}.one{display:block}.two{display:inline}'
- ],
- 'with many important comments': [
- '/*! comment 1 *//*! comment 2 */a{width:100px}div{color:red}.one{display:block}.two{display:inline;color:red}',
- '/*! comment 1 *//*! comment 2 */.two,div{color:red}a{width:100px}.one{display:block}.two{display:inline}'
- ],
- 'with important comment and charset': [
- '@charset "utf-8";/*! comment */a{width:100px}div{color:red}.one{display:block}.two{display:inline;color:red}',
- '@charset "utf-8";/*! comment */.two,div{color:red}a{width:100px}.one{display:block}.two{display:inline}'
- ],
- 'with charset and import': [
- '@charset "UTF-8";@import url(http://fonts.googleapis.com/css?family=Lora:400,700);a{width:100px}div{color:red}.one{display:block}.two{display:inline;color:red}',
- '@charset "UTF-8";@import url(http://fonts.googleapis.com/css?family=Lora:400,700);.two,div{color:red}a{width:100px}.one{display:block}.two{display:inline}'
- ],
- 'with charset and import and comments': [
- '@charset "UTF-8";@import url(http://fonts.googleapis.com/css?family=Lora:400,700);/*! comment */a{width:100px}div{color:red}.one{display:block}.two{display:inline;color:red}',
- '@charset "UTF-8";@import url(http://fonts.googleapis.com/css?family=Lora:400,700);/*! comment */.two,div{color:red}a{width:100px}.one{display:block}.two{display:inline}'
- ],
- 'with vendor prefixed value group': [
- 'a{-moz-box-sizing:content-box;box-sizing:content-box}div{color:red}p{-moz-box-sizing:content-box;-webkit-box-sizing:content-box;box-sizing:content-box}',
- 'a{box-sizing:content-box}a,p{-moz-box-sizing:content-box}div{color:red}p{-webkit-box-sizing:content-box;box-sizing:content-box}'
- ]
- }, { advanced: true })
- )
.addBatch(
optimizerContext('@media', {
'empty': [
--- /dev/null
+var vows = require('vows');
+var optimizerContext = require('../test-helper').optimizerContext;
+
+vows.describe('restructure')
+ .addBatch(
+ optimizerContext('advanced on', {
+ 'up until changed': [
+ 'a{color:#000}div{color:red}.one{display:block}.two{display:inline;color:red}',
+ 'a{color:#000}.two,div{color:red}.one{display:block}.two{display:inline}'
+ ],
+ 'up until top': [
+ 'a{width:100px}div{color:red}.one{display:block}.two{display:inline;color:red}',
+ '.two,div{color:red}a{width:100px}.one{display:block}.two{display:inline}'
+ ],
+ 'up until top with charset': [
+ '@charset "utf-8";a{width:100px}div{color:red}.one{display:block}.two{display:inline;color:red}',
+ '@charset "utf-8";.two,div{color:red}a{width:100px}.one{display:block}.two{display:inline}'
+ ],
+ 'two at once': [
+ '.one,.two,.three{color:red;display:block}div{margin:0}.four,.five,.six{color:red;display:block}',
+ '.five,.four,.one,.six,.three,.two{color:red;display:block}div{margin:0}'
+ ],
+ 'down until changed': [
+ '.one{padding:0}.two{margin:0}.one{margin-bottom:3px}',
+ '.two{margin:0}.one{padding:0;margin-bottom:3px}'
+ ],
+ 'over shorthands': [
+ 'div{margin-top:0}.one{margin:0}.two{display:block;margin-top:0}',
+ '.two,div{margin-top:0}.one{margin:0}.two{display:block}'
+ ],
+ 'over shorthands with flush': [
+ 'div{margin-top:0}.one{margin:5px}.two{display:block;margin-top:0}.three{color:red}.four{margin-top:0}',
+ 'div{margin-top:0}.one{margin:5px}.four,.two{margin-top:0}.two{display:block}.three{color:red}'
+ ],
+ 'over shorthand - border': [
+ '.one{border-color:red}.two{border:1px solid}.three{color:#fff;border-color:red}',
+ '.one{border-color:red}.two{border:1px solid}.three{color:#fff;border-color:red}'
+ ],
+ 'granuar over granular': [
+ 'div{margin-top:0}.one{margin-bottom:2px}.two{display:block;margin-top:0}',
+ '.two,div{margin-top:0}.one{margin-bottom:2px}.two{display:block}'
+ ],
+ 'shorthand over granular with different value': [
+ 'div{margin:0}.one{margin-bottom:1px}.two{display:block;margin:0}',
+ 'div{margin:0}.one{margin-bottom:1px}.two{display:block;margin:0}'
+ ],
+ 'shorthand over granular with different value for simple tags': [
+ 'div{margin:0}body{margin-bottom:1px}p{display:block;margin:0}',
+ 'div,p{margin:0}body{margin-bottom:1px}p{display:block}'
+ ],
+ 'shorthand over granular with different value for simple tags when tag match': [
+ 'div{margin:0}body,p{margin-bottom:1px}p{display:block;margin:0}',
+ 'div{margin:0}body,p{margin-bottom:1px}p{display:block;margin:0}'
+ ],
+ 'shorthand over granular with same value': [
+ 'div{margin:0}.one{margin-bottom:0}.two{display:block;margin:0}',
+ '.two,div{margin:0}.one{margin-bottom:0}.two{display:block}'
+ ],
+ 'dropping longer content at a right place': [
+ '.one,a:hover{color:red}a:hover{color:#000;display:block;border-color:#000}.longer-name{color:#000;border-color:#000}',
+ '.one,a:hover{color:red}.longer-name,a:hover{color:#000;border-color:#000}a:hover{display:block}'
+ ],
+ 'over media without overriding': [
+ 'div{margin:0}@media{.one{color:red}}.two{display:block;margin:0}',
+ '.two,div{margin:0}@media{.one{color:red}}.two{display:block}'
+ ],
+ 'over media with overriding by different value': [
+ 'div{margin:0}@media{.one{margin:10px}}.two{display:block;margin:0}',
+ 'div{margin:0}@media{.one{margin:10px}}.two{display:block;margin:0}'
+ ],
+ 'over media with overriding by same value': [
+ 'div{margin:0}@media{.one{margin:0}}.two{display:block;margin:0}',
+ '.two,div{margin:0}@media{.one{margin:0}}.two{display:block}'
+ ],
+ 'over media with overriding by a granular': [
+ 'div{margin:0}@media{.one{margin-bottom:0}}.two{display:block;margin:0}',
+ '.two,div{margin:0}@media{.one{margin-bottom:0}}.two{display:block}'
+ ],
+ 'over media with overriding by a different granular': [
+ 'div{margin-top:0}@media{.one{margin-bottom:0}}.two{display:block;margin-top:0}',
+ '.two,div{margin-top:0}@media{.one{margin-bottom:0}}.two{display:block}'
+ ],
+ 'over media with a new property': [
+ 'div{margin-top:0}@media{.one{margin-top:0}}.two{display:block;margin:0}',
+ 'div{margin-top:0}@media{.one{margin-top:0}}.two{display:block;margin:0}'
+ ],
+ 'over a property in the same selector': [
+ 'div{background-size:100%}a{background:no-repeat;background-size:100%}',
+ 'div{background-size:100%}a{background:no-repeat;background-size:100%}'
+ ],
+ 'multiple granular up to a shorthand': [
+ '.one{border:1px solid #bbb}.two{border-color:#666}.three{border-width:1px;border-style:solid}',
+ '.one{border:1px solid #bbb}.two{border-color:#666}.three{border-width:1px;border-style:solid}'
+ ],
+ 'multiple granular - complex case': [
+ '.one{background:red;padding:8px 16px}.two{padding-left:16px;padding-right:16px}.three{padding-top:20px}.four{border-left:1px solid #000;border-right:1px solid #000;border-bottom:1px solid #000}.five{background-color:#fff;background-image:-moz-linear-gradient();background-image:-ms-linear-gradient();background-image:-webkit-gradient();background-image:-webkit-linear-gradient()}',
+ '.one{background:red;padding:8px 16px}.two{padding-left:16px;padding-right:16px}.three{padding-top:20px}.four{border-left:1px solid #000;border-right:1px solid #000;border-bottom:1px solid #000}.five{background-color:#fff;background-image:-moz-linear-gradient();background-image:-ms-linear-gradient();background-image:-webkit-gradient();background-image:-webkit-linear-gradient()}'
+ ],
+ 'multiple granular - special': [
+ 'input:-ms-input-placeholder{color:red;text-align:center}input::placeholder{color:red;text-align:center}',
+ 'input:-ms-input-placeholder{color:red;text-align:center}input::placeholder{color:red;text-align:center}'
+ ],
+ 'moving one already being moved with different value': [
+ '.one{color:red}.two{display:block}.three{color:red;display:inline}.four{display:inline-block}.five{color:#000}',
+ '.one,.three{color:red}.two{display:block}.three{display:inline}.four{display:inline-block}.five{color:#000}'
+ ],
+ 'not in keyframes': [
+ '@keyframes test{0%{transform:scale3d(1,1,1);opacity:1}100%{transform:scale3d(.5,.5,.5);opacity:1}}',
+ '@keyframes test{0%{transform:scale3d(1,1,1);opacity:1}100%{transform:scale3d(.5,.5,.5);opacity:1}}'
+ ],
+ 'not in vendored keyframes': [
+ '@-moz-keyframes test{0%{transform:scale3d(1,1,1);opacity:1}100%{transform:scale3d(.5,.5,.5);opacity:1}}',
+ '@-moz-keyframes test{0%{transform:scale3d(1,1,1);opacity:1}100%{transform:scale3d(.5,.5,.5);opacity:1}}'
+ ],
+ 'with one important comment': [
+ '/*! comment */a{width:100px}div{color:red}.one{display:block}.two{display:inline;color:red}',
+ '/*! comment */.two,div{color:red}a{width:100px}.one{display:block}.two{display:inline}'
+ ],
+ 'with many important comments': [
+ '/*! comment 1 *//*! comment 2 */a{width:100px}div{color:red}.one{display:block}.two{display:inline;color:red}',
+ '/*! comment 1 *//*! comment 2 */.two,div{color:red}a{width:100px}.one{display:block}.two{display:inline}'
+ ],
+ 'with important comment and charset': [
+ '@charset "utf-8";/*! comment */a{width:100px}div{color:red}.one{display:block}.two{display:inline;color:red}',
+ '@charset "utf-8";/*! comment */.two,div{color:red}a{width:100px}.one{display:block}.two{display:inline}'
+ ],
+ 'with charset and import': [
+ '@charset "UTF-8";@import url(http://fonts.googleapis.com/css?family=Lora:400,700);a{width:100px}div{color:red}.one{display:block}.two{display:inline;color:red}',
+ '@charset "UTF-8";@import url(http://fonts.googleapis.com/css?family=Lora:400,700);.two,div{color:red}a{width:100px}.one{display:block}.two{display:inline}'
+ ],
+ 'with charset and import and comments': [
+ '@charset "UTF-8";@import url(http://fonts.googleapis.com/css?family=Lora:400,700);/*! comment */a{width:100px}div{color:red}.one{display:block}.two{display:inline;color:red}',
+ '@charset "UTF-8";@import url(http://fonts.googleapis.com/css?family=Lora:400,700);/*! comment */.two,div{color:red}a{width:100px}.one{display:block}.two{display:inline}'
+ ],
+ 'with vendor prefixed value group': [
+ 'a{-moz-box-sizing:content-box;box-sizing:content-box}div{color:red}p{-moz-box-sizing:content-box;-webkit-box-sizing:content-box;box-sizing:content-box}',
+ 'a{box-sizing:content-box}a,p{-moz-box-sizing:content-box}div{color:red}p{-webkit-box-sizing:content-box;box-sizing:content-box}'
+ ]
+ })
+ )
+ .addBatch(
+ optimizerContext('advanced off', {
+ 'up until changed': [
+ 'a{color:#000}div{color:red}.one{display:block}.two{display:inline;color:red}',
+ 'a{color:#000}div{color:red}.one{display:block}.two{display:inline;color:red}'
+ ]
+ }, { advanced: false })
+ )
+ .export(module);