250 lines
		
	
	
		
			6.6 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
			
		
		
	
	
			250 lines
		
	
	
		
			6.6 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
| 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;
 |