diff options
Diffstat (limited to 'node_modules/csso/lib/compressor/restructure/4-restructShorthand.js')
| -rw-r--r-- | node_modules/csso/lib/compressor/restructure/4-restructShorthand.js | 430 |
1 files changed, 430 insertions, 0 deletions
diff --git a/node_modules/csso/lib/compressor/restructure/4-restructShorthand.js b/node_modules/csso/lib/compressor/restructure/4-restructShorthand.js new file mode 100644 index 00000000..aa95e3cc --- /dev/null +++ b/node_modules/csso/lib/compressor/restructure/4-restructShorthand.js @@ -0,0 +1,430 @@ +var List = require('../../utils/list.js'); +var translate = require('../../utils/translate.js'); +var walkRulesRight = require('../../utils/walk.js').rulesRight; + +var REPLACE = 1; +var REMOVE = 2; +var TOP = 0; +var RIGHT = 1; +var BOTTOM = 2; +var LEFT = 3; +var SIDES = ['top', 'right', 'bottom', 'left']; +var SIDE = { + 'margin-top': 'top', + 'margin-right': 'right', + 'margin-bottom': 'bottom', + 'margin-left': 'left', + + 'padding-top': 'top', + 'padding-right': 'right', + 'padding-bottom': 'bottom', + 'padding-left': 'left', + + 'border-top-color': 'top', + 'border-right-color': 'right', + 'border-bottom-color': 'bottom', + 'border-left-color': 'left', + 'border-top-width': 'top', + 'border-right-width': 'right', + 'border-bottom-width': 'bottom', + 'border-left-width': 'left', + 'border-top-style': 'top', + 'border-right-style': 'right', + 'border-bottom-style': 'bottom', + 'border-left-style': 'left' +}; +var MAIN_PROPERTY = { + 'margin': 'margin', + 'margin-top': 'margin', + 'margin-right': 'margin', + 'margin-bottom': 'margin', + 'margin-left': 'margin', + + 'padding': 'padding', + 'padding-top': 'padding', + 'padding-right': 'padding', + 'padding-bottom': 'padding', + 'padding-left': 'padding', + + 'border-color': 'border-color', + 'border-top-color': 'border-color', + 'border-right-color': 'border-color', + 'border-bottom-color': 'border-color', + 'border-left-color': 'border-color', + 'border-width': 'border-width', + 'border-top-width': 'border-width', + 'border-right-width': 'border-width', + 'border-bottom-width': 'border-width', + 'border-left-width': 'border-width', + 'border-style': 'border-style', + 'border-top-style': 'border-style', + 'border-right-style': 'border-style', + 'border-bottom-style': 'border-style', + 'border-left-style': 'border-style' +}; + +function TRBL(name) { + this.name = name; + this.info = null; + this.iehack = undefined; + this.sides = { + 'top': null, + 'right': null, + 'bottom': null, + 'left': null + }; +} + +TRBL.prototype.getValueSequence = function(value, count) { + var values = []; + var iehack = ''; + var hasBadValues = value.sequence.some(function(child) { + var special = false; + + switch (child.type) { + case 'Identifier': + switch (child.name) { + case '\\0': + case '\\9': + iehack = child.name; + return; + + case 'inherit': + case 'initial': + case 'unset': + case 'revert': + special = child.name; + break; + } + break; + + case 'Dimension': + switch (child.unit) { + // is not supported until IE11 + case 'rem': + + // v* units is too buggy across browsers and better + // don't merge values with those units + case 'vw': + case 'vh': + case 'vmin': + case 'vmax': + case 'vm': // IE9 supporting "vm" instead of "vmin". + special = child.unit; + break; + } + break; + + case 'Hash': // color + case 'Number': + case 'Percentage': + break; + + case 'Function': + special = child.name; + break; + + case 'Space': + return false; // ignore space + + default: + return true; // bad value + } + + values.push({ + node: child, + special: special, + important: value.important + }); + }); + + if (hasBadValues || values.length > count) { + return false; + } + + if (typeof this.iehack === 'string' && this.iehack !== iehack) { + return false; + } + + this.iehack = iehack; // move outside + + return values; +}; + +TRBL.prototype.canOverride = function(side, value) { + var currentValue = this.sides[side]; + + return !currentValue || (value.important && !currentValue.important); +}; + +TRBL.prototype.add = function(name, value, info) { + function attemptToAdd() { + var sides = this.sides; + var side = SIDE[name]; + + if (side) { + if (side in sides === false) { + return false; + } + + var values = this.getValueSequence(value, 1); + + if (!values || !values.length) { + return false; + } + + // can mix only if specials are equal + for (var key in sides) { + if (sides[key] !== null && sides[key].special !== values[0].special) { + return false; + } + } + + if (!this.canOverride(side, values[0])) { + return true; + } + + sides[side] = values[0]; + return true; + } else if (name === this.name) { + var values = this.getValueSequence(value, 4); + + if (!values || !values.length) { + return false; + } + + switch (values.length) { + case 1: + values[RIGHT] = values[TOP]; + values[BOTTOM] = values[TOP]; + values[LEFT] = values[TOP]; + break; + + case 2: + values[BOTTOM] = values[TOP]; + values[LEFT] = values[RIGHT]; + break; + + case 3: + values[LEFT] = values[RIGHT]; + break; + } + + // can mix only if specials are equal + for (var i = 0; i < 4; i++) { + for (var key in sides) { + if (sides[key] !== null && sides[key].special !== values[i].special) { + return false; + } + } + } + + for (var i = 0; i < 4; i++) { + if (this.canOverride(SIDES[i], values[i])) { + sides[SIDES[i]] = values[i]; + } + } + + return true; + } + } + + if (!attemptToAdd.call(this)) { + return false; + } + + if (this.info) { + this.info = { + primary: this.info, + merged: info + }; + } else { + this.info = info; + } + + return true; +}; + +TRBL.prototype.isOkToMinimize = function() { + var top = this.sides.top; + var right = this.sides.right; + var bottom = this.sides.bottom; + var left = this.sides.left; + + if (top && right && bottom && left) { + var important = + top.important + + right.important + + bottom.important + + left.important; + + return important === 0 || important === 4; + } + + return false; +}; + +TRBL.prototype.getValue = function() { + var result = []; + var sides = this.sides; + var values = [ + sides.top, + sides.right, + sides.bottom, + sides.left + ]; + var stringValues = [ + translate(sides.top.node), + translate(sides.right.node), + translate(sides.bottom.node), + translate(sides.left.node) + ]; + + if (stringValues[LEFT] === stringValues[RIGHT]) { + values.pop(); + if (stringValues[BOTTOM] === stringValues[TOP]) { + values.pop(); + if (stringValues[RIGHT] === stringValues[TOP]) { + values.pop(); + } + } + } + + for (var i = 0; i < values.length; i++) { + if (i) { + result.push({ type: 'Space' }); + } + + result.push(values[i].node); + } + + if (this.iehack) { + result.push({ type: 'Space' }, { + type: 'Identifier', + info: {}, + name: this.iehack + }); + } + + return { + type: 'Value', + info: {}, + important: sides.top.important, + sequence: new List(result) + }; +}; + +TRBL.prototype.getProperty = function() { + return { + type: 'Property', + info: {}, + name: this.name + }; +}; + +function processRuleset(ruleset, shorts, shortDeclarations, lastShortSelector) { + var declarations = ruleset.block.declarations; + var selector = ruleset.selector.selectors.first().id; + + ruleset.block.declarations.eachRight(function(declaration, item) { + var property = declaration.property.name; + + if (!MAIN_PROPERTY.hasOwnProperty(property)) { + return; + } + + var key = MAIN_PROPERTY[property]; + var shorthand; + var operation; + + if (!lastShortSelector || selector === lastShortSelector) { + if (key in shorts) { + operation = REMOVE; + shorthand = shorts[key]; + } + } + + if (!shorthand || !shorthand.add(property, declaration.value, declaration.info)) { + operation = REPLACE; + shorthand = new TRBL(key); + + // if can't parse value ignore it and break shorthand sequence + if (!shorthand.add(property, declaration.value, declaration.info)) { + lastShortSelector = null; + return; + } + } + + shorts[key] = shorthand; + shortDeclarations.push({ + operation: operation, + block: declarations, + item: item, + shorthand: shorthand + }); + + lastShortSelector = selector; + }); + + return lastShortSelector; +}; + +function processShorthands(shortDeclarations, markDeclaration) { + shortDeclarations.forEach(function(item) { + var shorthand = item.shorthand; + + if (!shorthand.isOkToMinimize()) { + return; + } + + if (item.operation === REPLACE) { + item.item.data = markDeclaration({ + type: 'Declaration', + info: shorthand.info, + property: shorthand.getProperty(), + value: shorthand.getValue(), + id: 0, + length: 0, + fingerprint: null + }); + } else { + item.block.remove(item.item); + } + }); +}; + +module.exports = function restructBlock(ast, indexer) { + var stylesheetMap = {}; + var shortDeclarations = []; + + walkRulesRight(ast, function(node) { + if (node.type !== 'Ruleset') { + return; + } + + var stylesheet = this.stylesheet; + var rulesetId = (node.pseudoSignature || '') + '|' + node.selector.selectors.first().id; + var rulesetMap; + var shorts; + + if (!stylesheetMap.hasOwnProperty(stylesheet.id)) { + rulesetMap = { + lastShortSelector: null + }; + stylesheetMap[stylesheet.id] = rulesetMap; + } else { + rulesetMap = stylesheetMap[stylesheet.id]; + } + + if (rulesetMap.hasOwnProperty(rulesetId)) { + shorts = rulesetMap[rulesetId]; + } else { + shorts = {}; + rulesetMap[rulesetId] = shorts; + } + + rulesetMap.lastShortSelector = processRuleset.call(this, node, shorts, shortDeclarations, rulesetMap.lastShortSelector); + }); + + processShorthands(shortDeclarations, indexer.declaration); +}; |
