From b00a36ec631895c507ce1590a5f14929df8f44d0 Mon Sep 17 00:00:00 2001 From: GoalSmashers Date: Wed, 30 Oct 2013 20:57:49 +0100 Subject: [PATCH] Refactors property merging into a separate Optimizer singleton. --- lib/properties/optimizer.js | 65 +++++++++++++++++++++++++++++++++++++ lib/selectors/optimizer.js | 47 +++------------------------ 2 files changed, 70 insertions(+), 42 deletions(-) create mode 100644 lib/properties/optimizer.js diff --git a/lib/properties/optimizer.js b/lib/properties/optimizer.js new file mode 100644 index 00000000..2b1bc55b --- /dev/null +++ b/lib/properties/optimizer.js @@ -0,0 +1,65 @@ +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); + } + }; +}; diff --git a/lib/selectors/optimizer.js b/lib/selectors/optimizer.js index 621f4422..11145bcb 100644 --- a/lib/selectors/optimizer.js +++ b/lib/selectors/optimizer.js @@ -1,4 +1,5 @@ var Tokenizer = require('./tokenizer'); +var PropertyOptimizer = require('../properties/optimizer'); module.exports = function Optimizer(data, options) { var specialSelectors = { @@ -6,6 +7,8 @@ module.exports = function Optimizer(data, options) { '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(','); @@ -24,46 +27,6 @@ module.exports = function Optimizer(data, options) { 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 = []; @@ -102,7 +65,7 @@ module.exports = function Optimizer(data, options) { 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); @@ -124,7 +87,7 @@ module.exports = function Optimizer(data, options) { 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); } -- 2.34.1