source: trip-planner-front/node_modules/csso/lib/restructure/4-restructShorthand.js

Last change on this file was 6a3a178, checked in by Ema <ema_spirova@…>, 3 years ago

initial commit

  • Property mode set to 100644
File size: 11.3 KB
RevLine 
[6a3a178]1var List = require('css-tree').List;
2var generate = require('css-tree').generate;
3var walk = require('css-tree').walk;
4
5var REPLACE = 1;
6var REMOVE = 2;
7var TOP = 0;
8var RIGHT = 1;
9var BOTTOM = 2;
10var LEFT = 3;
11var SIDES = ['top', 'right', 'bottom', 'left'];
12var SIDE = {
13 'margin-top': 'top',
14 'margin-right': 'right',
15 'margin-bottom': 'bottom',
16 'margin-left': 'left',
17
18 'padding-top': 'top',
19 'padding-right': 'right',
20 'padding-bottom': 'bottom',
21 'padding-left': 'left',
22
23 'border-top-color': 'top',
24 'border-right-color': 'right',
25 'border-bottom-color': 'bottom',
26 'border-left-color': 'left',
27 'border-top-width': 'top',
28 'border-right-width': 'right',
29 'border-bottom-width': 'bottom',
30 'border-left-width': 'left',
31 'border-top-style': 'top',
32 'border-right-style': 'right',
33 'border-bottom-style': 'bottom',
34 'border-left-style': 'left'
35};
36var MAIN_PROPERTY = {
37 'margin': 'margin',
38 'margin-top': 'margin',
39 'margin-right': 'margin',
40 'margin-bottom': 'margin',
41 'margin-left': 'margin',
42
43 'padding': 'padding',
44 'padding-top': 'padding',
45 'padding-right': 'padding',
46 'padding-bottom': 'padding',
47 'padding-left': 'padding',
48
49 'border-color': 'border-color',
50 'border-top-color': 'border-color',
51 'border-right-color': 'border-color',
52 'border-bottom-color': 'border-color',
53 'border-left-color': 'border-color',
54 'border-width': 'border-width',
55 'border-top-width': 'border-width',
56 'border-right-width': 'border-width',
57 'border-bottom-width': 'border-width',
58 'border-left-width': 'border-width',
59 'border-style': 'border-style',
60 'border-top-style': 'border-style',
61 'border-right-style': 'border-style',
62 'border-bottom-style': 'border-style',
63 'border-left-style': 'border-style'
64};
65
66function TRBL(name) {
67 this.name = name;
68 this.loc = null;
69 this.iehack = undefined;
70 this.sides = {
71 'top': null,
72 'right': null,
73 'bottom': null,
74 'left': null
75 };
76}
77
78TRBL.prototype.getValueSequence = function(declaration, count) {
79 var values = [];
80 var iehack = '';
81 var hasBadValues = declaration.value.type !== 'Value' || declaration.value.children.some(function(child) {
82 var special = false;
83
84 switch (child.type) {
85 case 'Identifier':
86 switch (child.name) {
87 case '\\0':
88 case '\\9':
89 iehack = child.name;
90 return;
91
92 case 'inherit':
93 case 'initial':
94 case 'unset':
95 case 'revert':
96 special = child.name;
97 break;
98 }
99 break;
100
101 case 'Dimension':
102 switch (child.unit) {
103 // is not supported until IE11
104 case 'rem':
105
106 // v* units is too buggy across browsers and better
107 // don't merge values with those units
108 case 'vw':
109 case 'vh':
110 case 'vmin':
111 case 'vmax':
112 case 'vm': // IE9 supporting "vm" instead of "vmin".
113 special = child.unit;
114 break;
115 }
116 break;
117
118 case 'Hash': // color
119 case 'Number':
120 case 'Percentage':
121 break;
122
123 case 'Function':
124 if (child.name === 'var') {
125 return true;
126 }
127
128 special = child.name;
129 break;
130
131 case 'WhiteSpace':
132 return false; // ignore space
133
134 default:
135 return true; // bad value
136 }
137
138 values.push({
139 node: child,
140 special: special,
141 important: declaration.important
142 });
143 });
144
145 if (hasBadValues || values.length > count) {
146 return false;
147 }
148
149 if (typeof this.iehack === 'string' && this.iehack !== iehack) {
150 return false;
151 }
152
153 this.iehack = iehack; // move outside
154
155 return values;
156};
157
158TRBL.prototype.canOverride = function(side, value) {
159 var currentValue = this.sides[side];
160
161 return !currentValue || (value.important && !currentValue.important);
162};
163
164TRBL.prototype.add = function(name, declaration) {
165 function attemptToAdd() {
166 var sides = this.sides;
167 var side = SIDE[name];
168
169 if (side) {
170 if (side in sides === false) {
171 return false;
172 }
173
174 var values = this.getValueSequence(declaration, 1);
175
176 if (!values || !values.length) {
177 return false;
178 }
179
180 // can mix only if specials are equal
181 for (var key in sides) {
182 if (sides[key] !== null && sides[key].special !== values[0].special) {
183 return false;
184 }
185 }
186
187 if (!this.canOverride(side, values[0])) {
188 return true;
189 }
190
191 sides[side] = values[0];
192 return true;
193 } else if (name === this.name) {
194 var values = this.getValueSequence(declaration, 4);
195
196 if (!values || !values.length) {
197 return false;
198 }
199
200 switch (values.length) {
201 case 1:
202 values[RIGHT] = values[TOP];
203 values[BOTTOM] = values[TOP];
204 values[LEFT] = values[TOP];
205 break;
206
207 case 2:
208 values[BOTTOM] = values[TOP];
209 values[LEFT] = values[RIGHT];
210 break;
211
212 case 3:
213 values[LEFT] = values[RIGHT];
214 break;
215 }
216
217 // can mix only if specials are equal
218 for (var i = 0; i < 4; i++) {
219 for (var key in sides) {
220 if (sides[key] !== null && sides[key].special !== values[i].special) {
221 return false;
222 }
223 }
224 }
225
226 for (var i = 0; i < 4; i++) {
227 if (this.canOverride(SIDES[i], values[i])) {
228 sides[SIDES[i]] = values[i];
229 }
230 }
231
232 return true;
233 }
234 }
235
236 if (!attemptToAdd.call(this)) {
237 return false;
238 }
239
240 // TODO: use it when we can refer to several points in source
241 // if (this.loc) {
242 // this.loc = {
243 // primary: this.loc,
244 // merged: declaration.loc
245 // };
246 // } else {
247 // this.loc = declaration.loc;
248 // }
249 if (!this.loc) {
250 this.loc = declaration.loc;
251 }
252
253 return true;
254};
255
256TRBL.prototype.isOkToMinimize = function() {
257 var top = this.sides.top;
258 var right = this.sides.right;
259 var bottom = this.sides.bottom;
260 var left = this.sides.left;
261
262 if (top && right && bottom && left) {
263 var important =
264 top.important +
265 right.important +
266 bottom.important +
267 left.important;
268
269 return important === 0 || important === 4;
270 }
271
272 return false;
273};
274
275TRBL.prototype.getValue = function() {
276 var result = new List();
277 var sides = this.sides;
278 var values = [
279 sides.top,
280 sides.right,
281 sides.bottom,
282 sides.left
283 ];
284 var stringValues = [
285 generate(sides.top.node),
286 generate(sides.right.node),
287 generate(sides.bottom.node),
288 generate(sides.left.node)
289 ];
290
291 if (stringValues[LEFT] === stringValues[RIGHT]) {
292 values.pop();
293 if (stringValues[BOTTOM] === stringValues[TOP]) {
294 values.pop();
295 if (stringValues[RIGHT] === stringValues[TOP]) {
296 values.pop();
297 }
298 }
299 }
300
301 for (var i = 0; i < values.length; i++) {
302 if (i) {
303 result.appendData({ type: 'WhiteSpace', value: ' ' });
304 }
305
306 result.appendData(values[i].node);
307 }
308
309 if (this.iehack) {
310 result.appendData({ type: 'WhiteSpace', value: ' ' });
311 result.appendData({
312 type: 'Identifier',
313 loc: null,
314 name: this.iehack
315 });
316 }
317
318 return {
319 type: 'Value',
320 loc: null,
321 children: result
322 };
323};
324
325TRBL.prototype.getDeclaration = function() {
326 return {
327 type: 'Declaration',
328 loc: this.loc,
329 important: this.sides.top.important,
330 property: this.name,
331 value: this.getValue()
332 };
333};
334
335function processRule(rule, shorts, shortDeclarations, lastShortSelector) {
336 var declarations = rule.block.children;
337 var selector = rule.prelude.children.first().id;
338
339 rule.block.children.eachRight(function(declaration, item) {
340 var property = declaration.property;
341
342 if (!MAIN_PROPERTY.hasOwnProperty(property)) {
343 return;
344 }
345
346 var key = MAIN_PROPERTY[property];
347 var shorthand;
348 var operation;
349
350 if (!lastShortSelector || selector === lastShortSelector) {
351 if (key in shorts) {
352 operation = REMOVE;
353 shorthand = shorts[key];
354 }
355 }
356
357 if (!shorthand || !shorthand.add(property, declaration)) {
358 operation = REPLACE;
359 shorthand = new TRBL(key);
360
361 // if can't parse value ignore it and break shorthand children
362 if (!shorthand.add(property, declaration)) {
363 lastShortSelector = null;
364 return;
365 }
366 }
367
368 shorts[key] = shorthand;
369 shortDeclarations.push({
370 operation: operation,
371 block: declarations,
372 item: item,
373 shorthand: shorthand
374 });
375
376 lastShortSelector = selector;
377 });
378
379 return lastShortSelector;
380}
381
382function processShorthands(shortDeclarations, markDeclaration) {
383 shortDeclarations.forEach(function(item) {
384 var shorthand = item.shorthand;
385
386 if (!shorthand.isOkToMinimize()) {
387 return;
388 }
389
390 if (item.operation === REPLACE) {
391 item.item.data = markDeclaration(shorthand.getDeclaration());
392 } else {
393 item.block.remove(item.item);
394 }
395 });
396}
397
398module.exports = function restructBlock(ast, indexer) {
399 var stylesheetMap = {};
400 var shortDeclarations = [];
401
402 walk(ast, {
403 visit: 'Rule',
404 reverse: true,
405 enter: function(node) {
406 var stylesheet = this.block || this.stylesheet;
407 var ruleId = (node.pseudoSignature || '') + '|' + node.prelude.children.first().id;
408 var ruleMap;
409 var shorts;
410
411 if (!stylesheetMap.hasOwnProperty(stylesheet.id)) {
412 ruleMap = {
413 lastShortSelector: null
414 };
415 stylesheetMap[stylesheet.id] = ruleMap;
416 } else {
417 ruleMap = stylesheetMap[stylesheet.id];
418 }
419
420 if (ruleMap.hasOwnProperty(ruleId)) {
421 shorts = ruleMap[ruleId];
422 } else {
423 shorts = {};
424 ruleMap[ruleId] = shorts;
425 }
426
427 ruleMap.lastShortSelector = processRule.call(this, node, shorts, shortDeclarations, ruleMap.lastShortSelector);
428 }
429 });
430
431 processShorthands(shortDeclarations, indexer.declaration);
432};
Note: See TracBrowser for help on using the repository browser.