1var roundingPrecisionFrom = require('./rounding-precision').roundingPrecisionFrom; 2 3var override = require('../utils/override'); 4 5var OptimizationLevel = { 6 Zero: '0', 7 One: '1', 8 Two: '2' 9}; 10 11var DEFAULTS = {}; 12 13DEFAULTS[OptimizationLevel.Zero] = {}; 14DEFAULTS[OptimizationLevel.One] = { 15 cleanupCharsets: true, 16 normalizeUrls: true, 17 optimizeBackground: true, 18 optimizeBorderRadius: true, 19 optimizeFilter: true, 20 optimizeFontWeight: true, 21 optimizeOutline: true, 22 removeEmpty: true, 23 removeNegativePaddings: true, 24 removeQuotes: true, 25 removeWhitespace: true, 26 replaceMultipleZeros: true, 27 replaceTimeUnits: true, 28 replaceZeroUnits: true, 29 roundingPrecision: roundingPrecisionFrom(undefined), 30 selectorsSortingMethod: 'standard', 31 specialComments: 'all', 32 tidyAtRules: true, 33 tidyBlockScopes: true, 34 tidySelectors: true, 35 transform: noop 36}; 37DEFAULTS[OptimizationLevel.Two] = { 38 mergeAdjacentRules: true, 39 mergeIntoShorthands: true, 40 mergeMedia: true, 41 mergeNonAdjacentRules: true, 42 mergeSemantically: false, 43 overrideProperties: true, 44 removeEmpty: true, 45 reduceNonAdjacentRules: true, 46 removeDuplicateFontRules: true, 47 removeDuplicateMediaBlocks: true, 48 removeDuplicateRules: true, 49 removeUnusedAtRules: false, 50 restructureRules: false, 51 skipProperties: [] 52}; 53 54var ALL_KEYWORD_1 = '*'; 55var ALL_KEYWORD_2 = 'all'; 56var FALSE_KEYWORD_1 = 'false'; 57var FALSE_KEYWORD_2 = 'off'; 58var TRUE_KEYWORD_1 = 'true'; 59var TRUE_KEYWORD_2 = 'on'; 60 61var LIST_VALUE_SEPARATOR = ','; 62var OPTION_SEPARATOR = ';'; 63var OPTION_VALUE_SEPARATOR = ':'; 64 65function noop() {} 66 67function optimizationLevelFrom(source) { 68 var level = override(DEFAULTS, {}); 69 var Zero = OptimizationLevel.Zero; 70 var One = OptimizationLevel.One; 71 var Two = OptimizationLevel.Two; 72 73 74 if (undefined === source) { 75 delete level[Two]; 76 return level; 77 } 78 79 if (typeof source == 'string') { 80 source = parseInt(source); 81 } 82 83 if (typeof source == 'number' && source === parseInt(Two)) { 84 return level; 85 } 86 87 if (typeof source == 'number' && source === parseInt(One)) { 88 delete level[Two]; 89 return level; 90 } 91 92 if (typeof source == 'number' && source === parseInt(Zero)) { 93 delete level[Two]; 94 delete level[One]; 95 return level; 96 } 97 98 if (typeof source == 'object') { 99 source = covertValuesToHashes(source); 100 } 101 102 if (One in source && 'roundingPrecision' in source[One]) { 103 source[One].roundingPrecision = roundingPrecisionFrom(source[One].roundingPrecision); 104 } 105 106 if (Two in source && 'skipProperties' in source[Two] && typeof(source[Two].skipProperties) == 'string') { 107 source[Two].skipProperties = source[Two].skipProperties.split(LIST_VALUE_SEPARATOR); 108 } 109 110 if (Zero in source || One in source || Two in source) { 111 level[Zero] = override(level[Zero], source[Zero]); 112 } 113 114 if (One in source && ALL_KEYWORD_1 in source[One]) { 115 level[One] = override(level[One], defaults(One, normalizeValue(source[One][ALL_KEYWORD_1]))); 116 delete source[One][ALL_KEYWORD_1]; 117 } 118 119 if (One in source && ALL_KEYWORD_2 in source[One]) { 120 level[One] = override(level[One], defaults(One, normalizeValue(source[One][ALL_KEYWORD_2]))); 121 delete source[One][ALL_KEYWORD_2]; 122 } 123 124 if (One in source || Two in source) { 125 level[One] = override(level[One], source[One]); 126 } else { 127 delete level[One]; 128 } 129 130 if (Two in source && ALL_KEYWORD_1 in source[Two]) { 131 level[Two] = override(level[Two], defaults(Two, normalizeValue(source[Two][ALL_KEYWORD_1]))); 132 delete source[Two][ALL_KEYWORD_1]; 133 } 134 135 if (Two in source && ALL_KEYWORD_2 in source[Two]) { 136 level[Two] = override(level[Two], defaults(Two, normalizeValue(source[Two][ALL_KEYWORD_2]))); 137 delete source[Two][ALL_KEYWORD_2]; 138 } 139 140 if (Two in source) { 141 level[Two] = override(level[Two], source[Two]); 142 } else { 143 delete level[Two]; 144 } 145 146 return level; 147} 148 149function defaults(level, value) { 150 var options = override(DEFAULTS[level], {}); 151 var key; 152 153 for (key in options) { 154 if (typeof options[key] == 'boolean') { 155 options[key] = value; 156 } 157 } 158 159 return options; 160} 161 162function normalizeValue(value) { 163 switch (value) { 164 case FALSE_KEYWORD_1: 165 case FALSE_KEYWORD_2: 166 return false; 167 case TRUE_KEYWORD_1: 168 case TRUE_KEYWORD_2: 169 return true; 170 default: 171 return value; 172 } 173} 174 175function covertValuesToHashes(source) { 176 var clonedSource = override(source, {}); 177 var level; 178 var i; 179 180 for (i = 0; i <= 2; i++) { 181 level = '' + i; 182 183 if (level in clonedSource && (clonedSource[level] === undefined || clonedSource[level] === false)) { 184 delete clonedSource[level]; 185 } 186 187 if (level in clonedSource && clonedSource[level] === true) { 188 clonedSource[level] = {}; 189 } 190 191 if (level in clonedSource && typeof clonedSource[level] == 'string') { 192 clonedSource[level] = covertToHash(clonedSource[level], level); 193 } 194 } 195 196 return clonedSource; 197} 198 199function covertToHash(asString, level) { 200 return asString 201 .split(OPTION_SEPARATOR) 202 .reduce(function (accumulator, directive) { 203 var parts = directive.split(OPTION_VALUE_SEPARATOR); 204 var name = parts[0]; 205 var value = parts[1]; 206 var normalizedValue = normalizeValue(value); 207 208 if (ALL_KEYWORD_1 == name || ALL_KEYWORD_2 == name) { 209 accumulator = override(accumulator, defaults(level, normalizedValue)); 210 } else { 211 accumulator[name] = normalizedValue; 212 } 213 214 return accumulator; 215 }, {}); 216} 217 218module.exports = { 219 OptimizationLevel: OptimizationLevel, 220 optimizationLevelFrom: optimizationLevelFrom, 221}; 222