|
|
var populateComponents = require('./properties/populate-components');
var wrapForOptimizing = require('../wrap-for-optimizing').single; var restoreFromOptimizing = require('../restore-from-optimizing');
var Token = require('../../tokenizer/token');
var animationNameRegex = /^(\-moz\-|\-o\-|\-webkit\-)?animation-name$/; var animationRegex = /^(\-moz\-|\-o\-|\-webkit\-)?animation$/; var keyframeRegex = /^@(\-moz\-|\-o\-|\-webkit\-)?keyframes /; var importantRegex = /\s{0,31}!important$/; var optionalMatchingQuotesRegex = /^(['"]?)(.*)\1$/;
function normalize(value) { return value .replace(optionalMatchingQuotesRegex, '$2') .replace(importantRegex, ''); }
function removeUnusedAtRules(tokens, context) { removeUnusedAtRule(tokens, matchCounterStyle, markCounterStylesAsUsed, context); removeUnusedAtRule(tokens, matchFontFace, markFontFacesAsUsed, context); removeUnusedAtRule(tokens, matchKeyframe, markKeyframesAsUsed, context); removeUnusedAtRule(tokens, matchNamespace, markNamespacesAsUsed, context); }
function removeUnusedAtRule(tokens, matchCallback, markCallback, context) { var atRules = {}; var atRule; var atRuleTokens; var atRuleToken; var zeroAt; var i, l;
for (i = 0, l = tokens.length; i < l; i++) { matchCallback(tokens[i], atRules); }
if (Object.keys(atRules).length === 0) { return; }
markUsedAtRules(tokens, markCallback, atRules, context);
for (atRule in atRules) { atRuleTokens = atRules[atRule];
for (i = 0, l = atRuleTokens.length; i < l; i++) { atRuleToken = atRuleTokens[i]; zeroAt = atRuleToken[0] == Token.AT_RULE ? 1 : 2; atRuleToken[zeroAt] = []; } } }
function markUsedAtRules(tokens, markCallback, atRules, context) { var boundMarkCallback = markCallback(atRules); var i, l;
for (i = 0, l = tokens.length; i < l; i++) { switch (tokens[i][0]) { case Token.RULE: boundMarkCallback(tokens[i], context); break; case Token.NESTED_BLOCK: markUsedAtRules(tokens[i][2], markCallback, atRules, context); } } }
function matchCounterStyle(token, atRules) { var match;
if (token[0] == Token.AT_RULE_BLOCK && token[1][0][1].indexOf('@counter-style') === 0) { match = token[1][0][1].split(' ')[1]; atRules[match] = atRules[match] || []; atRules[match].push(token); } }
function markCounterStylesAsUsed(atRules) { return function (token, context) { var property; var wrappedProperty; var i, l;
for (i = 0, l = token[2].length; i < l; i++) { property = token[2][i];
if (property[1][1] == 'list-style') { wrappedProperty = wrapForOptimizing(property); populateComponents([wrappedProperty], context.validator, context.warnings);
if (wrappedProperty.components[0].value[0][1] in atRules) { delete atRules[property[2][1]]; }
restoreFromOptimizing([wrappedProperty]); }
if (property[1][1] == 'list-style-type' && property[2][1] in atRules) { delete atRules[property[2][1]]; } } }; }
function matchFontFace(token, atRules) { var property; var match; var i, l;
if (token[0] == Token.AT_RULE_BLOCK && token[1][0][1] == '@font-face') { for (i = 0, l = token[2].length; i < l; i++) { property = token[2][i];
if (property[1][1] == 'font-family') { match = normalize(property[2][1].toLowerCase()); atRules[match] = atRules[match] || []; atRules[match].push(token); break; } } } }
function markFontFacesAsUsed(atRules) { return function (token, context) { var property; var wrappedProperty; var component; var normalizedMatch; var i, l; var j, m;
for (i = 0, l = token[2].length; i < l; i++) { property = token[2][i];
if (property[1][1] == 'font') { wrappedProperty = wrapForOptimizing(property); populateComponents([wrappedProperty], context.validator, context.warnings); component = wrappedProperty.components[6];
for (j = 0, m = component.value.length; j < m; j++) { normalizedMatch = normalize(component.value[j][1].toLowerCase());
if (normalizedMatch in atRules) { delete atRules[normalizedMatch]; } }
restoreFromOptimizing([wrappedProperty]); }
if (property[1][1] == 'font-family') { for (j = 2, m = property.length; j < m; j++) { normalizedMatch = normalize(property[j][1].toLowerCase());
if (normalizedMatch in atRules) { delete atRules[normalizedMatch]; } } } } }; }
function matchKeyframe(token, atRules) { var match;
if (token[0] == Token.NESTED_BLOCK && keyframeRegex.test(token[1][0][1])) { match = token[1][0][1].split(' ')[1]; atRules[match] = atRules[match] || []; atRules[match].push(token); } }
function markKeyframesAsUsed(atRules) { return function (token, context) { var property; var wrappedProperty; var component; var i, l; var j, m;
for (i = 0, l = token[2].length; i < l; i++) { property = token[2][i];
if (animationRegex.test(property[1][1])) { wrappedProperty = wrapForOptimizing(property); populateComponents([wrappedProperty], context.validator, context.warnings); component = wrappedProperty.components[7];
for (j = 0, m = component.value.length; j < m; j++) { if (component.value[j][1] in atRules) { delete atRules[component.value[j][1]]; } }
restoreFromOptimizing([wrappedProperty]); }
if (animationNameRegex.test(property[1][1])) { for (j = 2, m = property.length; j < m; j++) { if (property[j][1] in atRules) { delete atRules[property[j][1]]; } } } } }; }
function matchNamespace(token, atRules) { var match;
if (token[0] == Token.AT_RULE && token[1].indexOf('@namespace') === 0) { match = token[1].split(' ')[1]; atRules[match] = atRules[match] || []; atRules[match].push(token); } }
function markNamespacesAsUsed(atRules) { var namespaceRegex = new RegExp(Object.keys(atRules).join('\\\||') + '\\\|', 'g');
return function (token) { var match; var scope; var normalizedMatch; var i, l; var j, m;
for (i = 0, l = token[1].length; i < l; i++) { scope = token[1][i]; match = scope[1].match(namespaceRegex);
for (j = 0, m = match.length; j < m; j++) { normalizedMatch = match[j].substring(0, match[j].length - 1);
if (normalizedMatch in atRules) { delete atRules[normalizedMatch]; } } } }; }
module.exports = removeUnusedAtRules;
|