You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

262 lines
8.9 KiB

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

var resolveProperty = require('../../utils/names.js').property;
var resolveKeyword = require('../../utils/names.js').keyword;
var walkRulesRight = require('../../utils/walk.js').rulesRight;
var translate = require('../../utils/translate.js');
var dontRestructure = {
'src': 1 // https://github.com/afelix/csso/issues/50
};
var DONT_MIX_VALUE = {
// https://developer.mozilla.org/en-US/docs/Web/CSS/display#Browser_compatibility
'display': /table|ruby|flex|-(flex)?box$|grid|contents|run-in/i,
// https://developer.mozilla.org/en/docs/Web/CSS/text-align
'text-align': /^(start|end|match-parent|justify-all)$/i
};
var CURSOR_SAFE_VALUE = [
'auto', 'crosshair', 'default', 'move', 'text', 'wait', 'help',
'n-resize', 'e-resize', 's-resize', 'w-resize',
'ne-resize', 'nw-resize', 'se-resize', 'sw-resize',
'pointer', 'progress', 'not-allowed', 'no-drop', 'vertical-text', 'all-scroll',
'col-resize', 'row-resize'
];
var NEEDLESS_TABLE = {
'border-width': ['border'],
'border-style': ['border'],
'border-color': ['border'],
'border-top': ['border'],
'border-right': ['border'],
'border-bottom': ['border'],
'border-left': ['border'],
'border-top-width': ['border-top', 'border-width', 'border'],
'border-right-width': ['border-right', 'border-width', 'border'],
'border-bottom-width': ['border-bottom', 'border-width', 'border'],
'border-left-width': ['border-left', 'border-width', 'border'],
'border-top-style': ['border-top', 'border-style', 'border'],
'border-right-style': ['border-right', 'border-style', 'border'],
'border-bottom-style': ['border-bottom', 'border-style', 'border'],
'border-left-style': ['border-left', 'border-style', 'border'],
'border-top-color': ['border-top', 'border-color', 'border'],
'border-right-color': ['border-right', 'border-color', 'border'],
'border-bottom-color': ['border-bottom', 'border-color', 'border'],
'border-left-color': ['border-left', 'border-color', 'border'],
'margin-top': ['margin'],
'margin-right': ['margin'],
'margin-bottom': ['margin'],
'margin-left': ['margin'],
'padding-top': ['padding'],
'padding-right': ['padding'],
'padding-bottom': ['padding'],
'padding-left': ['padding'],
'font-style': ['font'],
'font-variant': ['font'],
'font-weight': ['font'],
'font-size': ['font'],
'font-family': ['font'],
'list-style-type': ['list-style'],
'list-style-position': ['list-style'],
'list-style-image': ['list-style']
};
function getPropertyFingerprint(propertyName, declaration, fingerprints) {
var realName = resolveProperty(propertyName).name;
if (realName === 'background' ||
(realName === 'filter' && declaration.value.sequence.first().type === 'Progid')) {
return propertyName + ':' + translate(declaration.value);
}
var declarationId = declaration.id;
var fingerprint = fingerprints[declarationId];
if (!fingerprint) {
var vendorId = '';
var iehack = '';
var special = {};
declaration.value.sequence.each(function walk(node) {
switch (node.type) {
case 'Argument':
case 'Value':
case 'Braces':
node.sequence.each(walk);
break;
case 'Identifier':
var name = node.name;
if (!vendorId) {
vendorId = resolveKeyword(name).vendor;
}
if (/\\[09]/.test(name)) {
iehack = RegExp.lastMatch;
}
if (realName === 'cursor') {
if (CURSOR_SAFE_VALUE.indexOf(name) === -1) {
special[name] = true;
}
} else if (DONT_MIX_VALUE.hasOwnProperty(realName)) {
if (DONT_MIX_VALUE[realName].test(name)) {
special[name] = true;
}
}
break;
case 'Function':
var name = node.name;
if (!vendorId) {
vendorId = resolveKeyword(name).vendor;
}
if (name === 'rect') {
// there are 2 forms of rect:
// rect(<top>, <right>, <bottom>, <left>) - standart
// rect(<top> <right> <bottom> <left>) backwards compatible syntax
// only the same form values can be merged
if (node.arguments.size < 4) {
name = 'rect-backward';
}
}
special[name + '()'] = true;
// check nested tokens too
node.arguments.each(walk);
break;
case 'Dimension':
var unit = node.unit;
switch (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[unit] = true;
break;
}
break;
}
});
fingerprint = '|' + Object.keys(special).sort() + '|' + iehack + vendorId;
fingerprints[declarationId] = fingerprint;
}
return propertyName + fingerprint;
}
function needless(props, declaration, fingerprints) {
var property = resolveProperty(declaration.property.name);
if (NEEDLESS_TABLE.hasOwnProperty(property.name)) {
var table = NEEDLESS_TABLE[property.name];
for (var i = 0; i < table.length; i++) {
var ppre = getPropertyFingerprint(property.prefix + table[i], declaration, fingerprints);
var prev = props[ppre];
if (prev && (!declaration.value.important || prev.item.data.value.important)) {
return prev;
}
}
}
}
function processRuleset(ruleset, item, list, props, fingerprints) {
var declarations = ruleset.block.declarations;
declarations.eachRight(function(declaration, declarationItem) {
var property = declaration.property.name;
var fingerprint = getPropertyFingerprint(property, declaration, fingerprints);
var prev = props[fingerprint];
if (prev && !dontRestructure.hasOwnProperty(property)) {
if (declaration.value.important && !prev.item.data.value.important) {
props[fingerprint] = {
block: declarations,
item: declarationItem
};
prev.block.remove(prev.item);
declaration.info = {
primary: declaration.info,
merged: prev.item.data.info
};
} else {
declarations.remove(declarationItem);
prev.item.data.info = {
primary: prev.item.data.info,
merged: declaration.info
};
}
} else {
var prev = needless(props, declaration, fingerprints);
if (prev) {
declarations.remove(declarationItem);
prev.item.data.info = {
primary: prev.item.data.info,
merged: declaration.info
};
} else {
declaration.fingerprint = fingerprint;
props[fingerprint] = {
block: declarations,
item: declarationItem
};
}
}
});
if (declarations.isEmpty()) {
list.remove(item);
}
};
module.exports = function restructBlock(ast) {
var stylesheetMap = {};
var fingerprints = Object.create(null);
walkRulesRight(ast, function(node, item, list) {
if (node.type !== 'Ruleset') {
return;
}
var stylesheet = this.stylesheet;
var rulesetId = (node.pseudoSignature || '') + '|' + node.selector.selectors.first().id;
var rulesetMap;
var props;
if (!stylesheetMap.hasOwnProperty(stylesheet.id)) {
rulesetMap = {};
stylesheetMap[stylesheet.id] = rulesetMap;
} else {
rulesetMap = stylesheetMap[stylesheet.id];
}
if (rulesetMap.hasOwnProperty(rulesetId)) {
props = rulesetMap[rulesetId];
} else {
props = {};
rulesetMap[rulesetId] = props;
}
processRuleset.call(this, node, item, list, props, fingerprints);
});
};