Home | History | Annotate | Download | only in src
      1 // Copyright 2014 Google Inc. All rights reserved.
      2 //
      3 // Licensed under the Apache License, Version 2.0 (the "License");
      4 // you may not use this file except in compliance with the License.
      5 //     You may obtain a copy of the License at
      6 //
      7 // http://www.apache.org/licenses/LICENSE-2.0
      8 //
      9 // Unless required by applicable law or agreed to in writing, software
     10 // distributed under the License is distributed on an "AS IS" BASIS,
     11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     12 //     See the License for the specific language governing permissions and
     13 // limitations under the License.
     14 
     15 (function(shared, testing) {
     16   var shorthandToLonghand = {
     17     background: [
     18       'backgroundImage',
     19       'backgroundPosition',
     20       'backgroundSize',
     21       'backgroundRepeat',
     22       'backgroundAttachment',
     23       'backgroundOrigin',
     24       'backgroundClip',
     25       'backgroundColor'
     26     ],
     27     border: [
     28       'borderTopColor',
     29       'borderTopStyle',
     30       'borderTopWidth',
     31       'borderRightColor',
     32       'borderRightStyle',
     33       'borderRightWidth',
     34       'borderBottomColor',
     35       'borderBottomStyle',
     36       'borderBottomWidth',
     37       'borderLeftColor',
     38       'borderLeftStyle',
     39       'borderLeftWidth'
     40     ],
     41     borderBottom: [
     42       'borderBottomWidth',
     43       'borderBottomStyle',
     44       'borderBottomColor'
     45     ],
     46     borderColor: [
     47       'borderTopColor',
     48       'borderRightColor',
     49       'borderBottomColor',
     50       'borderLeftColor'
     51     ],
     52     borderLeft: [
     53       'borderLeftWidth',
     54       'borderLeftStyle',
     55       'borderLeftColor'
     56     ],
     57     borderRadius: [
     58       'borderTopLeftRadius',
     59       'borderTopRightRadius',
     60       'borderBottomRightRadius',
     61       'borderBottomLeftRadius'
     62     ],
     63     borderRight: [
     64       'borderRightWidth',
     65       'borderRightStyle',
     66       'borderRightColor'
     67     ],
     68     borderTop: [
     69       'borderTopWidth',
     70       'borderTopStyle',
     71       'borderTopColor'
     72     ],
     73     borderWidth: [
     74       'borderTopWidth',
     75       'borderRightWidth',
     76       'borderBottomWidth',
     77       'borderLeftWidth'
     78     ],
     79     flex: [
     80       'flexGrow',
     81       'flexShrink',
     82       'flexBasis'
     83     ],
     84     font: [
     85       'fontFamily',
     86       'fontSize',
     87       'fontStyle',
     88       'fontVariant',
     89       'fontWeight',
     90       'lineHeight'
     91     ],
     92     margin: [
     93       'marginTop',
     94       'marginRight',
     95       'marginBottom',
     96       'marginLeft'
     97     ],
     98     outline: [
     99       'outlineColor',
    100       'outlineStyle',
    101       'outlineWidth'
    102     ],
    103     padding: [
    104       'paddingTop',
    105       'paddingRight',
    106       'paddingBottom',
    107       'paddingLeft'
    108     ]
    109   };
    110 
    111   var shorthandExpanderElem = document.createElementNS('http://www.w3.org/1999/xhtml', 'div');
    112 
    113   var borderWidthAliases = {
    114     thin: '1px',
    115     medium: '3px',
    116     thick: '5px'
    117   };
    118 
    119   var aliases = {
    120     borderBottomWidth: borderWidthAliases,
    121     borderLeftWidth: borderWidthAliases,
    122     borderRightWidth: borderWidthAliases,
    123     borderTopWidth: borderWidthAliases,
    124     fontSize: {
    125       'xx-small': '60%',
    126       'x-small': '75%',
    127       'small': '89%',
    128       'medium': '100%',
    129       'large': '120%',
    130       'x-large': '150%',
    131       'xx-large': '200%'
    132     },
    133     fontWeight: {
    134       normal: '400',
    135       bold: '700'
    136     },
    137     outlineWidth: borderWidthAliases,
    138     textShadow: {
    139       none: '0px 0px 0px transparent'
    140     },
    141     boxShadow: {
    142       none: '0px 0px 0px 0px transparent'
    143     }
    144   };
    145 
    146   function antiAlias(property, value) {
    147     if (property in aliases) {
    148       return aliases[property][value] || value;
    149     }
    150     return value;
    151   }
    152 
    153   // This delegates parsing shorthand value syntax to the browser.
    154   function expandShorthandAndAntiAlias(property, value, result) {
    155     var longProperties = shorthandToLonghand[property];
    156     if (longProperties) {
    157       shorthandExpanderElem.style[property] = value;
    158       for (var i in longProperties) {
    159         var longProperty = longProperties[i];
    160         var longhandValue = shorthandExpanderElem.style[longProperty];
    161         result[longProperty] = antiAlias(longProperty, longhandValue);
    162       }
    163     } else {
    164       result[property] = antiAlias(property, value);
    165     }
    166   };
    167 
    168   function normalizeKeyframes(effectInput) {
    169     if (!Array.isArray(effectInput) && effectInput !== null)
    170       throw new TypeError('Keyframe effect must be null or an array of keyframes');
    171 
    172     if (effectInput == null)
    173       return [];
    174 
    175     var keyframeEffect = effectInput.map(function(originalKeyframe) {
    176       var keyframe = {};
    177       for (var member in originalKeyframe) {
    178         var memberValue = originalKeyframe[member];
    179         if (member == 'offset') {
    180           if (memberValue != null) {
    181             memberValue = Number(memberValue);
    182             if (!isFinite(memberValue))
    183               throw new TypeError('keyframe offsets must be numbers.');
    184           }
    185         } else if (member == 'composite') {
    186           throw {
    187             type: DOMException.NOT_SUPPORTED_ERR,
    188             name: 'NotSupportedError',
    189             message: 'add compositing is not supported'
    190           };
    191         } else if (member == 'easing') {
    192           memberValue = shared.toTimingFunction(memberValue);
    193         } else {
    194           memberValue = '' + memberValue;
    195         }
    196         expandShorthandAndAntiAlias(member, memberValue, keyframe);
    197       }
    198       if (keyframe.offset == undefined)
    199         keyframe.offset = null;
    200       if (keyframe.easing == undefined)
    201         keyframe.easing = shared.toTimingFunction('linear');
    202       return keyframe;
    203     });
    204 
    205     var everyFrameHasOffset = true;
    206     var looselySortedByOffset = true;
    207     var previousOffset = -Infinity;
    208     for (var i = 0; i < keyframeEffect.length; i++) {
    209       var offset = keyframeEffect[i].offset;
    210       if (offset != null) {
    211         if (offset < previousOffset) {
    212           throw {
    213             code: DOMException.INVALID_MODIFICATION_ERR,
    214             name: 'InvalidModificationError',
    215             message: 'Keyframes are not loosely sorted by offset. Sort or specify offsets.'
    216           };
    217         }
    218         previousOffset = offset;
    219       } else {
    220         everyFrameHasOffset = false;
    221       }
    222     }
    223 
    224     keyframeEffect = keyframeEffect.filter(function(keyframe) {
    225       return keyframe.offset >= 0 && keyframe.offset <= 1;
    226     });
    227 
    228     function spaceKeyframes() {
    229       var length = keyframeEffect.length;
    230       if (keyframeEffect[length - 1].offset == null)
    231         keyframeEffect[length - 1].offset = 1;
    232       if (length > 1 && keyframeEffect[0].offset == null)
    233         keyframeEffect[0].offset = 0;
    234 
    235       var previousIndex = 0;
    236       var previousOffset = keyframeEffect[0].offset;
    237       for (var i = 1; i < length; i++) {
    238         var offset = keyframeEffect[i].offset;
    239         if (offset != null) {
    240           for (var j = 1; j < i - previousIndex; j++)
    241             keyframeEffect[previousIndex + j].offset = previousOffset + (offset - previousOffset) * j / (i - previousIndex);
    242           previousIndex = i;
    243           previousOffset = offset;
    244         }
    245       }
    246     }
    247     if (!everyFrameHasOffset)
    248       spaceKeyframes();
    249 
    250     return keyframeEffect;
    251   }
    252 
    253   shared.normalizeKeyframes = normalizeKeyframes;
    254 
    255   if (WEB_ANIMATIONS_TESTING) {
    256     testing.normalizeKeyframes = normalizeKeyframes;
    257   }
    258 
    259 })(webAnimationsShared, webAnimationsTesting);
    260