158 lines
5.8 KiB
JavaScript
158 lines
5.8 KiB
JavaScript
|
var List = require('../../utils/list.js');
|
||
|
var utils = require('./utils.js');
|
||
|
var walkRulesRight = require('../../utils/walk.js').rulesRight;
|
||
|
|
||
|
function calcSelectorLength(list) {
|
||
|
var length = 0;
|
||
|
|
||
|
list.each(function(data) {
|
||
|
length += data.id.length + 1;
|
||
|
});
|
||
|
|
||
|
return length - 1;
|
||
|
}
|
||
|
|
||
|
function calcDeclarationsLength(tokens) {
|
||
|
var length = 0;
|
||
|
|
||
|
for (var i = 0; i < tokens.length; i++) {
|
||
|
length += tokens[i].length;
|
||
|
}
|
||
|
|
||
|
return (
|
||
|
length + // declarations
|
||
|
tokens.length - 1 // delimeters
|
||
|
);
|
||
|
}
|
||
|
|
||
|
function processRuleset(node, item, list) {
|
||
|
var avoidRulesMerge = this.stylesheet.avoidRulesMerge;
|
||
|
var selectors = node.selector.selectors;
|
||
|
var block = node.block;
|
||
|
var disallowDownMarkers = Object.create(null);
|
||
|
var allowMergeUp = true;
|
||
|
var allowMergeDown = true;
|
||
|
|
||
|
list.prevUntil(item.prev, function(prev, prevItem) {
|
||
|
// skip non-ruleset node if safe
|
||
|
if (prev.type !== 'Ruleset') {
|
||
|
return utils.unsafeToSkipNode.call(selectors, prev);
|
||
|
}
|
||
|
|
||
|
var prevSelectors = prev.selector.selectors;
|
||
|
var prevBlock = prev.block;
|
||
|
|
||
|
if (node.pseudoSignature !== prev.pseudoSignature) {
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
allowMergeDown = !prevSelectors.some(function(selector) {
|
||
|
return selector.compareMarker in disallowDownMarkers;
|
||
|
});
|
||
|
|
||
|
// try prev ruleset if simpleselectors has no equal specifity and element selector
|
||
|
if (!allowMergeDown && !allowMergeUp) {
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
// try to join by selectors
|
||
|
if (allowMergeUp && utils.isEqualLists(prevSelectors, selectors)) {
|
||
|
prevBlock.declarations.appendList(block.declarations);
|
||
|
list.remove(item);
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
// try to join by properties
|
||
|
var diff = utils.compareDeclarations(block.declarations, prevBlock.declarations);
|
||
|
|
||
|
// console.log(diff.eq, diff.ne1, diff.ne2);
|
||
|
|
||
|
if (diff.eq.length) {
|
||
|
if (!diff.ne1.length && !diff.ne2.length) {
|
||
|
// equal blocks
|
||
|
if (allowMergeDown) {
|
||
|
utils.addSelectors(selectors, prevSelectors);
|
||
|
list.remove(prevItem);
|
||
|
}
|
||
|
|
||
|
return true;
|
||
|
} else if (!avoidRulesMerge) { /* probably we don't need to prevent those merges for @keyframes
|
||
|
TODO: need to be checked */
|
||
|
|
||
|
if (diff.ne1.length && !diff.ne2.length) {
|
||
|
// prevBlock is subset block
|
||
|
var selectorLength = calcSelectorLength(selectors);
|
||
|
var blockLength = calcDeclarationsLength(diff.eq); // declarations length
|
||
|
|
||
|
if (allowMergeUp && selectorLength < blockLength) {
|
||
|
utils.addSelectors(prevSelectors, selectors);
|
||
|
block.declarations = new List(diff.ne1);
|
||
|
}
|
||
|
} else if (!diff.ne1.length && diff.ne2.length) {
|
||
|
// node is subset of prevBlock
|
||
|
var selectorLength = calcSelectorLength(prevSelectors);
|
||
|
var blockLength = calcDeclarationsLength(diff.eq); // declarations length
|
||
|
|
||
|
if (allowMergeDown && selectorLength < blockLength) {
|
||
|
utils.addSelectors(selectors, prevSelectors);
|
||
|
prevBlock.declarations = new List(diff.ne2);
|
||
|
}
|
||
|
} else {
|
||
|
// diff.ne1.length && diff.ne2.length
|
||
|
// extract equal block
|
||
|
var newSelector = {
|
||
|
type: 'Selector',
|
||
|
info: {},
|
||
|
selectors: utils.addSelectors(prevSelectors.copy(), selectors)
|
||
|
};
|
||
|
var newBlockLength = calcSelectorLength(newSelector.selectors) + 2; // selectors length + curly braces length
|
||
|
var blockLength = calcDeclarationsLength(diff.eq); // declarations length
|
||
|
|
||
|
// create new ruleset if declarations length greater than
|
||
|
// ruleset description overhead
|
||
|
if (allowMergeDown && blockLength >= newBlockLength) {
|
||
|
var newRuleset = {
|
||
|
type: 'Ruleset',
|
||
|
info: {},
|
||
|
pseudoSignature: node.pseudoSignature,
|
||
|
selector: newSelector,
|
||
|
block: {
|
||
|
type: 'Block',
|
||
|
info: {},
|
||
|
declarations: new List(diff.eq)
|
||
|
}
|
||
|
};
|
||
|
|
||
|
block.declarations = new List(diff.ne1);
|
||
|
prevBlock.declarations = new List(diff.ne2.concat(diff.ne2overrided));
|
||
|
list.insert(list.createItem(newRuleset), prevItem);
|
||
|
return true;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (allowMergeUp) {
|
||
|
// TODO: disallow up merge only if any property interception only (i.e. diff.ne2overrided.length > 0);
|
||
|
// await property families to find property interception correctly
|
||
|
allowMergeUp = !prevSelectors.some(function(prevSelector) {
|
||
|
return selectors.some(function(selector) {
|
||
|
return selector.compareMarker === prevSelector.compareMarker;
|
||
|
});
|
||
|
});
|
||
|
}
|
||
|
|
||
|
prevSelectors.each(function(data) {
|
||
|
disallowDownMarkers[data.compareMarker] = true;
|
||
|
});
|
||
|
});
|
||
|
};
|
||
|
|
||
|
module.exports = function restructRuleset(ast) {
|
||
|
walkRulesRight(ast, function(node, item, list) {
|
||
|
if (node.type === 'Ruleset') {
|
||
|
processRuleset.call(this, node, item, list);
|
||
|
}
|
||
|
});
|
||
|
};
|