--- /dev/null
+module.exports = function Optimizer() {
+ var tokenize = function(body) {
+ var tokens = body.split(';');
+ var keyValues = [];
+
+ for (var i = 0, l = tokens.length; i < l; i++) {
+ var token = tokens[i];
+ var firstColon = token.indexOf(':');
+ keyValues.push([token.substring(0, firstColon), token.substring(firstColon + 1)]);
+ }
+
+ return keyValues;
+ };
+
+ var optimize = function(tokenized, allowAdjacent) {
+ var merged = [];
+ var properties = [];
+ var lastProperty = null;
+
+ for (var i = 0, l = tokenized.length; i < l; i++) {
+ var property = tokenized[i][0];
+ var value = tokenized[i][1];
+ var alreadyIn = properties.indexOf(property);
+
+ if (alreadyIn > -1 && merged[alreadyIn][1].indexOf('!important') > 0 && value.indexOf('!important') == -1)
+ continue;
+
+ // comment is necessary - we assume that if two properties are one after another
+ // then it is intentional way of redefining property which may not be widely supported
+ // however if `allowAdjacent` is set then the rule does not apply (see merging two adjacent selectors)
+ if (alreadyIn > -1 && (allowAdjacent || lastProperty != property)) {
+ merged.splice(alreadyIn, 1);
+ properties.splice(alreadyIn, 1);
+ }
+
+ merged.push([property, value]);
+ properties.push(property);
+
+ lastProperty = property;
+ }
+
+ return merged;
+ };
+
+ var rebuild = function(tokenized) {
+ var flat = [];
+
+ for (var i = 0, l = tokenized.length; i < l; i++) {
+ flat.push(tokenized[i][0] + ':' + tokenized[i][1]);
+ }
+
+ return flat.join(';');
+ };
+
+ return {
+ process: function(body, allowAdjacent) {
+ var tokenized = tokenize(body);
+ if (tokenized.length < 2)
+ return body;
+
+ var optimized = optimize(tokenized, allowAdjacent);
+ return rebuild(optimized);
+ }
+ };
+};
var Tokenizer = require('./tokenizer');
+var PropertyOptimizer = require('../properties/optimizer');
module.exports = function Optimizer(data, options) {
var specialSelectors = {
'ie8': /(\-moz\-|\-ms\-|\-o\-|\-webkit\-|:not|:target|:visited|:empty|:first\-of|:last|:nth|:only|:root)/
};
+ var propertyOptimizer = new PropertyOptimizer();
+
var cleanUpSelector = function(selectors) {
var plain = [];
selectors = selectors.split(',');
return specialSelectors[options.selectorsMergeMode || '*'].test(selector);
};
- var mergeProperties = function(body, allowAdjacent) {
- var merged = [];
- var properties = [];
- var flat = [];
- var tokenized = body.split(';');
- var lastKey = null;
-
- if (tokenized.length == 1 && tokenized[0] === '')
- return body;
-
- for (var i = 0, l = tokenized.length; i < l; i++) {
- var firstColon = tokenized[i].indexOf(':');
- var key = tokenized[i].substring(0, firstColon);
- var value = tokenized[i].substring(firstColon + 1);
- var alreadyIn = properties.indexOf(key);
-
- if (alreadyIn > -1 && merged[alreadyIn][1].indexOf('!important') > 0 && value.indexOf('!important') == -1)
- continue;
-
- // comment is necessary - we assume that if two keys are one after another
- // then it is intentional way of redefining property which may not be widely supported
- // however if `allowAdjacent` is set then the rule does not apply (see merging two adjacent selectors)
- if (alreadyIn > -1 && (allowAdjacent || lastKey != key)) {
- merged.splice(alreadyIn, 1);
- properties.splice(alreadyIn, 1);
- }
-
- merged.push([key, value]);
- properties.push(key);
-
- lastKey = key;
- }
-
- for (var j = 0, m = merged.length; j < m; j++) {
- flat.push(merged[j].join(':'));
- }
-
- return flat.join(';');
- };
-
var removeDuplicates = function(tokens) {
var matched = {};
var forRemoval = [];
continue;
if (token.selector == lastToken.selector) {
- lastToken.body = mergeProperties(lastToken.body + ';' + token.body, true);
+ lastToken.body = propertyOptimizer.process(lastToken.body + ';' + token.body, true);
forRemoval.push(i);
} else if (token.body == lastToken.body && !isSpecial(token.selector) && !isSpecial(lastToken.selector)) {
lastToken.selector = cleanUpSelector(lastToken.selector + ',' + token.selector);
if (token.selector) {
token.selector = cleanUpSelector(token.selector);
- token.body = mergeProperties(token.body, false);
+ token.body = propertyOptimizer.process(token.body, false);
} else if (token.block) {
optimize(token.body);
}