| 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; |