1// Contains the interpretation of CSS properties, as used by the property optimizer
2
3var breakUp = require('./break-up');
4var canOverride = require('./can-override');
5var restore = require('./restore');
6
7var override = require('../../utils/override');
8
9// Properties to process
10// Extend this object in order to add support for more properties in the optimizer.
11//
12// Each key in this object represents a CSS property and should be an object.
13// Such an object contains properties that describe how the represented CSS property should be handled.
14// Possible options:
15//
16// * components: array (Only specify for shorthand properties.)
17//   Contains the names of the granular properties this shorthand compacts.
18//
19// * canOverride: function
20//   Returns whether two tokens of this property can be merged with each other.
21//   This property has no meaning for shorthands.
22//
23// * defaultValue: string
24//   Specifies the default value of the property according to the CSS standard.
25//   For shorthand, this is used when every component is set to its default value, therefore it should be the shortest possible default value of all the components.
26//
27// * shortestValue: string
28//   Specifies the shortest possible value the property can possibly have.
29//   (Falls back to defaultValue if unspecified.)
30//
31// * breakUp: function (Only specify for shorthand properties.)
32//   Breaks the shorthand up to its components.
33//
34// * restore: function (Only specify for shorthand properties.)
35//   Puts the shorthand together from its components.
36//
37var compactable = {
38  'animation': {
39    canOverride: canOverride.generic.components([
40      canOverride.generic.time,
41      canOverride.generic.timingFunction,
42      canOverride.generic.time,
43      canOverride.property.animationIterationCount,
44      canOverride.property.animationDirection,
45      canOverride.property.animationFillMode,
46      canOverride.property.animationPlayState,
47      canOverride.property.animationName
48    ]),
49    components: [
50      'animation-duration',
51      'animation-timing-function',
52      'animation-delay',
53      'animation-iteration-count',
54      'animation-direction',
55      'animation-fill-mode',
56      'animation-play-state',
57      'animation-name'
58    ],
59    breakUp: breakUp.multiplex(breakUp.animation),
60    defaultValue: 'none',
61    restore: restore.multiplex(restore.withoutDefaults),
62    shorthand: true,
63    vendorPrefixes: [
64      '-moz-',
65      '-o-',
66      '-webkit-'
67    ]
68  },
69  'animation-delay': {
70    canOverride: canOverride.generic.time,
71    componentOf: [
72      'animation'
73    ],
74    defaultValue: '0s',
75    intoMultiplexMode: 'real',
76    vendorPrefixes: [
77      '-moz-',
78      '-o-',
79      '-webkit-'
80    ]
81  },
82  'animation-direction': {
83    canOverride: canOverride.property.animationDirection,
84    componentOf: [
85      'animation'
86    ],
87    defaultValue: 'normal',
88    intoMultiplexMode: 'real',
89    vendorPrefixes: [
90      '-moz-',
91      '-o-',
92      '-webkit-'
93    ]
94  },
95  'animation-duration': {
96    canOverride: canOverride.generic.time,
97    componentOf: [
98      'animation'
99    ],
100    defaultValue: '0s',
101    intoMultiplexMode: 'real',
102    keepUnlessDefault: 'animation-delay',
103    vendorPrefixes: [
104      '-moz-',
105      '-o-',
106      '-webkit-'
107    ]
108  },
109  'animation-fill-mode': {
110    canOverride: canOverride.property.animationFillMode,
111    componentOf: [
112      'animation'
113    ],
114    defaultValue: 'none',
115    intoMultiplexMode: 'real',
116    vendorPrefixes: [
117      '-moz-',
118      '-o-',
119      '-webkit-'
120    ]
121  },
122  'animation-iteration-count': {
123    canOverride: canOverride.property.animationIterationCount,
124    componentOf: [
125      'animation'
126    ],
127    defaultValue: '1',
128    intoMultiplexMode: 'real',
129    vendorPrefixes: [
130      '-moz-',
131      '-o-',
132      '-webkit-'
133    ]
134  },
135  'animation-name': {
136    canOverride: canOverride.property.animationName,
137    componentOf: [
138      'animation'
139    ],
140    defaultValue: 'none',
141    intoMultiplexMode: 'real',
142    vendorPrefixes: [
143      '-moz-',
144      '-o-',
145      '-webkit-'
146    ]
147  },
148  'animation-play-state': {
149    canOverride: canOverride.property.animationPlayState,
150    componentOf: [
151      'animation'
152    ],
153    defaultValue: 'running',
154    intoMultiplexMode: 'real',
155    vendorPrefixes: [
156      '-moz-',
157      '-o-',
158      '-webkit-'
159    ]
160  },
161  'animation-timing-function': {
162    canOverride: canOverride.generic.timingFunction,
163    componentOf: [
164      'animation'
165    ],
166    defaultValue: 'ease',
167    intoMultiplexMode: 'real',
168    vendorPrefixes: [
169      '-moz-',
170      '-o-',
171      '-webkit-'
172    ]
173  },
174  'background': {
175    canOverride: canOverride.generic.components([
176      canOverride.generic.image,
177      canOverride.property.backgroundPosition,
178      canOverride.property.backgroundSize,
179      canOverride.property.backgroundRepeat,
180      canOverride.property.backgroundAttachment,
181      canOverride.property.backgroundOrigin,
182      canOverride.property.backgroundClip,
183      canOverride.generic.color
184    ]),
185    components: [
186      'background-image',
187      'background-position',
188      'background-size',
189      'background-repeat',
190      'background-attachment',
191      'background-origin',
192      'background-clip',
193      'background-color'
194    ],
195    breakUp: breakUp.multiplex(breakUp.background),
196    defaultValue: '0 0',
197    restore: restore.multiplex(restore.background),
198    shortestValue: '0',
199    shorthand: true
200  },
201  'background-attachment': {
202    canOverride: canOverride.property.backgroundAttachment,
203    componentOf: [
204      'background'
205    ],
206    defaultValue: 'scroll',
207    intoMultiplexMode: 'real'
208  },
209  'background-clip': {
210    canOverride: canOverride.property.backgroundClip,
211    componentOf: [
212      'background'
213    ],
214    defaultValue: 'border-box',
215    intoMultiplexMode: 'real',
216    shortestValue: 'border-box'
217  },
218  'background-color': {
219    canOverride: canOverride.generic.color,
220    componentOf: [
221      'background'
222    ],
223    defaultValue: 'transparent',
224    intoMultiplexMode: 'real', // otherwise real color will turn into default since color appears in last multiplex only
225    multiplexLastOnly: true,
226    nonMergeableValue: 'none',
227    shortestValue: 'red'
228  },
229  'background-image': {
230    canOverride: canOverride.generic.image,
231    componentOf: [
232      'background'
233    ],
234    defaultValue: 'none',
235    intoMultiplexMode: 'default'
236  },
237  'background-origin': {
238    canOverride: canOverride.property.backgroundOrigin,
239    componentOf: [
240      'background'
241    ],
242    defaultValue: 'padding-box',
243    intoMultiplexMode: 'real',
244    shortestValue: 'border-box'
245  },
246  'background-position': {
247    canOverride: canOverride.property.backgroundPosition,
248    componentOf: [
249      'background'
250    ],
251    defaultValue: ['0', '0'],
252    doubleValues: true,
253    intoMultiplexMode: 'real',
254    shortestValue: '0'
255  },
256  'background-repeat': {
257    canOverride: canOverride.property.backgroundRepeat,
258    componentOf: [
259      'background'
260    ],
261    defaultValue: ['repeat'],
262    doubleValues: true,
263    intoMultiplexMode: 'real'
264  },
265  'background-size': {
266    canOverride: canOverride.property.backgroundSize,
267    componentOf: [
268      'background'
269    ],
270    defaultValue: ['auto'],
271    doubleValues: true,
272    intoMultiplexMode: 'real',
273    shortestValue: '0 0'
274  },
275  'bottom': {
276    canOverride: canOverride.property.bottom,
277    defaultValue: 'auto'
278  },
279  'border': {
280    breakUp: breakUp.border,
281    canOverride: canOverride.generic.components([
282      canOverride.generic.unit,
283      canOverride.property.borderStyle,
284      canOverride.generic.color
285    ]),
286    components: [
287      'border-width',
288      'border-style',
289      'border-color'
290    ],
291    defaultValue: 'none',
292    overridesShorthands: [
293      'border-bottom',
294      'border-left',
295      'border-right',
296      'border-top'
297    ],
298    restore: restore.withoutDefaults,
299    shorthand: true,
300    shorthandComponents: true
301  },
302  'border-bottom': {
303    breakUp: breakUp.border,
304    canOverride: canOverride.generic.components([
305      canOverride.generic.unit,
306      canOverride.property.borderStyle,
307      canOverride.generic.color
308    ]),
309    components: [
310      'border-bottom-width',
311      'border-bottom-style',
312      'border-bottom-color'
313    ],
314    defaultValue: 'none',
315    restore: restore.withoutDefaults,
316    shorthand: true
317  },
318  'border-bottom-color': {
319    canOverride: canOverride.generic.color,
320    componentOf: [
321      'border-bottom',
322      'border-color'
323    ],
324    defaultValue: 'none'
325  },
326  'border-bottom-left-radius': {
327    canOverride: canOverride.generic.unit,
328    componentOf: [
329      'border-radius'
330    ],
331    defaultValue: '0',
332    vendorPrefixes: [
333      '-moz-',
334      '-o-'
335    ]
336  },
337  'border-bottom-right-radius': {
338    canOverride: canOverride.generic.unit,
339    componentOf: [
340      'border-radius'
341    ],
342    defaultValue: '0',
343    vendorPrefixes: [
344      '-moz-',
345      '-o-'
346    ]
347  },
348  'border-bottom-style': {
349    canOverride: canOverride.property.borderStyle,
350    componentOf: [
351      'border-bottom',
352      'border-style'
353    ],
354    defaultValue: 'none'
355  },
356  'border-bottom-width': {
357    canOverride: canOverride.generic.unit,
358    componentOf: [
359      'border-bottom',
360      'border-width'
361    ],
362    defaultValue: 'medium',
363    oppositeTo: 'border-top-width',
364    shortestValue: '0'
365  },
366  'border-collapse': {
367    canOverride: canOverride.property.borderCollapse,
368    defaultValue: 'separate'
369  },
370  'border-color': {
371    breakUp: breakUp.fourValues,
372    canOverride: canOverride.generic.components([
373      canOverride.generic.color,
374      canOverride.generic.color,
375      canOverride.generic.color,
376      canOverride.generic.color
377    ]),
378    componentOf: [
379      'border'
380    ],
381    components: [
382      'border-top-color',
383      'border-right-color',
384      'border-bottom-color',
385      'border-left-color'
386    ],
387    defaultValue: 'none',
388    restore: restore.fourValues,
389    shortestValue: 'red',
390    shorthand: true
391  },
392  'border-left': {
393    breakUp: breakUp.border,
394    canOverride: canOverride.generic.components([
395      canOverride.generic.unit,
396      canOverride.property.borderStyle,
397      canOverride.generic.color
398    ]),
399    components: [
400      'border-left-width',
401      'border-left-style',
402      'border-left-color'
403    ],
404    defaultValue: 'none',
405    restore: restore.withoutDefaults,
406    shorthand: true
407  },
408  'border-left-color': {
409    canOverride: canOverride.generic.color,
410    componentOf: [
411      'border-color',
412      'border-left'
413    ],
414    defaultValue: 'none'
415  },
416  'border-left-style': {
417    canOverride: canOverride.property.borderStyle,
418    componentOf: [
419      'border-left',
420      'border-style'
421    ],
422    defaultValue: 'none'
423  },
424  'border-left-width': {
425    canOverride: canOverride.generic.unit,
426    componentOf: [
427      'border-left',
428      'border-width'
429    ],
430    defaultValue: 'medium',
431    oppositeTo: 'border-right-width',
432    shortestValue: '0'
433  },
434  'border-radius': {
435    breakUp: breakUp.borderRadius,
436    canOverride: canOverride.generic.components([
437      canOverride.generic.unit,
438      canOverride.generic.unit,
439      canOverride.generic.unit,
440      canOverride.generic.unit
441    ]),
442    components: [
443      'border-top-left-radius',
444      'border-top-right-radius',
445      'border-bottom-right-radius',
446      'border-bottom-left-radius'
447    ],
448    defaultValue: '0',
449    restore: restore.borderRadius,
450    shorthand: true,
451    vendorPrefixes: [
452      '-moz-',
453      '-o-'
454    ]
455  },
456  'border-right': {
457    breakUp: breakUp.border,
458    canOverride: canOverride.generic.components([
459      canOverride.generic.unit,
460      canOverride.property.borderStyle,
461      canOverride.generic.color
462    ]),
463    components: [
464      'border-right-width',
465      'border-right-style',
466      'border-right-color'
467    ],
468    defaultValue: 'none',
469    restore: restore.withoutDefaults,
470    shorthand: true
471  },
472  'border-right-color': {
473    canOverride: canOverride.generic.color,
474    componentOf: [
475      'border-color',
476      'border-right'
477    ],
478    defaultValue: 'none'
479  },
480  'border-right-style': {
481    canOverride: canOverride.property.borderStyle,
482    componentOf: [
483      'border-right',
484      'border-style'
485    ],
486    defaultValue: 'none'
487  },
488  'border-right-width': {
489    canOverride: canOverride.generic.unit,
490    componentOf: [
491      'border-right',
492      'border-width'
493    ],
494    defaultValue: 'medium',
495    oppositeTo: 'border-left-width',
496    shortestValue: '0'
497  },
498  'border-style': {
499    breakUp: breakUp.fourValues,
500    canOverride: canOverride.generic.components([
501      canOverride.property.borderStyle,
502      canOverride.property.borderStyle,
503      canOverride.property.borderStyle,
504      canOverride.property.borderStyle
505    ]),
506    componentOf: [
507      'border'
508    ],
509    components: [
510      'border-top-style',
511      'border-right-style',
512      'border-bottom-style',
513      'border-left-style'
514    ],
515    defaultValue: 'none',
516    restore: restore.fourValues,
517    shorthand: true
518  },
519  'border-top': {
520    breakUp: breakUp.border,
521    canOverride: canOverride.generic.components([
522      canOverride.generic.unit,
523      canOverride.property.borderStyle,
524      canOverride.generic.color
525    ]),
526    components: [
527      'border-top-width',
528      'border-top-style',
529      'border-top-color'
530    ],
531    defaultValue: 'none',
532    restore: restore.withoutDefaults,
533    shorthand: true
534  },
535  'border-top-color': {
536    canOverride: canOverride.generic.color,
537    componentOf: [
538      'border-color',
539      'border-top'
540    ],
541    defaultValue: 'none'
542  },
543  'border-top-left-radius': {
544    canOverride: canOverride.generic.unit,
545    componentOf: [
546      'border-radius'
547    ],
548    defaultValue: '0',
549    vendorPrefixes: [
550      '-moz-',
551      '-o-'
552    ]
553  },
554  'border-top-right-radius': {
555    canOverride: canOverride.generic.unit,
556    componentOf: [
557      'border-radius'
558    ],
559    defaultValue: '0',
560    vendorPrefixes: [
561      '-moz-',
562      '-o-'
563    ]
564  },
565  'border-top-style': {
566    canOverride: canOverride.property.borderStyle,
567    componentOf: [
568      'border-style',
569      'border-top'
570    ],
571    defaultValue: 'none'
572  },
573  'border-top-width': {
574    canOverride: canOverride.generic.unit,
575    componentOf: [
576      'border-top',
577      'border-width'
578    ],
579    defaultValue: 'medium',
580    oppositeTo: 'border-bottom-width',
581    shortestValue: '0'
582  },
583  'border-width': {
584    breakUp: breakUp.fourValues,
585    canOverride: canOverride.generic.components([
586      canOverride.generic.unit,
587      canOverride.generic.unit,
588      canOverride.generic.unit,
589      canOverride.generic.unit
590    ]),
591    componentOf: [
592      'border'
593    ],
594    components: [
595      'border-top-width',
596      'border-right-width',
597      'border-bottom-width',
598      'border-left-width'
599    ],
600    defaultValue: 'medium',
601    restore: restore.fourValues,
602    shortestValue: '0',
603    shorthand: true
604  },
605  'clear': {
606    canOverride: canOverride.property.clear,
607    defaultValue: 'none'
608  },
609  'color': {
610    canOverride: canOverride.generic.color,
611    defaultValue: 'transparent',
612    shortestValue: 'red'
613  },
614  'cursor': {
615    canOverride: canOverride.property.cursor,
616    defaultValue: 'auto'
617  },
618  'display': {
619    canOverride: canOverride.property.display,
620  },
621  'float': {
622    canOverride: canOverride.property.float,
623    defaultValue: 'none'
624  },
625  'font': {
626    breakUp: breakUp.font,
627    canOverride: canOverride.generic.components([
628      canOverride.property.fontStyle,
629      canOverride.property.fontVariant,
630      canOverride.property.fontWeight,
631      canOverride.property.fontStretch,
632      canOverride.generic.unit,
633      canOverride.generic.unit,
634      canOverride.property.fontFamily
635    ]),
636    components: [
637      'font-style',
638      'font-variant',
639      'font-weight',
640      'font-stretch',
641      'font-size',
642      'line-height',
643      'font-family'
644    ],
645    restore: restore.font,
646    shorthand: true
647  },
648  'font-family': {
649    canOverride: canOverride.property.fontFamily,
650    defaultValue: 'user|agent|specific'
651  },
652  'font-size': {
653    canOverride: canOverride.generic.unit,
654    defaultValue: 'medium',
655    shortestValue: '0'
656  },
657  'font-stretch': {
658    canOverride: canOverride.property.fontStretch,
659    defaultValue: 'normal'
660  },
661  'font-style': {
662    canOverride: canOverride.property.fontStyle,
663    defaultValue: 'normal'
664  },
665  'font-variant': {
666    canOverride: canOverride.property.fontVariant,
667    defaultValue: 'normal'
668  },
669  'font-weight': {
670    canOverride: canOverride.property.fontWeight,
671    defaultValue: 'normal',
672    shortestValue: '400'
673  },
674  'height': {
675    canOverride: canOverride.generic.unit,
676    defaultValue: 'auto',
677    shortestValue: '0'
678  },
679  'left': {
680    canOverride: canOverride.property.left,
681    defaultValue: 'auto'
682  },
683  'line-height': {
684    canOverride: canOverride.generic.unitOrNumber,
685    defaultValue: 'normal',
686    shortestValue: '0'
687  },
688  'list-style': {
689    canOverride: canOverride.generic.components([
690      canOverride.property.listStyleType,
691      canOverride.property.listStylePosition,
692      canOverride.property.listStyleImage
693    ]),
694    components: [
695      'list-style-type',
696      'list-style-position',
697      'list-style-image'
698    ],
699    breakUp: breakUp.listStyle,
700    restore: restore.withoutDefaults,
701    defaultValue: 'outside', // can't use 'disc' because that'd override default 'decimal' for <ol>
702    shortestValue: 'none',
703    shorthand: true
704  },
705  'list-style-image' : {
706    canOverride: canOverride.generic.image,
707    componentOf: [
708      'list-style'
709    ],
710    defaultValue: 'none'
711  },
712  'list-style-position' : {
713    canOverride: canOverride.property.listStylePosition,
714    componentOf: [
715      'list-style'
716    ],
717    defaultValue: 'outside',
718    shortestValue: 'inside'
719  },
720  'list-style-type' : {
721    canOverride: canOverride.property.listStyleType,
722    componentOf: [
723      'list-style'
724    ],
725    // NOTE: we can't tell the real default value here, it's 'disc' for <ul> and 'decimal' for <ol>
726    // this is a hack, but it doesn't matter because this value will be either overridden or
727    // it will disappear at the final step anyway
728    defaultValue: 'decimal|disc',
729    shortestValue: 'none'
730  },
731  'margin': {
732    breakUp: breakUp.fourValues,
733    canOverride: canOverride.generic.components([
734      canOverride.generic.unit,
735      canOverride.generic.unit,
736      canOverride.generic.unit,
737      canOverride.generic.unit
738    ]),
739    components: [
740      'margin-top',
741      'margin-right',
742      'margin-bottom',
743      'margin-left'
744    ],
745    defaultValue: '0',
746    restore: restore.fourValues,
747    shorthand: true
748  },
749  'margin-bottom': {
750    canOverride: canOverride.generic.unit,
751    componentOf: [
752      'margin'
753    ],
754    defaultValue: '0',
755    oppositeTo: 'margin-top'
756  },
757  'margin-left': {
758    canOverride: canOverride.generic.unit,
759    componentOf: [
760      'margin'
761    ],
762    defaultValue: '0',
763    oppositeTo: 'margin-right'
764  },
765  'margin-right': {
766    canOverride: canOverride.generic.unit,
767    componentOf: [
768      'margin'
769    ],
770    defaultValue: '0',
771    oppositeTo: 'margin-left'
772  },
773  'margin-top': {
774    canOverride: canOverride.generic.unit,
775    componentOf: [
776      'margin'
777    ],
778    defaultValue: '0',
779    oppositeTo: 'margin-bottom'
780  },
781  'outline': {
782    canOverride: canOverride.generic.components([
783      canOverride.generic.color,
784      canOverride.property.outlineStyle,
785      canOverride.generic.unit
786    ]),
787    components: [
788      'outline-color',
789      'outline-style',
790      'outline-width'
791    ],
792    breakUp: breakUp.outline,
793    restore: restore.withoutDefaults,
794    defaultValue: '0',
795    shorthand: true
796  },
797  'outline-color': {
798    canOverride: canOverride.generic.color,
799    componentOf: [
800      'outline'
801    ],
802    defaultValue: 'invert',
803    shortestValue: 'red'
804  },
805  'outline-style': {
806    canOverride: canOverride.property.outlineStyle,
807    componentOf: [
808      'outline'
809    ],
810    defaultValue: 'none'
811  },
812  'outline-width': {
813    canOverride: canOverride.generic.unit,
814    componentOf: [
815      'outline'
816    ],
817    defaultValue: 'medium',
818    shortestValue: '0'
819  },
820  'overflow': {
821    canOverride: canOverride.property.overflow,
822    defaultValue: 'visible'
823  },
824  'overflow-x': {
825    canOverride: canOverride.property.overflow,
826    defaultValue: 'visible'
827  },
828  'overflow-y': {
829    canOverride: canOverride.property.overflow,
830    defaultValue: 'visible'
831  },
832  'padding': {
833    breakUp: breakUp.fourValues,
834    canOverride: canOverride.generic.components([
835      canOverride.generic.unit,
836      canOverride.generic.unit,
837      canOverride.generic.unit,
838      canOverride.generic.unit
839    ]),
840    components: [
841      'padding-top',
842      'padding-right',
843      'padding-bottom',
844      'padding-left'
845    ],
846    defaultValue: '0',
847    restore: restore.fourValues,
848    shorthand: true
849  },
850  'padding-bottom': {
851    canOverride: canOverride.generic.unit,
852    componentOf: [
853      'padding'
854    ],
855    defaultValue: '0',
856    oppositeTo: 'padding-top'
857  },
858  'padding-left': {
859    canOverride: canOverride.generic.unit,
860    componentOf: [
861      'padding'
862    ],
863    defaultValue: '0',
864    oppositeTo: 'padding-right'
865  },
866  'padding-right': {
867    canOverride: canOverride.generic.unit,
868    componentOf: [
869      'padding'
870    ],
871    defaultValue: '0',
872    oppositeTo: 'padding-left'
873  },
874  'padding-top': {
875    canOverride: canOverride.generic.unit,
876    componentOf: [
877      'padding'
878    ],
879    defaultValue: '0',
880    oppositeTo: 'padding-bottom'
881  },
882  'position': {
883    canOverride: canOverride.property.position,
884    defaultValue: 'static'
885  },
886  'right': {
887    canOverride: canOverride.property.right,
888    defaultValue: 'auto'
889  },
890  'text-align': {
891    canOverride: canOverride.property.textAlign,
892    // NOTE: we can't tell the real default value here, as it depends on default text direction
893    // this is a hack, but it doesn't matter because this value will be either overridden or
894    // it will disappear anyway
895    defaultValue: 'left|right'
896  },
897  'text-decoration': {
898    canOverride: canOverride.property.textDecoration,
899    defaultValue: 'none'
900  },
901  'text-overflow': {
902    canOverride: canOverride.property.textOverflow,
903    defaultValue: 'none'
904  },
905  'text-shadow': {
906    canOverride: canOverride.property.textShadow,
907    defaultValue: 'none'
908  },
909  'top': {
910    canOverride: canOverride.property.top,
911    defaultValue: 'auto'
912  },
913  'transform': {
914    canOverride: canOverride.property.transform,
915    vendorPrefixes: [
916      '-moz-',
917      '-ms-',
918      '-webkit-'
919    ]
920  },
921  'transition': {
922    breakUp: breakUp.multiplex(breakUp.transition),
923    canOverride: canOverride.generic.components([
924      canOverride.property.transitionProperty,
925      canOverride.generic.time,
926      canOverride.generic.timingFunction,
927      canOverride.generic.time
928    ]),
929    components: [
930      'transition-property',
931      'transition-duration',
932      'transition-timing-function',
933      'transition-delay'
934    ],
935    defaultValue: 'none',
936    restore: restore.multiplex(restore.withoutDefaults),
937    shorthand: true,
938    vendorPrefixes: [
939      '-moz-',
940      '-o-',
941      '-webkit-'
942    ]
943  },
944  'transition-delay': {
945    canOverride: canOverride.generic.time,
946    componentOf: [
947      'transition'
948    ],
949    defaultValue: '0s',
950    intoMultiplexMode: 'real',
951    vendorPrefixes: [
952      '-moz-',
953      '-o-',
954      '-webkit-'
955    ]
956  },
957  'transition-duration': {
958    canOverride: canOverride.generic.time,
959    componentOf: [
960      'transition'
961    ],
962    defaultValue: '0s',
963    intoMultiplexMode: 'real',
964    vendorPrefixes: [
965      '-moz-',
966      '-o-',
967      '-webkit-'
968    ]
969  },
970  'transition-property': {
971    canOverride: canOverride.generic.propertyName,
972    componentOf: [
973      'transition'
974    ],
975    defaultValue: 'all',
976    intoMultiplexMode: 'placeholder',
977    placeholderValue: '_', // it's a short value that won't match any property and still be a valid `transition-property`
978    vendorPrefixes: [
979      '-moz-',
980      '-o-',
981      '-webkit-'
982    ]
983  },
984  'transition-timing-function': {
985    canOverride: canOverride.generic.timingFunction,
986    componentOf: [
987      'transition'
988    ],
989    defaultValue: 'ease',
990    intoMultiplexMode: 'real',
991    vendorPrefixes: [
992      '-moz-',
993      '-o-',
994      '-webkit-'
995    ]
996  },
997  'vertical-align': {
998    canOverride: canOverride.property.verticalAlign,
999    defaultValue: 'baseline'
1000  },
1001  'visibility': {
1002    canOverride: canOverride.property.visibility,
1003    defaultValue: 'visible'
1004  },
1005  'white-space': {
1006    canOverride: canOverride.property.whiteSpace,
1007    defaultValue: 'normal'
1008  },
1009  'width': {
1010    canOverride: canOverride.generic.unit,
1011    defaultValue: 'auto',
1012    shortestValue: '0'
1013  },
1014  'z-index': {
1015    canOverride: canOverride.property.zIndex,
1016    defaultValue: 'auto'
1017  }
1018};
1019
1020function cloneDescriptor(propertyName, prefix) {
1021  var clonedDescriptor = override(compactable[propertyName], {});
1022
1023  if ('componentOf' in clonedDescriptor) {
1024    clonedDescriptor.componentOf = clonedDescriptor.componentOf.map(function (shorthandName) {
1025      return prefix + shorthandName;
1026    });
1027  }
1028
1029  if ('components' in clonedDescriptor) {
1030    clonedDescriptor.components = clonedDescriptor.components.map(function (longhandName) {
1031      return prefix + longhandName;
1032    });
1033  }
1034
1035  if ('keepUnlessDefault' in clonedDescriptor) {
1036    clonedDescriptor.keepUnlessDefault = prefix + clonedDescriptor.keepUnlessDefault;
1037  }
1038
1039  return clonedDescriptor;
1040}
1041
1042// generate vendor-prefixed properties
1043var vendorPrefixedCompactable = {};
1044
1045for (var propertyName in compactable) {
1046  var descriptor = compactable[propertyName];
1047
1048  if (!('vendorPrefixes' in descriptor)) {
1049    continue;
1050  }
1051
1052  for (var i = 0; i < descriptor.vendorPrefixes.length; i++) {
1053    var prefix = descriptor.vendorPrefixes[i];
1054    var clonedDescriptor = cloneDescriptor(propertyName, prefix);
1055    delete clonedDescriptor.vendorPrefixes;
1056
1057    vendorPrefixedCompactable[prefix + propertyName] = clonedDescriptor;
1058  }
1059
1060  delete descriptor.vendorPrefixes;
1061}
1062
1063module.exports = override(compactable, vendorPrefixedCompactable);
1064