blob: 1fd025487752df2b742bf9e3e617893ded729e69 [file] [log] [blame]
// Copyright 2014 Google Inc. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
(function(shared, testing) {
var shorthandToLonghand = {
background: [
'backgroundImage',
'backgroundPosition',
'backgroundSize',
'backgroundRepeat',
'backgroundAttachment',
'backgroundOrigin',
'backgroundClip',
'backgroundColor'
],
border: [
'borderTopColor',
'borderTopStyle',
'borderTopWidth',
'borderRightColor',
'borderRightStyle',
'borderRightWidth',
'borderBottomColor',
'borderBottomStyle',
'borderBottomWidth',
'borderLeftColor',
'borderLeftStyle',
'borderLeftWidth'
],
borderBottom: [
'borderBottomWidth',
'borderBottomStyle',
'borderBottomColor'
],
borderColor: [
'borderTopColor',
'borderRightColor',
'borderBottomColor',
'borderLeftColor'
],
borderLeft: [
'borderLeftWidth',
'borderLeftStyle',
'borderLeftColor'
],
borderRadius: [
'borderTopLeftRadius',
'borderTopRightRadius',
'borderBottomRightRadius',
'borderBottomLeftRadius'
],
borderRight: [
'borderRightWidth',
'borderRightStyle',
'borderRightColor'
],
borderTop: [
'borderTopWidth',
'borderTopStyle',
'borderTopColor'
],
borderWidth: [
'borderTopWidth',
'borderRightWidth',
'borderBottomWidth',
'borderLeftWidth'
],
flex: [
'flexGrow',
'flexShrink',
'flexBasis'
],
font: [
'fontFamily',
'fontSize',
'fontStyle',
'fontVariant',
'fontWeight',
'lineHeight'
],
margin: [
'marginTop',
'marginRight',
'marginBottom',
'marginLeft'
],
outline: [
'outlineColor',
'outlineStyle',
'outlineWidth'
],
padding: [
'paddingTop',
'paddingRight',
'paddingBottom',
'paddingLeft'
]
};
var shorthandExpanderElem = document.createElementNS('http://www.w3.org/1999/xhtml', 'div');
var borderWidthAliases = {
thin: '1px',
medium: '3px',
thick: '5px'
};
var aliases = {
borderBottomWidth: borderWidthAliases,
borderLeftWidth: borderWidthAliases,
borderRightWidth: borderWidthAliases,
borderTopWidth: borderWidthAliases,
fontSize: {
'xx-small': '60%',
'x-small': '75%',
'small': '89%',
'medium': '100%',
'large': '120%',
'x-large': '150%',
'xx-large': '200%'
},
fontWeight: {
normal: '400',
bold: '700'
},
outlineWidth: borderWidthAliases,
textShadow: {
none: '0px 0px 0px transparent'
},
boxShadow: {
none: '0px 0px 0px 0px transparent'
}
};
function antiAlias(property, value) {
if (property in aliases) {
return aliases[property][value] || value;
}
return value;
}
function isNotAnimatable(property) {
// https://w3c.github.io/web-animations/#concept-not-animatable
return property === 'display' || property.lastIndexOf('animation', 0) === 0 || property.lastIndexOf('transition', 0) === 0;
}
// This delegates parsing shorthand value syntax to the browser.
function expandShorthandAndAntiAlias(property, value, result) {
if (isNotAnimatable(property)) {
return;
}
var longProperties = shorthandToLonghand[property];
if (longProperties) {
shorthandExpanderElem.style[property] = value;
for (var i in longProperties) {
var longProperty = longProperties[i];
var longhandValue = shorthandExpanderElem.style[longProperty];
result[longProperty] = antiAlias(longProperty, longhandValue);
}
} else {
result[property] = antiAlias(property, value);
}
};
function convertToArrayForm(effectInput) {
var normalizedEffectInput = [];
for (var property in effectInput) {
if (property in ['easing', 'offset', 'composite']) {
continue;
}
var values = effectInput[property];
if (!Array.isArray(values)) {
values = [values];
}
var keyframe;
var numKeyframes = values.length;
for (var i = 0; i < numKeyframes; i++) {
keyframe = {};
if ('offset' in effectInput) {
keyframe.offset = effectInput.offset;
} else if (numKeyframes == 1) {
keyframe.offset = 1.0;
} else {
keyframe.offset = i / (numKeyframes - 1.0);
}
if ('easing' in effectInput) {
keyframe.easing = effectInput.easing;
}
if ('composite' in effectInput) {
keyframe.composite = effectInput.composite;
}
keyframe[property] = values[i];
normalizedEffectInput.push(keyframe);
}
}
normalizedEffectInput.sort(function(a, b) { return a.offset - b.offset; });
return normalizedEffectInput;
};
function normalizeKeyframes(effectInput) {
if (effectInput == null) {
return [];
}
if (window.Symbol && Symbol.iterator && Array.prototype.from && effectInput[Symbol.iterator]) {
// Handle custom iterables in most browsers by converting to an array
effectInput = Array.from(effectInput);
}
if (!Array.isArray(effectInput)) {
effectInput = convertToArrayForm(effectInput);
}
var keyframes = effectInput.map(function(originalKeyframe) {
var keyframe = {};
for (var member in originalKeyframe) {
var memberValue = originalKeyframe[member];
if (member == 'offset') {
if (memberValue != null) {
memberValue = Number(memberValue);
if (!isFinite(memberValue))
throw new TypeError('Keyframe offsets must be numbers.');
if (memberValue < 0 || memberValue > 1)
throw new TypeError('Keyframe offsets must be between 0 and 1.');
}
} else if (member == 'composite') {
if (memberValue == 'add' || memberValue == 'accumulate') {
throw {
type: DOMException.NOT_SUPPORTED_ERR,
name: 'NotSupportedError',
message: 'add compositing is not supported'
};
} else if (memberValue != 'replace') {
throw new TypeError('Invalid composite mode ' + memberValue + '.');
}
} else if (member == 'easing') {
memberValue = shared.normalizeEasing(memberValue);
} else {
memberValue = '' + memberValue;
}
expandShorthandAndAntiAlias(member, memberValue, keyframe);
}
if (keyframe.offset == undefined)
keyframe.offset = null;
if (keyframe.easing == undefined)
keyframe.easing = 'linear';
return keyframe;
});
var everyFrameHasOffset = true;
var looselySortedByOffset = true;
var previousOffset = -Infinity;
for (var i = 0; i < keyframes.length; i++) {
var offset = keyframes[i].offset;
if (offset != null) {
if (offset < previousOffset) {
throw new TypeError('Keyframes are not loosely sorted by offset. Sort or specify offsets.');
}
previousOffset = offset;
} else {
everyFrameHasOffset = false;
}
}
keyframes = keyframes.filter(function(keyframe) {
return keyframe.offset >= 0 && keyframe.offset <= 1;
});
function spaceKeyframes() {
var length = keyframes.length;
if (keyframes[length - 1].offset == null)
keyframes[length - 1].offset = 1;
if (length > 1 && keyframes[0].offset == null)
keyframes[0].offset = 0;
var previousIndex = 0;
var previousOffset = keyframes[0].offset;
for (var i = 1; i < length; i++) {
var offset = keyframes[i].offset;
if (offset != null) {
for (var j = 1; j < i - previousIndex; j++)
keyframes[previousIndex + j].offset = previousOffset + (offset - previousOffset) * j / (i - previousIndex);
previousIndex = i;
previousOffset = offset;
}
}
}
if (!everyFrameHasOffset)
spaceKeyframes();
return keyframes;
}
shared.convertToArrayForm = convertToArrayForm;
shared.normalizeKeyframes = normalizeKeyframes;
if (WEB_ANIMATIONS_TESTING) {
testing.normalizeKeyframes = normalizeKeyframes;
}
})(webAnimationsShared, webAnimationsTesting);