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