Spaces:
Sleeping
Sleeping
| 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; | |