Added Polymer

This commit is contained in:
Thaum
2014-11-26 10:18:35 +00:00
parent 5eea4714b2
commit 3408ba9e8d
1210 changed files with 394645 additions and 47 deletions

View File

@@ -0,0 +1,135 @@
// 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, scope, testing) {
function groupChildDuration(node) {
return node._timing.delay + node.activeDuration + node._timing.endDelay;
};
function KeyframeEffect(effect) {
this._frames = shared.normalizeKeyframes(effect);
}
KeyframeEffect.prototype = {
getFrames: function() { return this._frames; }
};
scope.Animation = function(target, effect, timingInput) {
this.target = target;
// TODO: Make modifications to specified update the underlying player
this._timing = shared.normalizeTimingInput(timingInput);
this.timing = shared.makeTiming(timingInput);
// TODO: Make this a live object - will need to separate normalization of
// keyframes into a shared module.
if (typeof effect == 'function')
this.effect = effect;
else
this.effect = new KeyframeEffect(effect);
this._effect = effect;
this.activeDuration = shared.calculateActiveDuration(this._timing);
return this;
};
var originalElementAnimate = Element.prototype.animate;
Element.prototype.animate = function(effect, timing) {
return scope.timeline.play(new scope.Animation(this, effect, timing));
};
var nullTarget = document.createElement('div');
scope.newUnderlyingPlayerForAnimation = function(animation) {
var target = animation.target || nullTarget;
var effect = animation._effect;
if (typeof effect == 'function') {
effect = [];
}
return originalElementAnimate.apply(target, [effect, animation.timing]);
};
scope.bindPlayerForAnimation = function(player) {
if (player.source && typeof player.source.effect == 'function') {
scope.bindPlayerForCustomEffect(player);
}
};
var pendingGroups = [];
scope.awaitStartTime = function(groupPlayer) {
if (groupPlayer.startTime !== null || !groupPlayer._isGroup)
return;
if (pendingGroups.length == 0) {
requestAnimationFrame(updatePendingGroups);
}
pendingGroups.push(groupPlayer);
};
function updatePendingGroups() {
var updated = false;
while (pendingGroups.length) {
pendingGroups.shift()._updateChildren();
updated = true;
}
return updated;
}
var originalGetComputedStyle = window.getComputedStyle;
Object.defineProperty(window, 'getComputedStyle', {
configurable: true,
enumerable: true,
value: function() {
var result = originalGetComputedStyle.apply(this, arguments);
if (updatePendingGroups())
result = originalGetComputedStyle.apply(this, arguments);
return result;
},
});
// TODO: Call into this less frequently.
scope.Player.prototype._updateChildren = function() {
if (this.startTime === null || !this.source || !this._isGroup)
return;
var offset = this.source._timing.delay;
for (var i = 0; i < this.source.children.length; i++) {
var child = this.source.children[i];
var childPlayer;
if (i >= this._childPlayers.length) {
childPlayer = window.document.timeline.play(child);
child.player = this.source.player;
this._childPlayers.push(childPlayer);
} else {
childPlayer = this._childPlayers[i];
}
if (childPlayer.startTime != this.startTime + offset) {
childPlayer.startTime = this.startTime + offset;
childPlayer._updateChildren();
}
if (this.playbackRate == -1 && this.currentTime < offset && childPlayer.currentTime !== -1) {
childPlayer.currentTime = -1;
}
if (this.source instanceof window.AnimationSequence)
offset += groupChildDuration(child);
}
};
window.Animation = scope.Animation;
window.Element.prototype.getAnimationPlayers = function() {
return document.timeline.getAnimationPlayers().filter(function(player) {
return player.source !== null && player.source.target == this;
}.bind(this));
};
scope.groupChildDuration = groupChildDuration;
}(webAnimationsShared, webAnimationsMaxifill, webAnimationsTesting));

View File

@@ -0,0 +1,31 @@
// 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, scope) {
scope.AnimationNode = function(timing) {
var timeFraction = 0;
var activeDuration = shared.calculateActiveDuration(timing);
var animationNode = function(localTime) {
return shared.calculateTimeFraction(activeDuration, localTime, timing);
};
animationNode._totalDuration = timing.delay + activeDuration + timing.endDelay;
animationNode._isCurrent = function(localTime) {
var phase = shared.calculatePhase(activeDuration, localTime, timing);
return phase === PhaseActive || phase === PhaseBefore;
};
return animationNode;
};
})(webAnimationsShared, webAnimationsMinifill);

View File

@@ -0,0 +1,65 @@
// 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, scope, testing) {
scope.Animation = function(target, effectInput, timingInput) {
var animationNode = scope.AnimationNode(shared.normalizeTimingInput(timingInput));
var effect = scope.convertEffectInput(effectInput);
var timeFraction;
var animation = function() {
WEB_ANIMATIONS_TESTING && console.assert(typeof timeFraction !== 'undefined');
effect(target, timeFraction);
};
// Returns whether the animation is in effect or not after the timing update.
animation._update = function(localTime) {
timeFraction = animationNode(localTime);
return timeFraction !== null;
};
animation._clear = function() {
effect(target, null);
};
animation._hasSameTarget = function(otherTarget) {
return target === otherTarget;
};
animation._isCurrent = animationNode._isCurrent;
animation._totalDuration = animationNode._totalDuration;
return animation;
};
scope.NullAnimation = function(clear) {
var nullAnimation = function() {
if (clear) {
clear();
clear = null;
}
};
nullAnimation._update = function() {
return null;
};
nullAnimation._totalDuration = 0;
nullAnimation._isCurrent = function() {
return false;
};
nullAnimation._hasSameTarget = function() {
return false;
};
return nullAnimation;
};
if (WEB_ANIMATIONS_TESTING) {
testing.minifillAnimation = scope.Animation;
}
})(webAnimationsShared, webAnimationsMinifill, webAnimationsTesting);

View File

@@ -0,0 +1,183 @@
// 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(scope, testing) {
var styleAttributes = {
cssText: 1,
length: 1,
parentRule: 1,
};
var styleMethods = {
getPropertyCSSValue: 1,
getPropertyPriority: 1,
getPropertyValue: 1,
item: 1,
removeProperty: 1,
setProperty: 1,
};
var styleMutatingMethods = {
removeProperty: 1,
setProperty: 1,
};
function configureProperty(object, property, descriptor) {
descriptor.enumerable = true;
descriptor.configurable = true;
Object.defineProperty(object, property, descriptor);
}
function AnimatedCSSStyleDeclaration(element) {
WEB_ANIMATIONS_TESTING && console.assert(!(element.style instanceof AnimatedCSSStyleDeclaration),
'Element must not already have an animated style attached.');
// Stores the inline style of the element on its behalf while the
// polyfill uses the element's inline style to simulate web animations.
// This is needed to fake regular inline style CSSOM access on the element.
this._surrogateElement = document.createElement('div');
this._surrogateStyle = this._surrogateElement.style;
this._style = element.style;
this._length = 0;
this._isAnimatedProperty = {};
// Copy the inline style contents over to the surrogate.
for (var i = 0; i < this._style.length; i++) {
var property = this._style[i];
this._surrogateStyle[property] = this._style[property];
}
this._updateIndices();
}
AnimatedCSSStyleDeclaration.prototype = {
get cssText() {
return this._surrogateStyle.cssText;
},
set cssText(text) {
var isAffectedProperty = {};
for (var i = 0; i < this._surrogateStyle.length; i++) {
isAffectedProperty[this._surrogateStyle[i]] = true;
}
this._surrogateStyle.cssText = text;
this._updateIndices();
for (var i = 0; i < this._surrogateStyle.length; i++) {
isAffectedProperty[this._surrogateStyle[i]] = true;
}
for (var property in isAffectedProperty) {
if (!this._isAnimatedProperty[property]) {
this._style.setProperty(property, this._surrogateStyle.getPropertyValue(property));
}
}
},
get length() {
return this._surrogateStyle.length;
},
get parentRule() {
return this._style.parentRule;
},
// Mirror the indexed getters and setters of the surrogate style.
_updateIndices: function() {
while (this._length < this._surrogateStyle.length) {
Object.defineProperty(this, this._length, {
configurable: true,
enumerable: false,
get: (function(index) {
return function() { return this._surrogateStyle[index]; };
})(this._length)
});
this._length++;
}
while (this._length > this._surrogateStyle.length) {
this._length--;
Object.defineProperty(this, this._length, {
configurable: true,
enumerable: false,
value: undefined
});
}
},
_set: function(property, value) {
this._style[property] = value;
this._isAnimatedProperty[property] = true;
},
_clear: function(property) {
this._style[property] = this._surrogateStyle[property];
delete this._isAnimatedProperty[property];
},
};
// Wrap the style methods.
for (var method in styleMethods) {
AnimatedCSSStyleDeclaration.prototype[method] = (function(method, modifiesStyle) {
return function() {
var result = this._surrogateStyle[method].apply(this._surrogateStyle, arguments);
if (modifiesStyle) {
if (!this._isAnimatedProperty[arguments[0]])
this._style[method].apply(this._style, arguments);
this._updateIndices();
}
return result;
}
})(method, method in styleMutatingMethods);
}
// Wrap the style.cssProperty getters and setters.
for (var property in document.documentElement.style) {
if (property in styleAttributes || property in styleMethods) {
continue;
}
(function(property) {
configureProperty(AnimatedCSSStyleDeclaration.prototype, property, {
get: function() {
return this._surrogateStyle[property];
},
set: function(value) {
this._surrogateStyle[property] = value;
this._updateIndices();
if (!this._isAnimatedProperty[property])
this._style[property] = value;
}
});
})(property);
}
function ensureStyleIsPatched(element) {
if (element._webAnimationsPatchedStyle)
return;
// If this style patch fails (on Safari and iOS) use the apply-preserving-inline-style-methods.js
// module instead and restrict inline style interactions to the methods listed in styleMethods.
var animatedStyle = new AnimatedCSSStyleDeclaration(element);
configureProperty(element, 'style', { get: function() { return animatedStyle; } });
// We must keep a handle on the patched style to prevent it from getting GC'd.
element._webAnimationsPatchedStyle = element.style;
}
scope.apply = function(element, property, value) {
ensureStyleIsPatched(element);
element.style._set(scope.propertyName(property), value);
};
scope.clear = function(element, property) {
if (element._webAnimationsPatchedStyle) {
element.style._clear(scope.propertyName(property));
}
};
if (WEB_ANIMATIONS_TESTING)
testing.ensureStyleIsPatched = ensureStyleIsPatched;
})(webAnimationsMinifill, webAnimationsTesting);

View File

@@ -0,0 +1,25 @@
// 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(scope, testing) {
scope.apply = function(element, property, value) {
element.style[scope.propertyName(property)] = value;
};
scope.clear = function(element, property) {
element.style[scope.propertyName(property)] = '';
};
})(webAnimationsMinifill, webAnimationsTesting);

View File

@@ -0,0 +1,57 @@
// 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(scope, testing) {
function consumeLengthPercentOrAuto(string) {
return scope.consumeLengthOrPercent(string) || scope.consumeToken(/^auto/, string);
}
function parseBox(string) {
var result = scope.consumeList([
scope.ignore(scope.consumeToken.bind(null, /^rect/)),
scope.ignore(scope.consumeToken.bind(null, /^\(/)),
scope.consumeRepeated.bind(null, consumeLengthPercentOrAuto, /^,/),
scope.ignore(scope.consumeToken.bind(null, /^\)/)),
], string);
if (result && result[0].length == 4) {
return result[0];
}
}
function mergeComponent(left, right) {
if (left == 'auto' || right == 'auto') {
return [true, false, function(t) {
var result = t ? left : right;
if (result == 'auto') {
return 'auto';
}
// FIXME: There's probably a better way to turn a dimension back into a string.
var merged = scope.mergeDimensions(result, result);
return merged[2](merged[0]);
}];
}
return scope.mergeDimensions(left, right);
}
function wrap(result) {
return 'rect(' + result + ')';
}
var mergeBoxes = scope.mergeWrappedNestedRepeated.bind(null, wrap, mergeComponent, ', ');
scope.parseBox = parseBox;
scope.mergeBoxes = mergeBoxes;
scope.addPropertiesHandler(parseBox, mergeBoxes, ['clip']);
})(webAnimationsMinifill, webAnimationsTesting);

View File

@@ -0,0 +1,62 @@
// 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(scope, testing) {
var canvas = document.createElement('canvas');
canvas.width = canvas.height = 1;
var context = canvas.getContext('2d');
function parseColor(string) {
string = string.trim();
// The context ignores invalid colors
context.fillStyle = '#000';
context.fillStyle = string;
var contextSerializedFillStyle = context.fillStyle;
context.fillStyle = '#fff';
context.fillStyle = string;
if (contextSerializedFillStyle != context.fillStyle)
return;
context.fillRect(0, 0, 1, 1);
var pixelColor = context.getImageData(0, 0, 1, 1).data;
context.clearRect(0, 0, 1, 1);
var alpha = pixelColor[3] / 255;
return [pixelColor[0] * alpha, pixelColor[1] * alpha, pixelColor[2] * alpha, alpha];
}
function mergeColors(left, right) {
return [left, right, function(x) {
function clamp(v) {
return Math.max(0, Math.min(255, v));
}
if (x[3]) {
for (var i = 0; i < 3; i++)
x[i] = Math.round(clamp(x[i] / x[3]));
}
x[3] = scope.numberToString(scope.clamp(0, 1, x[3]));
return 'rgba(' + x.join(',') + ')';
}];
}
scope.addPropertiesHandler(parseColor, mergeColors,
['background-color', 'border-bottom-color', 'border-left-color', 'border-right-color',
'border-top-color', 'color', 'outline-color', 'text-decoration-color']);
scope.consumeColor = scope.consumeParenthesised.bind(null, parseColor);
scope.mergeColors = mergeColors;
if (WEB_ANIMATIONS_TESTING) {
testing.parseColor = parseColor;
}
})(webAnimationsMinifill, webAnimationsTesting);

View File

@@ -0,0 +1,16 @@
// 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.
var WEB_ANIMATIONS_TESTING = false;
var webAnimationsTesting = null;

View File

@@ -0,0 +1,167 @@
// 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(scope, testing) {
function parseDimension(unitRegExp, string) {
string = string.trim().toLowerCase();
if (string == '0' && 'px'.search(unitRegExp) >= 0)
return {px: 0};
// If we have parenthesis, we're a calc and need to start with 'calc'.
if (!/^[^(]*$|^calc/.test(string))
return;
string = string.replace(/calc\(/g, '(');
// We tag units by prefixing them with 'U' (note that we are already
// lowercase) to prevent problems with types which are substrings of
// each other (although prefixes may be problematic!)
var matchedUnits = {};
string = string.replace(unitRegExp, function(match) {
matchedUnits[match] = null;
return 'U' + match;
});
var taggedUnitRegExp = 'U(' + unitRegExp.source + ')';
// Validating input is simply applying as many reductions as we can.
var typeCheck = string.replace(/[-+]?(\d*\.)?\d+/g, 'N')
.replace(new RegExp('N' + taggedUnitRegExp, 'g'), 'D')
.replace(/\s[+-]\s/g, 'O')
.replace(/\s/g, '');
var reductions = [/N\*(D)/g, /(N|D)[*/]N/g, /(N|D)O\1/g, /\((N|D)\)/g];
var i = 0;
while (i < reductions.length) {
if (reductions[i].test(typeCheck)) {
typeCheck = typeCheck.replace(reductions[i], '$1');
i = 0;
} else {
i++;
}
}
if (typeCheck != 'D')
return;
for (var unit in matchedUnits) {
var result = eval(string.replace(new RegExp('U' + unit, 'g'), '').replace(new RegExp(taggedUnitRegExp, 'g'), '*0'));
if (!isFinite(result))
return;
matchedUnits[unit] = result;
}
return matchedUnits;
}
function mergeDimensionsNonNegative(left, right) {
return mergeDimensions(left, right, true);
}
function mergeDimensions(left, right, nonNegative) {
var units = [], unit;
for (unit in left)
units.push(unit);
for (unit in right) {
if (units.indexOf(unit) < 0)
units.push(unit);
}
left = units.map(function(unit) { return left[unit] || 0; });
right = units.map(function(unit) { return right[unit] || 0; });
return [left, right, function(values) {
var result = values.map(function(value, i) {
if (values.length == 1 && nonNegative) {
value = Math.max(value, 0);
}
// Scientific notation (e.g. 1e2) is not yet widely supported by browser vendors.
return scope.numberToString(value) + units[i];
}).join(' + ');
return values.length > 1 ? 'calc(' + result + ')' : result;
}];
}
var lengthUnits = 'px|em|ex|ch|rem|vw|vh|vmin|vmax|cm|mm|in|pt|pc';
var parseLength = parseDimension.bind(null, new RegExp(lengthUnits, 'g'));
var parseLengthOrPercent = parseDimension.bind(null, new RegExp(lengthUnits + '|%', 'g'));
var parseAngle = parseDimension.bind(null, /deg|rad|grad|turn/g);
scope.parseLength = parseLength;
scope.parseLengthOrPercent = parseLengthOrPercent;
scope.consumeLengthOrPercent = scope.consumeParenthesised.bind(null, parseLengthOrPercent);
scope.parseAngle = parseAngle;
scope.mergeDimensions = mergeDimensions;
var consumeLength = scope.consumeParenthesised.bind(null, parseLength);
var consumeSizePair = scope.consumeRepeated.bind(undefined, consumeLength, /^/);
var consumeSizePairList = scope.consumeRepeated.bind(undefined, consumeSizePair, /^,/);
scope.consumeSizePairList = consumeSizePairList;
var parseSizePairList = function(input) {
var result = consumeSizePairList(input);
if (result && result[1] == '') {
return result[0];
}
};
var mergeNonNegativeSizePair = scope.mergeNestedRepeated.bind(undefined, mergeDimensionsNonNegative, ' ');
var mergeNonNegativeSizePairList = scope.mergeNestedRepeated.bind(undefined, mergeNonNegativeSizePair, ',');
scope.mergeNonNegativeSizePair = mergeNonNegativeSizePair;
scope.addPropertiesHandler(parseSizePairList, mergeNonNegativeSizePairList, [
'background-size'
]);
scope.addPropertiesHandler(parseLengthOrPercent, mergeDimensionsNonNegative, [
'border-bottom-width',
'border-image-width',
'border-left-width',
'border-right-width',
'border-top-width',
'flex-basis',
'font-size',
'height',
'line-height',
'max-height',
'max-width',
'outline-width',
'width',
]);
scope.addPropertiesHandler(parseLengthOrPercent, mergeDimensions, [
'border-bottom-left-radius',
'border-bottom-right-radius',
'border-top-left-radius',
'border-top-right-radius',
'bottom',
'left',
'letter-spacing',
'margin-bottom',
'margin-left',
'margin-right',
'margin-top',
'min-height',
'min-width',
'outline-offset',
'padding-bottom',
'padding-left',
'padding-right',
'padding-top',
'perspective',
'right',
'shape-margin',
'text-indent',
'top',
'vertical-align',
'word-spacing',
]);
})(webAnimationsMinifill, webAnimationsTesting);

View File

@@ -0,0 +1,86 @@
// 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, scope, testing) {
var nullTarget = document.createElement('div');
var sequenceNumber = 0;
scope.bindPlayerForCustomEffect = function(player) {
var target = player.source.target;
var effect = player.source.effect;
var timing = player.source.timing;
var last = undefined;
timing = shared.normalizeTimingInput(timing);
var callback = function() {
var t = callback._player ? callback._player.currentTime : null;
if (t !== null) {
t = shared.calculateTimeFraction(shared.calculateActiveDuration(timing), t, timing);
if (isNaN(t))
t = null;
}
// FIXME: There are actually more conditions under which the effect
// should be called.
if (t !== last)
effect(t, target, player.source);
last = t;
};
callback._player = player;
callback._registered = false;
callback._sequenceNumber = sequenceNumber++;
player._callback = callback;
register(callback);
};
var callbacks = [];
var ticking = false;
function register(callback) {
if (callback._registered)
return;
callback._registered = true;
callbacks.push(callback);
if (!ticking) {
ticking = true;
requestAnimationFrame(tick);
}
}
function tick(t) {
var updating = callbacks;
callbacks = [];
updating.sort(function(left, right) {
return left._sequenceNumber - right._sequenceNumber;
});
updating.filter(function(callback) {
callback();
if (!callback._player || callback._player.finished || callback._player.paused)
callback._registered = false;
return callback._registered;
});
callbacks.push.apply(callbacks, updating);
if (callbacks.length) {
ticking = true;
requestAnimationFrame(tick);
} else {
ticking = false;
}
}
scope.Player.prototype._register = function() {
if (this._callback)
register(this._callback);
};
})(webAnimationsShared, webAnimationsMaxifill, webAnimationsTesting);

View File

@@ -0,0 +1,110 @@
// 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, scope, testing) {
scope.convertEffectInput = function(effectInput) {
var keyframeEffect = shared.normalizeKeyframes(effectInput);
var propertySpecificKeyframeGroups = makePropertySpecificKeyframeGroups(keyframeEffect);
var interpolations = makeInterpolations(propertySpecificKeyframeGroups);
return function(target, fraction) {
if (fraction != null) {
interpolations.filter(function(interpolation) {
return (fraction <= 0 && interpolation.startTime == 0) ||
(fraction >= 1 && interpolation.endTime == 1) ||
(fraction >= interpolation.startTime && fraction <= interpolation.endTime);
}).forEach(function(interpolation) {
var offsetFraction = fraction - interpolation.startTime;
var localDuration = interpolation.endTime - interpolation.startTime;
var scaledLocalTime = localDuration == 0 ? 0 : interpolation.easing(offsetFraction / localDuration);
scope.apply(target, interpolation.property, interpolation.interpolation(scaledLocalTime));
});
} else {
for (var property in propertySpecificKeyframeGroups)
if (property != 'offset' && property != 'easing' && property != 'composite')
scope.clear(target, property);
}
};
};
function makePropertySpecificKeyframeGroups(keyframeEffect) {
var propertySpecificKeyframeGroups = {};
for (var i = 0; i < keyframeEffect.length; i++) {
for (var member in keyframeEffect[i]) {
if (member != 'offset' && member != 'easing' && member != 'composite') {
var propertySpecificKeyframe = {
offset: keyframeEffect[i].offset,
easing: keyframeEffect[i].easing,
value: keyframeEffect[i][member]
};
propertySpecificKeyframeGroups[member] = propertySpecificKeyframeGroups[member] || [];
propertySpecificKeyframeGroups[member].push(propertySpecificKeyframe);
}
}
}
for (var groupName in propertySpecificKeyframeGroups) {
var group = propertySpecificKeyframeGroups[groupName];
if (group[0].offset != 0 || group[group.length - 1].offset != 1) {
throw {
type: DOMException.NOT_SUPPORTED_ERR,
name: 'NotSupportedError',
message: 'Partial keyframes are not supported'
};
}
}
return propertySpecificKeyframeGroups;
}
function makeInterpolations(propertySpecificKeyframeGroups) {
var interpolations = [];
for (var groupName in propertySpecificKeyframeGroups) {
var group = propertySpecificKeyframeGroups[groupName];
for (var i = 0; i < group.length - 1; i++) {
var startTime = group[i].offset;
var endTime = group[i + 1].offset;
var startValue = group[i].value;
var endValue = group[i + 1].value;
if (startTime == endTime) {
if (endTime == 1) {
startValue = endValue;
} else {
endValue = startValue;
}
}
interpolations.push({
startTime: startTime,
endTime: endTime,
easing: group[i].easing,
property: groupName,
interpolation: scope.propertyInterpolation(groupName, startValue, endValue)
});
}
}
interpolations.sort(function(leftInterpolation, rightInterpolation) {
return leftInterpolation.startTime - rightInterpolation.startTime;
});
return interpolations;
}
if (WEB_ANIMATIONS_TESTING) {
testing.makePropertySpecificKeyframeGroups = makePropertySpecificKeyframeGroups;
testing.makeInterpolations = makeInterpolations;
}
})(webAnimationsShared, webAnimationsMinifill, webAnimationsTesting);

View File

@@ -0,0 +1,19 @@
// 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(scope) {
window.Element.prototype.animate = function(effectInput, timingInput) {
return scope.timeline._play(scope.Animation(this, effectInput, timingInput));
};
})(webAnimationsMinifill);

View File

@@ -0,0 +1,42 @@
// 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(scope) {
function parse(string) {
var out = Number(string);
if (isNaN(out) || out < 100 || out > 900 || out % 100 !== 0) {
return;
}
return out;
}
function toCss(value) {
value = Math.round(value / 100) * 100;
value = scope.clamp(100, 900, value);
if (value === 400) {
return 'normal';
}
if (value === 700) {
return 'bold';
}
return String(value);
}
function merge(left, right) {
return [left, right, toCss];
}
scope.addPropertiesHandler(parse, merge, ['font-weight']);
})(webAnimationsMinifill);

View File

@@ -0,0 +1,81 @@
// 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, scope, testing) {
function constructor(children, timingInput) {
this.children = children || [];
this._timing = shared.normalizeTimingInput(timingInput, true);
this.timing = shared.makeTiming(timingInput, true);
if (this._timing.duration === 'auto')
this._timing.duration = this.activeDuration;
}
window.AnimationSequence = function() {
constructor.apply(this, arguments);
};
window.AnimationGroup = function() {
constructor.apply(this, arguments);
};
window.AnimationSequence.prototype = {
get activeDuration() {
var total = 0;
this.children.forEach(function(child) {
total += scope.groupChildDuration(child);
});
return Math.max(total, 0);
}
};
window.AnimationGroup.prototype = {
get activeDuration() {
var max = 0;
this.children.forEach(function(child) {
max = Math.max(max, scope.groupChildDuration(child));
});
return max;
}
};
scope.newUnderlyingPlayerForGroup = function(group) {
var underlyingPlayer;
var ticker = function(tf) {
var player = underlyingPlayer._wrapper;
if (!player.source)
return;
if (tf == null) {
player._removePlayers();
return;
}
if (player.startTime === null)
return;
player._updateChildren();
};
underlyingPlayer = scope.timeline.play(new scope.Animation(null, ticker, group._timing));
return underlyingPlayer;
};
scope.bindPlayerForGroup = function(player) {
player._player._wrapper = player;
player._isGroup = true;
scope.awaitStartTime(player);
};
})(webAnimationsShared, webAnimationsMaxifill, webAnimationsTesting);

View File

@@ -0,0 +1,177 @@
// 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(scope) {
// consume* functions return a 2 value array of [parsed-data, '' or not-yet consumed input]
// Regex should be anchored with /^
function consumeToken(regex, string) {
var result = regex.exec(string);
if (result) {
result = regex.ignoreCase ? result[0].toLowerCase() : result[0];
return [result, string.substr(result.length)];
}
}
function consumeTrimmed(consumer, string) {
string = string.replace(/^\s*/, '');
var result = consumer(string);
if (result) {
return [result[0], result[1].replace(/^\s*/, '')];
}
}
function consumeRepeated(consumer, separator, string) {
consumer = consumeTrimmed.bind(null, consumer);
var list = [];
while (true) {
var result = consumer(string);
if (!result) {
return [list, string];
}
list.push(result[0]);
string = result[1];
result = consumeToken(separator, string);
if (!result || result[1] == '') {
return [list, string];
}
string = result[1];
}
}
// Consumes a token or expression with balanced parentheses
function consumeParenthesised(parser, string) {
var nesting = 0;
for (var n = 0; n < string.length; n++) {
if (/\s|,/.test(string[n]) && nesting == 0) {
break;
} else if (string[n] == '(') {
nesting++;
} else if (string[n] == ')') {
nesting--;
if (nesting == 0)
n++;
if (nesting <= 0)
break;
}
}
var parsed = parser(string.substr(0, n));
return parsed == undefined ? undefined : [parsed, string.substr(n)];
}
function lcm(a, b) {
var c = a;
var d = b;
while (c && d)
c > d ? c %= d : d %= c;
c = (a * b) / (c + d);
return c;
}
function ignore(value) {
return function(input) {
var result = value(input);
if (result)
result[0] = undefined;
return result;
}
}
function optional(value, defaultValue) {
return function(input) {
var result = value(input);
if (result)
return result;
return [defaultValue, input];
}
}
function consumeList(list, input) {
var output = [];
for (var i = 0; i < list.length; i++) {
var result = scope.consumeTrimmed(list[i], input);
if (!result || result[0] == '')
return;
if (result[0] !== undefined)
output.push(result[0]);
input = result[1];
}
if (input == '') {
return output;
}
}
function mergeWrappedNestedRepeated(wrap, nestedMerge, separator, left, right) {
var matchingLeft = [];
var matchingRight = [];
var reconsititution = [];
var length = lcm(left.length, right.length);
for (var i = 0; i < length; i++) {
var thing = nestedMerge(left[i % left.length], right[i % right.length]);
if (!thing) {
return;
}
matchingLeft.push(thing[0]);
matchingRight.push(thing[1]);
reconsititution.push(thing[2]);
}
return [matchingLeft, matchingRight, function(positions) {
var result = positions.map(function(position, i) {
return reconsititution[i](position);
}).join(separator);
return wrap ? wrap(result) : result;
}];
}
function mergeList(left, right, list) {
var lefts = [];
var rights = [];
var functions = [];
var j = 0;
for (var i = 0; i < list.length; i++) {
if (typeof list[i] == 'function') {
var result = list[i](left[j], right[j++]);
lefts.push(result[0]);
rights.push(result[1]);
functions.push(result[2]);
} else {
(function(pos) {
lefts.push(false);
rights.push(false);
functions.push(function() { return list[pos]; });
})(i);
}
}
return [lefts, rights, function(results) {
var result = '';
for (var i = 0; i < results.length; i++) {
result += functions[i](results[i]);
}
return result;
}];
}
scope.consumeToken = consumeToken;
scope.consumeTrimmed = consumeTrimmed;
scope.consumeRepeated = consumeRepeated;
scope.consumeParenthesised = consumeParenthesised;
scope.ignore = ignore;
scope.optional = optional;
scope.consumeList = consumeList;
scope.mergeNestedRepeated = mergeWrappedNestedRepeated.bind(null, null);
scope.mergeWrappedNestedRepeated = mergeWrappedNestedRepeated;
scope.mergeList = mergeList;
})(webAnimationsMinifill);

View File

@@ -0,0 +1,49 @@
// 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(scope, testing) {
function interpolate(from, to, f) {
if ((typeof from == 'number') && (typeof to == 'number')) {
return from * (1 - f) + to * f;
}
if ((typeof from == 'boolean') && (typeof to == 'boolean')) {
return f < 0.5 ? from : to;
}
WEB_ANIMATIONS_TESTING && console.assert(
Array.isArray(from) && Array.isArray(to),
'If interpolation arguments are not numbers or bools they must be arrays');
if (from.length == to.length) {
var r = [];
for (var i = 0; i < from.length; i++) {
r.push(interpolate(from[i], to[i], f));
}
return r;
}
throw 'Mismatched interpolation arguments ' + from + ':' + to;
}
scope.Interpolation = function(from, to, convertToString) {
return function(f) {
return convertToString(interpolate(from, to, f));
}
};
if (WEB_ANIMATIONS_TESTING) {
testing.interpolate = interpolate;
}
})(webAnimationsMinifill, webAnimationsTesting);

View File

@@ -0,0 +1,452 @@
// 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(scope, testing) {
var decomposeMatrix = (function() {
function determinant(m) {
return m[0][0] * m[1][1] * m[2][2] +
m[1][0] * m[2][1] * m[0][2] +
m[2][0] * m[0][1] * m[1][2] -
m[0][2] * m[1][1] * m[2][0] -
m[1][2] * m[2][1] * m[0][0] -
m[2][2] * m[0][1] * m[1][0];
}
// from Wikipedia:
//
// [A B]^-1 = [A^-1 + A^-1B(D - CA^-1B)^-1CA^-1 -A^-1B(D - CA^-1B)^-1]
// [C D] [-(D - CA^-1B)^-1CA^-1 (D - CA^-1B)^-1 ]
//
// Therefore
//
// [A [0]]^-1 = [A^-1 [0]]
// [C 1 ] [ -CA^-1 1 ]
function inverse(m) {
var iDet = 1 / determinant(m);
var a = m[0][0], b = m[0][1], c = m[0][2];
var d = m[1][0], e = m[1][1], f = m[1][2];
var g = m[2][0], h = m[2][1], k = m[2][2];
var Ainv = [
[(e * k - f * h) * iDet, (c * h - b * k) * iDet,
(b * f - c * e) * iDet, 0],
[(f * g - d * k) * iDet, (a * k - c * g) * iDet,
(c * d - a * f) * iDet, 0],
[(d * h - e * g) * iDet, (g * b - a * h) * iDet,
(a * e - b * d) * iDet, 0]
];
var lastRow = [];
for (var i = 0; i < 3; i++) {
var val = 0;
for (var j = 0; j < 3; j++) {
val += m[3][j] * Ainv[j][i];
}
lastRow.push(val);
}
lastRow.push(1);
Ainv.push(lastRow);
return Ainv;
}
function transposeMatrix4(m) {
return [[m[0][0], m[1][0], m[2][0], m[3][0]],
[m[0][1], m[1][1], m[2][1], m[3][1]],
[m[0][2], m[1][2], m[2][2], m[3][2]],
[m[0][3], m[1][3], m[2][3], m[3][3]]];
}
function multVecMatrix(v, m) {
var result = [];
for (var i = 0; i < 4; i++) {
var val = 0;
for (var j = 0; j < 4; j++) {
val += v[j] * m[j][i];
}
result.push(val);
}
return result;
}
function normalize(v) {
var len = length(v);
return [v[0] / len, v[1] / len, v[2] / len];
}
function length(v) {
return Math.sqrt(v[0] * v[0] + v[1] * v[1] + v[2] * v[2]);
}
function combine(v1, v2, v1s, v2s) {
return [v1s * v1[0] + v2s * v2[0], v1s * v1[1] + v2s * v2[1],
v1s * v1[2] + v2s * v2[2]];
}
function cross(v1, v2) {
return [v1[1] * v2[2] - v1[2] * v2[1],
v1[2] * v2[0] - v1[0] * v2[2],
v1[0] * v2[1] - v1[1] * v2[0]];
}
// TODO: Implement 2D matrix decomposition.
// http://dev.w3.org/csswg/css-transforms/#decomposing-a-2d-matrix
function decomposeMatrix(matrix) {
var m3d = [
matrix.slice(0, 4),
matrix.slice(4, 8),
matrix.slice(8, 12),
matrix.slice(12, 16)
];
// skip normalization step as m3d[3][3] should always be 1
if (m3d[3][3] !== 1) {
return null;
}
var perspectiveMatrix = [];
for (var i = 0; i < 4; i++) {
perspectiveMatrix.push(m3d[i].slice());
}
for (var i = 0; i < 3; i++) {
perspectiveMatrix[i][3] = 0;
}
if (determinant(perspectiveMatrix) === 0) {
return false;
}
var rhs = [];
var perspective;
if (m3d[0][3] || m3d[1][3] || m3d[2][3]) {
rhs.push(m3d[0][3]);
rhs.push(m3d[1][3]);
rhs.push(m3d[2][3]);
rhs.push(m3d[3][3]);
var inversePerspectiveMatrix = inverse(perspectiveMatrix);
var transposedInversePerspectiveMatrix =
transposeMatrix4(inversePerspectiveMatrix);
perspective = multVecMatrix(rhs, transposedInversePerspectiveMatrix);
} else {
perspective = [0, 0, 0, 1];
}
var translate = m3d[3].slice(0, 3);
var row = [];
row.push(m3d[0].slice(0, 3));
var scale = [];
scale.push(length(row[0]));
row[0] = normalize(row[0]);
var skew = [];
row.push(m3d[1].slice(0, 3));
skew.push(dot(row[0], row[1]));
row[1] = combine(row[1], row[0], 1.0, -skew[0]);
scale.push(length(row[1]));
row[1] = normalize(row[1]);
skew[0] /= scale[1];
row.push(m3d[2].slice(0, 3));
skew.push(dot(row[0], row[2]));
row[2] = combine(row[2], row[0], 1.0, -skew[1]);
skew.push(dot(row[1], row[2]));
row[2] = combine(row[2], row[1], 1.0, -skew[2]);
scale.push(length(row[2]));
row[2] = normalize(row[2]);
skew[1] /= scale[2];
skew[2] /= scale[2];
var pdum3 = cross(row[1], row[2]);
if (dot(row[0], pdum3) < 0) {
for (var i = 0; i < 3; i++) {
scale[i] *= -1;
row[i][0] *= -1;
row[i][1] *= -1;
row[i][2] *= -1;
}
}
var t = row[0][0] + row[1][1] + row[2][2] + 1;
var s;
var quaternion;
if (t > 1e-4) {
s = 0.5 / Math.sqrt(t);
quaternion = [
(row[2][1] - row[1][2]) * s,
(row[0][2] - row[2][0]) * s,
(row[1][0] - row[0][1]) * s,
0.25 / s
];
} else if (row[0][0] > row[1][1] && row[0][0] > row[2][2]) {
s = Math.sqrt(1 + row[0][0] - row[1][1] - row[2][2]) * 2.0;
quaternion = [
0.25 * s,
(row[0][1] + row[1][0]) / s,
(row[0][2] + row[2][0]) / s,
(row[2][1] - row[1][2]) / s
];
} else if (row[1][1] > row[2][2]) {
s = Math.sqrt(1.0 + row[1][1] - row[0][0] - row[2][2]) * 2.0;
quaternion = [
(row[0][1] + row[1][0]) / s,
0.25 * s,
(row[1][2] + row[2][1]) / s,
(row[0][2] - row[2][0]) / s
];
} else {
s = Math.sqrt(1.0 + row[2][2] - row[0][0] - row[1][1]) * 2.0;
quaternion = [
(row[0][2] + row[2][0]) / s,
(row[1][2] + row[2][1]) / s,
0.25 * s,
(row[1][0] - row[0][1]) / s
];
}
return [translate, scale, skew, quaternion, perspective];
}
return decomposeMatrix;
})();
function dot(v1, v2) {
var result = 0;
for (var i = 0; i < v1.length; i++) {
result += v1[i] * v2[i];
}
return result;
}
function multiplyMatrices(a, b) {
return [
a[0] * b[0] + a[4] * b[1] + a[8] * b[2] + a[12] * b[3],
a[1] * b[0] + a[5] * b[1] + a[9] * b[2] + a[13] * b[3],
a[2] * b[0] + a[6] * b[1] + a[10] * b[2] + a[14] * b[3],
a[3] * b[0] + a[7] * b[1] + a[11] * b[2] + a[15] * b[3],
a[0] * b[4] + a[4] * b[5] + a[8] * b[6] + a[12] * b[7],
a[1] * b[4] + a[5] * b[5] + a[9] * b[6] + a[13] * b[7],
a[2] * b[4] + a[6] * b[5] + a[10] * b[6] + a[14] * b[7],
a[3] * b[4] + a[7] * b[5] + a[11] * b[6] + a[15] * b[7],
a[0] * b[8] + a[4] * b[9] + a[8] * b[10] + a[12] * b[11],
a[1] * b[8] + a[5] * b[9] + a[9] * b[10] + a[13] * b[11],
a[2] * b[8] + a[6] * b[9] + a[10] * b[10] + a[14] * b[11],
a[3] * b[8] + a[7] * b[9] + a[11] * b[10] + a[15] * b[11],
a[0] * b[12] + a[4] * b[13] + a[8] * b[14] + a[12] * b[15],
a[1] * b[12] + a[5] * b[13] + a[9] * b[14] + a[13] * b[15],
a[2] * b[12] + a[6] * b[13] + a[10] * b[14] + a[14] * b[15],
a[3] * b[12] + a[7] * b[13] + a[11] * b[14] + a[15] * b[15]
];
}
// TODO: This can probably be made smaller.
function convertItemToMatrix(item) {
switch (item.t) {
// TODO: Handle units other than rads and degs.
case 'rotatex':
var rads = item.d[0].rad || 0;
var degs = item.d[0].deg || 0;
var angle = (degs * Math.PI / 180) + rads;
return [1, 0, 0, 0,
0, Math.cos(angle), Math.sin(angle), 0,
0, -Math.sin(angle), Math.cos(angle), 0,
0, 0, 0, 1];
case 'rotatey':
var rads = item.d[0].rad || 0;
var degs = item.d[0].deg || 0;
var angle = (degs * Math.PI / 180) + rads;
return [Math.cos(angle), 0, -Math.sin(angle), 0,
0, 1, 0, 0,
Math.sin(angle), 0, Math.cos(angle), 0,
0, 0, 0, 1];
case 'rotate':
case 'rotatez':
var rads = item.d[0].rad || 0;
var degs = item.d[0].deg || 0;
var angle = (degs * Math.PI / 180) + rads;
return [Math.cos(angle), Math.sin(angle), 0, 0,
-Math.sin(angle), Math.cos(angle), 0, 0,
0, 0, 1, 0,
0, 0, 0, 1];
case 'rotate3d':
var x = item.d[0];
var y = item.d[1];
var z = item.d[2];
var rads = item.d[3].rad || 0;
var degs = item.d[3].deg || 0;
var angle = (degs * Math.PI / 180) + rads;
var sqrLength = x * x + y * y + z * z;
if (sqrLength === 0) {
x = 1;
y = 0;
z = 0;
} else if (sqrLength !== 1) {
var length = Math.sqrt(sqrLength);
x /= length;
y /= length;
z /= length;
}
var s = Math.sin(angle / 2);
var sc = s * Math.cos(angle / 2);
var sq = s * s;
return [
1 - 2 * (y * y + z * z) * sq,
2 * (x * y * sq + z * sc),
2 * (x * z * sq - y * sc),
0,
2 * (x * y * sq - z * sc),
1 - 2 * (x * x + z * z) * sq,
2 * (y * z * sq + x * sc),
0,
2 * (x * z * sq + y * sc),
2 * (y * z * sq - x * sc),
1 - 2 * (x * x + y * y) * sq,
0,
0, 0, 0, 1
];
case 'scale':
return [item.d[0], 0, 0, 0,
0, item.d[1], 0, 0,
0, 0, 1, 0,
0, 0, 0, 1];
case 'scalex':
return [item.d[0], 0, 0, 0,
0, 1, 0, 0,
0, 0, 1, 0,
0, 0, 0, 1];
case 'scaley':
return [1, 0, 0, 0,
0, item.d[0], 0, 0,
0, 0, 1, 0,
0, 0, 0, 1];
case 'scalez':
return [1, 0, 0, 0,
0, 1, 0, 0,
0, 0, item.d[0], 0,
0, 0, 0, 1];
case 'scale3d':
return [item.d[0], 0, 0, 0,
0, item.d[1], 0, 0,
0, 0, item.d[2], 0,
0, 0, 0, 1];
// FIXME: Skew behaves differently in Blink, FireFox and here. Need to work out why.
case 'skew':
var xDegs = item.d[0].deg || 0;
var xRads = item.d[0].rad || 0;
var yDegs = item.d[1].deg || 0;
var yRads = item.d[1].rad || 0;
var xAngle = (xDegs * Math.PI / 180) + xRads;
var yAngle = (yDegs * Math.PI / 180) + yRads;
return [1, Math.tan(yAngle), 0, 0,
Math.tan(xAngle), 1, 0, 0,
0, 0, 1, 0,
0, 0, 0, 1];
case 'skewx':
var rads = item.d[0].rad || 0;
var degs = item.d[0].deg || 0;
var angle = (degs * Math.PI / 180) + rads;
return [1, 0, 0, 0,
Math.tan(angle), 1, 0, 0,
0, 0, 1, 0,
0, 0, 0, 1];
case 'skewy':
var rads = item.d[0].rad || 0;
var degs = item.d[0].deg || 0;
var angle = (degs * Math.PI / 180) + rads;
return [1, Math.tan(angle), 0, 0,
0, 1, 0, 0,
0, 0, 1, 0,
0, 0, 0, 1];
// TODO: Work out what to do with non-px values.
case 'translate':
var x = item.d[0].px || 0;
var y = item.d[1].px || 0;
return [1, 0, 0, 0,
0, 1, 0, 0,
0, 0, 1, 0,
x, y, 0, 1];
case 'translatex':
var x = item.d[0].px || 0;
return [1, 0, 0, 0,
0, 1, 0, 0,
0, 0, 1, 0,
x, 0, 0, 1];
case 'translatey':
var y = item.d[0].px || 0;
return [1, 0, 0, 0,
0, 1, 0, 0,
0, 0, 1, 0,
0, y, 0, 1];
case 'translatez':
var z = item.d[0].px || 0;
return [1, 0, 0, 0,
0, 1, 0, 0,
0, 0, 1, 0,
0, 0, z, 1];
case 'translate3d':
var x = item.d[0].px || 0;
var y = item.d[1].px || 0;
var z = item.d[2].px || 0;
return [1, 0, 0, 0,
0, 1, 0, 0,
0, 0, 1, 0,
x, y, z, 1];
case 'perspective':
var p = item.d[0].px ? (-1 / item.d[0].px) : 0;
return [
1, 0, 0, 0,
0, 1, 0, 0,
0, 0, 1, p,
0, 0, 0, 1];
case 'matrix':
return [item.d[0], item.d[1], 0, 0,
item.d[2], item.d[3], 0, 0,
0, 0, 1, 0,
item.d[4], item.d[5], 0, 1];
case 'matrix3d':
return item.d;
default:
WEB_ANIMATIONS_TESTING && console.assert(false, 'Transform item type ' + item.t +
' conversion to matrix not yet implemented.');
}
}
function convertToMatrix(transformList) {
if (transformList.length === 0) {
return [1, 0, 0, 0,
0, 1, 0, 0,
0, 0, 1, 0,
0, 0, 0, 1];
}
return transformList.map(convertItemToMatrix).reduce(multiplyMatrices);
}
function makeMatrixDecomposition(transformList) {
return [decomposeMatrix(convertToMatrix(transformList))];
}
scope.dot = dot;
scope.makeMatrixDecomposition = makeMatrixDecomposition;
})(webAnimationsMinifill, webAnimationsTesting);

View File

@@ -0,0 +1,130 @@
// 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(scope, testing) {
var composeMatrix = (function() {
function multiply(a, b) {
var result = [[0, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0]];
for (var i = 0; i < 4; i++) {
for (var j = 0; j < 4; j++) {
for (var k = 0; k < 4; k++) {
result[i][j] += b[i][k] * a[k][j];
}
}
}
return result;
}
function is2D(m) {
return (
m[0][2] == 0 &&
m[0][3] == 0 &&
m[1][2] == 0 &&
m[1][3] == 0 &&
m[2][0] == 0 &&
m[2][1] == 0 &&
m[2][2] == 1 &&
m[2][3] == 0 &&
m[3][2] == 0 &&
m[3][3] == 1);
}
function composeMatrix(translate, scale, skew, quat, perspective) {
var matrix = [[1, 0, 0, 0], [0, 1, 0, 0], [0, 0, 1, 0], [0, 0, 0, 1]];
for (var i = 0; i < 4; i++) {
matrix[i][3] = perspective[i];
}
for (var i = 0; i < 3; i++) {
for (var j = 0; j < 3; j++) {
matrix[3][i] += translate[j] * matrix[j][i];
}
}
var x = quat[0], y = quat[1], z = quat[2], w = quat[3];
var rotMatrix = [[1, 0, 0, 0], [0, 1, 0, 0], [0, 0, 1, 0], [0, 0, 0, 1]];
rotMatrix[0][0] = 1 - 2 * (y * y + z * z);
rotMatrix[0][1] = 2 * (x * y - z * w);
rotMatrix[0][2] = 2 * (x * z + y * w);
rotMatrix[1][0] = 2 * (x * y + z * w);
rotMatrix[1][1] = 1 - 2 * (x * x + z * z);
rotMatrix[1][2] = 2 * (y * z - x * w);
rotMatrix[2][0] = 2 * (x * z - y * w);
rotMatrix[2][1] = 2 * (y * z + x * w);
rotMatrix[2][2] = 1 - 2 * (x * x + y * y);
matrix = multiply(matrix, rotMatrix);
var temp = [[1, 0, 0, 0], [0, 1, 0, 0], [0, 0, 1, 0], [0, 0, 0, 1]];
if (skew[2]) {
temp[2][1] = skew[2];
matrix = multiply(matrix, temp);
}
if (skew[1]) {
temp[2][1] = 0;
temp[2][0] = skew[0];
matrix = multiply(matrix, temp);
}
if (skew[0]) {
temp[2][0] = 0;
temp[1][0] = skew[0];
matrix = multiply(matrix, temp);
}
for (var i = 0; i < 3; i++) {
for (var j = 0; j < 3; j++) {
matrix[i][j] *= scale[i];
}
}
if (is2D(matrix)) {
return [matrix[0][0], matrix[0][1], matrix[1][0], matrix[1][1], matrix[3][0], matrix[3][1]];
}
return matrix[0].concat(matrix[1], matrix[2], matrix[3]);
}
return composeMatrix;
})();
function clamp(x, min, max) {
return Math.max(Math.min(x, max), min);
};
function quat(fromQ, toQ, f) {
var product = scope.dot(fromQ, toQ);
product = clamp(product, -1.0, 1.0);
var quat = [];
if (product === 1.0) {
quat = fromQ;
} else {
var theta = Math.acos(product);
var w = Math.sin(f * theta) * 1 / Math.sqrt(1 - product * product);
for (var i = 0; i < 4; i++) {
quat.push(fromQ[i] * (Math.cos(f * theta) - product * w) +
toQ[i] * w);
}
}
return quat;
}
scope.composeMatrix = composeMatrix;
scope.quat = quat;
})(webAnimationsMinifill, webAnimationsTesting);

View File

@@ -0,0 +1,162 @@
// 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, scope, testing) {
scope.Player = function(source) {
this.source = source;
if (source) {
// FIXME: detach existing player.
source.player = this;
}
this._isGroup = false;
this._player = null;
this._childPlayers = [];
this._callback = null;
this._rebuildUnderlyingPlayer();
// Players are constructed in the idle state.
this._player.cancel();
};
// TODO: add a source getter/setter
scope.Player.prototype = {
_rebuildUnderlyingPlayer: function() {
if (this._player) {
this._player.cancel();
this._player = null;
}
if (!this.source || this.source instanceof window.Animation) {
this._player = scope.newUnderlyingPlayerForAnimation(this.source);
scope.bindPlayerForAnimation(this);
}
if (this.source instanceof window.AnimationSequence || this.source instanceof window.AnimationGroup) {
this._player = scope.newUnderlyingPlayerForGroup(this.source);
scope.bindPlayerForGroup(this);
}
// FIXME: move existing currentTime/startTime/playState to new player
},
get paused() {
return this._player.paused;
},
get playState() {
return this._player.playState;
},
get onfinish() {
return this._onfinish;
},
set onfinish(v) {
if (typeof v == 'function') {
this._onfinish = v;
this._player.onfinish = (function(e) {
e.target = this;
v.call(this, e);
}).bind(this);
} else {
this._player.onfinish = v;
this.onfinish = this._player.onfinish;
}
},
get currentTime() {
return this._player.currentTime;
},
set currentTime(v) {
this._player.currentTime = v;
this._register();
this._forEachChild(function(child, offset) {
child.currentTime = v - offset;
});
},
get startTime() {
return this._player.startTime;
},
set startTime(v) {
this._player.startTime = v;
this._register();
this._forEachChild(function(child, offset) {
child.startTime = v + offset;
});
},
get playbackRate() {
return this._player.playbackRate;
},
get finished() {
return this._player.finished;
},
play: function() {
this._player.play();
this._register();
scope.awaitStartTime(this);
this._forEachChild(function(child) {
var time = child.currentTime;
child.play();
child.currentTime = time;
});
},
pause: function() {
this._player.pause();
this._register();
this._forEachChild(function(child) {
child.pause();
});
},
finish: function() {
this._player.finish();
this._register();
// TODO: child players??
},
cancel: function() {
this._player.cancel();
this._register();
this._removePlayers();
},
reverse: function() {
this._player.reverse();
scope.awaitStartTime(this);
this._register();
this._forEachChild(function(child, offset) {
child.reverse();
child.startTime = this.startTime + offset * this.playbackRate;
child.currentTime = this.currentTime + offset * this.playbackRate;
});
},
addEventListener: function(type, handler) {
var wrapped = handler;
if (typeof handler == 'function') {
wrapped = (function(e) {
e.target = this;
handler.call(this, e);
}).bind(this);
handler._wrapper = wrapped;
}
this._player.addEventListener(type, wrapped);
},
removeEventListener: function(type, handler) {
this._player.removeEventListener(type, (handler && handler._wrapper) || handler);
},
_removePlayers: function() {
while (this._childPlayers.length)
this._childPlayers.pop().cancel();
},
_forEachChild: function(f) {
var offset = 0;
this._childPlayers.forEach(function(child) {
f.call(this, child, offset);
if (this.source instanceof window.AnimationSequence)
offset += child.source.activeDuration;
}.bind(this));
},
};
})(webAnimationsShared, webAnimationsMaxifill, webAnimationsTesting);

View File

@@ -0,0 +1,259 @@
// 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.createElement('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;
}
// This delegates parsing shorthand value syntax to the browser.
function expandShorthandAndAntiAlias(property, value, result) {
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 normalizeKeyframes(effectInput) {
if (!Array.isArray(effectInput) && effectInput !== null)
throw new TypeError('Keyframe effect must be null or an array of keyframes');
if (effectInput == null)
return [];
var keyframeEffect = 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.');
}
} else if (member == 'composite') {
throw {
type: DOMException.NOT_SUPPORTED_ERR,
name: 'NotSupportedError',
message: 'add compositing is not supported'
};
} else if (member == 'easing') {
memberValue = shared.toTimingFunction(memberValue);
} else {
memberValue = '' + memberValue;
}
expandShorthandAndAntiAlias(member, memberValue, keyframe);
}
if (keyframe.offset == undefined)
keyframe.offset = null;
if (keyframe.easing == undefined)
keyframe.easing = shared.toTimingFunction('linear');
return keyframe;
});
var everyFrameHasOffset = true;
var looselySortedByOffset = true;
var previousOffset = -Infinity;
for (var i = 0; i < keyframeEffect.length; i++) {
var offset = keyframeEffect[i].offset;
if (offset != null) {
if (offset < previousOffset) {
throw {
code: DOMException.INVALID_MODIFICATION_ERR,
name: 'InvalidModificationError',
message: 'Keyframes are not loosely sorted by offset. Sort or specify offsets.'
};
}
previousOffset = offset;
} else {
everyFrameHasOffset = false;
}
}
keyframeEffect = keyframeEffect.filter(function(keyframe) {
return keyframe.offset >= 0 && keyframe.offset <= 1;
});
function spaceKeyframes() {
var length = keyframeEffect.length;
if (keyframeEffect[length - 1].offset == null)
keyframeEffect[length - 1].offset = 1;
if (length > 1 && keyframeEffect[0].offset == null)
keyframeEffect[0].offset = 0;
var previousIndex = 0;
var previousOffset = keyframeEffect[0].offset;
for (var i = 1; i < length; i++) {
var offset = keyframeEffect[i].offset;
if (offset != null) {
for (var j = 1; j < i - previousIndex; j++)
keyframeEffect[previousIndex + j].offset = previousOffset + (offset - previousOffset) * j / (i - previousIndex);
previousIndex = i;
previousOffset = offset;
}
}
}
if (!everyFrameHasOffset)
spaceKeyframes();
return keyframeEffect;
}
shared.normalizeKeyframes = normalizeKeyframes;
if (WEB_ANIMATIONS_TESTING) {
testing.normalizeKeyframes = normalizeKeyframes;
}
})(webAnimationsShared, webAnimationsTesting);

View File

@@ -0,0 +1,72 @@
// 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(scope, testing) {
function numberToString(x) {
return x.toFixed(3).replace('.000', '');
}
function clamp(min, max, x) {
return Math.min(max, Math.max(min, x));
}
function parseNumber(string) {
if (/^\s*[-+]?(\d*\.)?\d+\s*$/.test(string))
return Number(string);
}
function mergeNumbers(left, right) {
return [left, right, numberToString];
}
// FIXME: This should probably go in it's own handler.
function mergeFlex(left, right) {
if (left == 0)
return;
return clampedMergeNumbers(0, Infinity)(left, right);
}
function mergePositiveIntegers(left, right) {
return [left, right, function(x) {
return Math.round(clamp(1, Infinity, x));
}];
}
function clampedMergeNumbers(min, max) {
return function(left, right) {
return [left, right, function(x) {
return numberToString(clamp(min, max, x));
}];
};
}
function round(left, right) {
return [left, right, Math.round];
}
scope.clamp = clamp;
scope.addPropertiesHandler(parseNumber, clampedMergeNumbers(0, Infinity), ['border-image-width', 'line-height']);
scope.addPropertiesHandler(parseNumber, clampedMergeNumbers(0, 1), ['opacity', 'shape-image-threshold']);
scope.addPropertiesHandler(parseNumber, clampedMergeNumbers(0.01, Infinity), ['zoom']);
scope.addPropertiesHandler(parseNumber, mergeFlex, ['flex-grow', 'flex-shrink']);
scope.addPropertiesHandler(parseNumber, mergeNumbers, ['zoom']);
scope.addPropertiesHandler(parseNumber, mergePositiveIntegers, ['orphans', 'widows']);
scope.addPropertiesHandler(parseNumber, round, ['z-index']);
scope.parseNumber = parseNumber;
scope.mergeNumbers = mergeNumbers;
scope.numberToString = numberToString;
})(webAnimationsMinifill, webAnimationsTesting);

View File

@@ -0,0 +1,193 @@
// 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(scope, testing) {
var sequenceNumber = 0;
var AnimationPlayerEvent = function(target, currentTime, timelineTime) {
this.target = target;
this.currentTime = currentTime;
this.timelineTime = timelineTime;
this.type = 'finish';
this.bubbles = false;
this.cancelable = false;
this.currentTarget = target;
this.defaultPrevented = false;
this.eventPhase = Event.AT_TARGET;
this.timeStamp = Date.now();
};
scope.Player = function(source) {
this._sequenceNumber = sequenceNumber++;
this._currentTime = 0;
this._startTime = null;
this.paused = false;
this._playbackRate = 1;
this._inTimeline = true;
this._finishedFlag = false;
this.onfinish = null;
this._finishHandlers = [];
this._source = source;
this._inEffect = this._source._update(0);
this._idle = true;
this._currentTimePending = false;
};
scope.Player.prototype = {
_ensureAlive: function() {
this._inEffect = this._source._update(this.currentTime);
if (!this._inTimeline && (this._inEffect || !this._finishedFlag)) {
this._inTimeline = true;
scope.timeline._players.push(this);
}
},
_tickCurrentTime: function(newTime, ignoreLimit) {
if (newTime != this._currentTime) {
this._currentTime = newTime;
if (this.finished && !ignoreLimit)
this._currentTime = this._playbackRate > 0 ? this._totalDuration : 0;
this._ensureAlive();
}
},
get currentTime() {
if (this._idle || this._currentTimePending)
return null;
return this._currentTime;
},
set currentTime(newTime) {
newTime = +newTime;
if (isNaN(newTime))
return;
scope.restart();
if (!this.paused && this._startTime != null) {
this._startTime = this._timeline.currentTime - newTime / this._playbackRate;
}
this._currentTimePending = false;
if (this._currentTime == newTime)
return;
this._tickCurrentTime(newTime, true);
scope.invalidateEffects();
},
get startTime() {
return this._startTime;
},
set startTime(newTime) {
newTime = +newTime;
if (isNaN(newTime))
return;
if (this.paused || this._idle)
return;
this._startTime = newTime;
this._tickCurrentTime((this._timeline.currentTime - this._startTime) * this.playbackRate);
scope.invalidateEffects();
},
get playbackRate() { return this._playbackRate; },
get finished() {
return !this._idle && (this._playbackRate > 0 && this._currentTime >= this._totalDuration ||
this._playbackRate < 0 && this._currentTime <= 0);
},
get _totalDuration() { return this._source._totalDuration; },
get playState() {
if (this._idle)
return 'idle';
if ((this._startTime == null && !this.paused && this.playbackRate != 0) || this._currentTimePending)
return 'pending';
if (this.paused)
return 'paused';
if (this.finished)
return 'finished';
return 'running';
},
play: function() {
this.paused = false;
if (this.finished || this._idle) {
this._currentTime = this._playbackRate > 0 ? 0 : this._totalDuration;
this._startTime = null;
scope.invalidateEffects();
}
this._finishedFlag = false;
scope.restart();
this._idle = false;
this._ensureAlive();
},
pause: function() {
if (!this.finished && !this.paused && !this._idle) {
this._currentTimePending = true;
}
this._startTime = null;
this.paused = true;
},
finish: function() {
if (this._idle)
return;
this.currentTime = this._playbackRate > 0 ? this._totalDuration : 0;
this._startTime = this._totalDuration - this.currentTime;
this._currentTimePending = false;
},
cancel: function() {
this._inEffect = false;
this._idle = true;
this.currentTime = 0;
this._startTime = null;
},
reverse: function() {
this._playbackRate *= -1;
this._startTime = null;
this.play();
},
addEventListener: function(type, handler) {
if (typeof handler == 'function' && type == 'finish')
this._finishHandlers.push(handler);
},
removeEventListener: function(type, handler) {
if (type != 'finish')
return;
var index = this._finishHandlers.indexOf(handler);
if (index >= 0)
this._finishHandlers.splice(index, 1);
},
_fireEvents: function(baseTime) {
var finished = this.finished;
if ((finished || this._idle) && !this._finishedFlag) {
var event = new AnimationPlayerEvent(this, this._currentTime, baseTime);
var handlers = this._finishHandlers.concat(this.onfinish ? [this.onfinish] : []);
setTimeout(function() {
handlers.forEach(function(handler) {
handler.call(event.target, event);
});
}, 0);
}
this._finishedFlag = finished;
},
_tick: function(timelineTime) {
if (!this._idle && !this.paused) {
if (this._startTime == null)
this.startTime = timelineTime - this._currentTime / this.playbackRate;
else if (!this.finished)
this._tickCurrentTime((timelineTime - this._startTime) * this.playbackRate);
}
this._currentTimePending = false;
this._fireEvents(timelineTime);
return !this._idle && (this._inEffect || !this._finishedFlag);
},
};
if (WEB_ANIMATIONS_TESTING) {
testing.Player = scope.Player;
}
})(webAnimationsMinifill, webAnimationsTesting);

View File

@@ -0,0 +1,117 @@
// 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(scope) {
function negateDimension(dimension) {
var result = {};
for (var k in dimension) {
result[k] = -dimension[k];
}
return result;
}
function consumeOffset(string) {
return scope.consumeToken(/^(left|center|right|top|bottom)\b/i, string) || scope.consumeLengthOrPercent(string);
}
var offsetMap = {
left: {'%': 0},
center: {'%': 50},
right: {'%': 100},
top: {'%': 0},
bottom: {'%': 100},
};
function parseOrigin(slots, string) {
var result = scope.consumeRepeated(consumeOffset, /^/, string);
if (!result || result[1] != '') return;
var tokens = result[0];
tokens[0] = tokens[0] || 'center';
tokens[1] = tokens[1] || 'center';
if (slots == 3) {
tokens[2] = tokens[2] || {px: 0};
}
if (tokens.length != slots) {
return;
}
// Reorder so that the horizontal axis comes first.
if (/top|bottom/.test(tokens[0]) || /left|right/.test(tokens[1])) {
var tmp = tokens[0];
tokens[0] = tokens[1];
tokens[1] = tmp;
}
// Invalid if not horizontal then vertical.
if (!/left|right|center|Object/.test(tokens[0]))
return;
if (!/top|bottom|center|Object/.test(tokens[1]))
return;
return tokens.map(function(position) {
return typeof position == 'object' ? position : offsetMap[position];
});
}
var mergeOffsetList = scope.mergeNestedRepeated.bind(null, scope.mergeDimensions, ' ');
scope.addPropertiesHandler(parseOrigin.bind(null, 3), mergeOffsetList, ['transform-origin']);
scope.addPropertiesHandler(parseOrigin.bind(null, 2), mergeOffsetList, ['perspective-origin']);
function consumePosition(string) {
var result = scope.consumeRepeated(consumeOffset, /^/, string);
if (!result) {
return;
}
var tokens = result[0];
var out = [{'%': 50}, {'%': 50}];
var pos = 0;
var bottomOrRight = false;
for (var i = 0; i < tokens.length; i++) {
var token = tokens[i];
if (typeof token == 'string') {
bottomOrRight = /bottom|right/.test(token);
pos = {left: 0, right: 0, center: pos, top: 1, bottom: 1}[token];
out[pos] = offsetMap[token];
if (token == 'center') {
// Center doesn't accept a length offset.
pos++;
}
} else {
if (bottomOrRight) {
// If bottom or right we need to subtract the length from 100%
token = negateDimension(token);
token['%'] = (token['%'] || 0) + 100;
}
out[pos] = token;
pos++;
bottomOrRight = false;
}
}
return [out, result[1]];
}
function parsePositionList(string) {
var result = scope.consumeRepeated(consumePosition, /^,/, string);
if (result && result[1] == '') {
return result[0];
}
}
scope.consumePosition = consumePosition;
scope.mergeOffsetList = mergeOffsetList;
var mergePositionList = scope.mergeNestedRepeated.bind(null, mergeOffsetList, ', ');
scope.addPropertiesHandler(parsePositionList, mergePositionList, ['background-position', 'object-position']);
})(webAnimationsMinifill);

View File

@@ -0,0 +1,62 @@
// 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(scope, testing) {
var propertyHandlers = {};
function addPropertyHandler(parser, merger, property) {
propertyHandlers[property] = propertyHandlers[property] || [];
propertyHandlers[property].push([parser, merger]);
}
function addPropertiesHandler(parser, merger, properties) {
for (var i = 0; i < properties.length; i++) {
var property = properties[i];
WEB_ANIMATIONS_TESTING && console.assert(property.toLowerCase() === property);
addPropertyHandler(parser, merger, property);
if (/-/.test(property)) {
// Add camel cased variant.
addPropertyHandler(parser, merger, property.replace(/-(.)/g, function(_, c) {
return c.toUpperCase();
}));
}
}
}
scope.addPropertiesHandler = addPropertiesHandler;
function propertyInterpolation(property, left, right) {
var handlers = left == right ? [] : propertyHandlers[property];
for (var i = 0; handlers && i < handlers.length; i++) {
var parsedLeft = handlers[i][0](left);
var parsedRight = handlers[i][0](right);
if (parsedLeft !== undefined && parsedRight !== undefined) {
var interpolationArgs = handlers[i][1](parsedLeft, parsedRight);
if (interpolationArgs) {
var interp = scope.Interpolation.apply(null, interpolationArgs);
return function(t) {
if (t == 0) return left;
if (t == 1) return right;
return interp(t);
};
}
}
}
return scope.Interpolation(false, true, function(bool) {
return bool ? right : left;
});
}
scope.propertyInterpolation = propertyInterpolation;
})(webAnimationsMinifill, webAnimationsTesting);

View File

@@ -0,0 +1,35 @@
// 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(scope, testing) {
var aliased = {};
function alias(name, aliases) {
aliases.concat([name]).forEach(function(candidate) {
if (candidate in document.documentElement.style) {
aliased[name] = candidate;
}
});
}
alias('transform', ['webkitTransform', 'msTransform']);
alias('transformOrigin', ['webkitTransformOrigin']);
alias('perspective', ['webkitPerspective']);
alias('perspectiveOrigin', ['webkitPerspectiveOrigin']);
scope.propertyName = function(property) {
return aliased[property] || property;
};
})(webAnimationsMinifill, webAnimationsTesting);

View File

@@ -0,0 +1,20 @@
// 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.
var webAnimationsShared = {};
var webAnimationsMinifill = {};
var webAnimationsMaxifill = {};
if (!WEB_ANIMATIONS_TESTING)
var webAnimationsTesting = null;

View File

@@ -0,0 +1,108 @@
// 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(scope) {
function consumeShadow(string) {
var shadow = {
inset: false,
lengths: [],
color: null,
};
function consumePart(string) {
var result = scope.consumeToken(/^inset/i, string);
if (result) {
shadow.inset = true;
return result;
}
var result = scope.consumeLengthOrPercent(string);
if (result) {
shadow.lengths.push(result[0]);
return result;
}
var result = scope.consumeColor(string);
if (result) {
shadow.color = result[0];
return result;
}
}
var result = scope.consumeRepeated(consumePart, /^/, string);
if (result && result[0].length) {
return [shadow, result[1]];
}
}
function parseShadowList(string) {
var result = scope.consumeRepeated(consumeShadow, /^,/, string);
if (result && result[1] == '') {
return result[0];
}
}
function mergeShadow(left, right) {
while (left.lengths.length < Math.max(left.lengths.length, right.lengths.length))
left.lengths.push({px: 0});
while (right.lengths.length < Math.max(left.lengths.length, right.lengths.length))
right.lengths.push({px: 0});
if (left.inset != right.inset || !!left.color != !!right.color) {
return;
}
var lengthReconstitution = [];
var colorReconstitution;
var matchingLeft = [[], 0];
var matchingRight = [[], 0];
for (var i = 0; i < left.lengths.length; i++) {
var mergedDimensions = scope.mergeDimensions(left.lengths[i], right.lengths[i], i == 2);
matchingLeft[0].push(mergedDimensions[0]);
matchingRight[0].push(mergedDimensions[1]);
lengthReconstitution.push(mergedDimensions[2]);
}
if (left.color && right.color) {
var mergedColor = scope.mergeColors(left.color, right.color);
matchingLeft[1] = mergedColor[0];
matchingRight[1] = mergedColor[1];
colorReconstitution = mergedColor[2];
}
return [matchingLeft, matchingRight, function(value) {
var result = left.inset ? 'inset ' : ' ';
for (var i = 0; i < lengthReconstitution.length; i++) {
result += lengthReconstitution[i](value[0][i]) + ' ';
}
if (colorReconstitution) {
result += colorReconstitution(value[1]);
}
return result;
}];
}
function mergeNestedRepeatedShadow(nestedMerge, separator, left, right) {
var leftCopy = [];
var rightCopy = [];
function defaultShadow(inset) {
return {inset: inset, color: [0, 0, 0, 0], lengths: [{px: 0}, {px: 0}, {px: 0}, {px: 0}]};
}
for (var i = 0; i < left.length || i < right.length; i++) {
var l = left[i] || defaultShadow(right[i].inset);
var r = right[i] || defaultShadow(left[i].inset);
leftCopy.push(l);
rightCopy.push(r);
}
return scope.mergeNestedRepeated(nestedMerge, separator, leftCopy, rightCopy);
}
var mergeShadowList = mergeNestedRepeatedShadow.bind(null, mergeShadow, ', ');
scope.addPropertiesHandler(parseShadowList, mergeShadowList, ['box-shadow', 'text-shadow']);
})(webAnimationsMinifill);

View File

@@ -0,0 +1,85 @@
// 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(scope) {
var consumeLengthOrPercent = scope.consumeParenthesised.bind(null, scope.parseLengthOrPercent);
var consumeLengthOrPercentPair = scope.consumeRepeated.bind(undefined, consumeLengthOrPercent, /^/);
var mergeSizePair = scope.mergeNestedRepeated.bind(undefined, scope.mergeDimensions, ' ');
var mergeSizePairList = scope.mergeNestedRepeated.bind(undefined, mergeSizePair, ',');
function parseShape(input) {
var circle = scope.consumeToken(/^circle/, input);
if (circle && circle[0]) {
return ['circle'].concat(scope.consumeList([
scope.ignore(scope.consumeToken.bind(undefined, /^\(/)),
consumeLengthOrPercent,
scope.ignore(scope.consumeToken.bind(undefined, /^at/)),
scope.consumePosition,
scope.ignore(scope.consumeToken.bind(undefined, /^\)/))
], circle[1]));
}
var ellipse = scope.consumeToken(/^ellipse/, input);
if (ellipse && ellipse[0]) {
return ['ellipse'].concat(scope.consumeList([
scope.ignore(scope.consumeToken.bind(undefined, /^\(/)),
consumeLengthOrPercentPair,
scope.ignore(scope.consumeToken.bind(undefined, /^at/)),
scope.consumePosition,
scope.ignore(scope.consumeToken.bind(undefined, /^\)/))
], ellipse[1]));
}
var polygon = scope.consumeToken(/^polygon/, input);
if (polygon && polygon[0]) {
return ['polygon'].concat(scope.consumeList([
scope.ignore(scope.consumeToken.bind(undefined, /^\(/)),
scope.optional(scope.consumeToken.bind(undefined, /^nonzero\s*,|^evenodd\s*,/), 'nonzero,'),
scope.consumeSizePairList,
scope.ignore(scope.consumeToken.bind(undefined, /^\)/))
], polygon[1]));
}
}
function mergeShapes(left, right) {
if (left[0] !== right[0])
return;
if (left[0] == 'circle') {
return scope.mergeList(left.slice(1), right.slice(1), [
'circle(',
scope.mergeDimensions,
' at ',
scope.mergeOffsetList,
')']);
}
if (left[0] == 'ellipse') {
return scope.mergeList(left.slice(1), right.slice(1), [
'ellipse(',
scope.mergeNonNegativeSizePair,
' at ',
scope.mergeOffsetList,
')']);
}
if (left[0] == 'polygon' && left[1] == right[1]) {
return scope.mergeList(left.slice(2), right.slice(2), [
'polygon(',
left[1],
mergeSizePairList,
')']);
}
}
scope.addPropertiesHandler(parseShape, mergeShapes, ['shape-outside']);
})(webAnimationsMinifill);

View File

@@ -0,0 +1,147 @@
// 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, scope, testing) {
var originalRequestAnimationFrame = window.requestAnimationFrame;
var rafCallbacks = [];
var rafId = 0;
window.requestAnimationFrame = function(f) {
var id = rafId++;
if (rafCallbacks.length == 0 && !WEB_ANIMATIONS_TESTING) {
originalRequestAnimationFrame(processRafCallbacks);
}
rafCallbacks.push([id, f]);
return id;
};
window.cancelAnimationFrame = function(id) {
rafCallbacks.forEach(function(entry) {
if (entry[0] == id) {
entry[1] = function() {};
}
});
};
function processRafCallbacks(t) {
var processing = rafCallbacks;
rafCallbacks = [];
tick(t);
processing.forEach(function(entry) { entry[1](t); });
if (needsRetick)
tick(t);
applyPendingEffects();
}
function comparePlayers(leftPlayer, rightPlayer) {
return leftPlayer._sequenceNumber - rightPlayer._sequenceNumber;
}
function InternalTimeline() {
this._players = [];
this.currentTime = window.performance ? performance.now() : 0;
};
InternalTimeline.prototype = {
_play: function(source) {
source._timing = shared.normalizeTimingInput(source.timing);
var player = new scope.Player(source);
player._idle = false;
player._timeline = this;
this._players.push(player);
scope.restart();
scope.invalidateEffects();
return player;
}
};
var ticking = false;
var hasRestartedThisFrame = false;
scope.restart = function() {
if (!ticking) {
ticking = true;
requestAnimationFrame(function() {});
hasRestartedThisFrame = true;
}
return hasRestartedThisFrame;
};
var needsRetick = false;
scope.invalidateEffects = function() {
needsRetick = true;
};
var pendingEffects = [];
function applyPendingEffects() {
pendingEffects.forEach(function(f) { f(); });
}
var originalGetComputedStyle = window.getComputedStyle;
Object.defineProperty(window, 'getComputedStyle', {
configurable: true,
enumerable: true,
value: function() {
if (needsRetick) tick(timeline.currentTime);
applyPendingEffects();
return originalGetComputedStyle.apply(this, arguments);
},
});
function tick(t) {
hasRestartedThisFrame = false;
var timeline = scope.timeline;
timeline.currentTime = t;
timeline._players.sort(comparePlayers);
ticking = false;
var updatingPlayers = timeline._players;
timeline._players = [];
var newPendingClears = [];
var newPendingEffects = [];
updatingPlayers = updatingPlayers.filter(function(player) {
player._inTimeline = player._tick(t);
if (!player._inEffect)
newPendingClears.push(player._source);
else
newPendingEffects.push(player._source);
if (!player.finished && !player.paused && !player._idle)
ticking = true;
return player._inTimeline;
});
pendingEffects.length = 0;
pendingEffects.push.apply(pendingEffects, newPendingClears);
pendingEffects.push.apply(pendingEffects, newPendingEffects);
timeline._players.push.apply(timeline._players, updatingPlayers);
needsRetick = false;
if (ticking)
requestAnimationFrame(function() {});
};
if (WEB_ANIMATIONS_TESTING) {
testing.tick = processRafCallbacks;
testing.isTicking = function() { return ticking; };
testing.setTicking = function(newVal) { ticking = newVal; };
}
var timeline = new InternalTimeline();
scope.timeline = timeline;
})(webAnimationsShared, webAnimationsMinifill, webAnimationsTesting);

View File

@@ -0,0 +1,77 @@
// 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, scope, testing) {
scope.AnimationTimeline = function() {
this._players = [];
this.currentTime = undefined;
};
scope.AnimationTimeline.prototype = {
// FIXME: This needs to return the wrapped players in maxifill
// TODO: Does this need to be sorted?
// TODO: Do we need to consider needsRetick?
getAnimationPlayers: function() {
this._discardPlayers();
return this._players.slice();
},
_discardPlayers: function() {
this._players = this._players.filter(function(player) {
return player.playState != 'finished' && player.playState != 'idle';
});
},
play: function(source) {
var player = new scope.Player(source);
this._players.push(player);
scope.restartMaxifillTick();
player.play();
return player;
},
};
var ticking = false;
scope.restartMaxifillTick = function() {
if (!ticking) {
ticking = true;
requestAnimationFrame(maxifillTick);
}
};
function maxifillTick(t) {
var timeline = window.document.timeline;
timeline.currentTime = t;
timeline._discardPlayers();
if (timeline._players.length == 0)
ticking = false;
else
requestAnimationFrame(maxifillTick);
}
var timeline = new scope.AnimationTimeline();
scope.timeline = timeline;
try {
Object.defineProperty(window.document, 'timeline', {
configurable: true,
get: function() { return timeline; }
});
} catch (e) { }
try {
window.document.timeline = timeline;
} catch (e) { }
})(webAnimationsShared, webAnimationsMaxifill, webAnimationsTesting);

View File

@@ -0,0 +1,239 @@
// 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 fills = 'backwards|forwards|both'.split('|');
var directions = 'reverse|alternate|alternate-reverse'.split('|');
function makeTiming(timingInput, forGroup) {
var timing = {
delay: 0,
endDelay: 0,
fill: forGroup ? 'both' : 'none',
iterationStart: 0,
iterations: 1,
duration: forGroup ? 'auto' : 0,
playbackRate: 1,
direction: 'normal',
easing: 'linear',
};
if (typeof timingInput == 'number' && !isNaN(timingInput)) {
timing.duration = timingInput;
} else if (timingInput !== undefined) {
Object.getOwnPropertyNames(timingInput).forEach(function(property) {
if (timingInput[property] != 'auto') {
if (typeof timing[property] == 'number' || property == 'duration') {
if (typeof timingInput[property] != 'number' || isNaN(timingInput[property])) {
return;
}
}
if ((property == 'fill') && (fills.indexOf(timingInput[property]) == -1)) {
return;
}
if ((property == 'direction') && (directions.indexOf(timingInput[property]) == -1)) {
return;
}
timing[property] = timingInput[property];
}
});
}
return timing;
}
function normalizeTimingInput(timingInput, forGroup) {
var timing = makeTiming(timingInput, forGroup);
timing.easing = toTimingFunction(timing.easing);
return timing;
}
function cubic(a, b, c, d) {
if (a < 0 || a > 1 || c < 0 || c > 1) {
return linear;
}
return function(x) {
var start = 0, end = 1;
while (1) {
var mid = (start + end) / 2;
function f(a, b, m) { return 3 * a * (1 - m) * (1 - m) * m + 3 * b * (1 - m) * m * m + m * m * m};
var xEst = f(a, c, mid);
if (Math.abs(x - xEst) < 0.001) {
return f(b, d, mid);
}
if (xEst < x) {
start = mid;
} else {
end = mid;
}
}
}
}
var Start = 1;
var Middle = 0.5;
var End = 0;
function step(count, pos) {
return function(x) {
if (x >= 1) {
return 1;
}
var stepSize = 1 / count;
x += pos * stepSize;
return x - x % stepSize;
}
}
var presets = {
'ease': cubic(0.25, 0.1, 0.25, 1),
'ease-in': cubic(0.42, 0, 1, 1),
'ease-out': cubic(0, 0, 0.58, 1),
'ease-in-out': cubic(0.42, 0, 0.58, 1),
'step-start': step(1, Start),
'step-middle': step(1, Middle),
'step-end': step(1, End)
};
var numberString = '\\s*(-?\\d+\\.?\\d*|-?\\.\\d+)\\s*';
var cubicBezierRe = new RegExp('cubic-bezier\\(' + numberString + ',' + numberString + ',' + numberString + ',' + numberString + '\\)');
var stepRe = /steps\(\s*(\d+)\s*,\s*(start|middle|end)\s*\)/;
var linear = function(x) { return x; };
function toTimingFunction(easing) {
var cubicData = cubicBezierRe.exec(easing);
if (cubicData) {
return cubic.apply(this, cubicData.slice(1).map(Number));
}
var stepData = stepRe.exec(easing);
if (stepData) {
return step(Number(stepData[1]), {'start': Start, 'middle': Middle, 'end': End}[stepData[2]]);
}
var preset = presets[easing];
if (preset) {
return preset;
}
return linear;
};
function calculateActiveDuration(timing) {
return Math.abs(repeatedDuration(timing) / timing.playbackRate);
}
function repeatedDuration(timing) {
return timing.duration * timing.iterations;
}
var PhaseNone = 0;
var PhaseBefore = 1;
var PhaseAfter = 2;
var PhaseActive = 3;
function calculatePhase(activeDuration, localTime, timing) {
if (localTime == null) {
return PhaseNone;
}
if (localTime < timing.delay) {
return PhaseBefore;
}
if (localTime >= timing.delay + activeDuration) {
return PhaseAfter;
}
return PhaseActive;
}
function calculateActiveTime(activeDuration, fillMode, localTime, phase, delay) {
switch (phase) {
case PhaseBefore:
if (fillMode == 'backwards' || fillMode == 'both')
return 0;
return null;
case PhaseActive:
return localTime - delay;
case PhaseAfter:
if (fillMode == 'forwards' || fillMode == 'both')
return activeDuration;
return null;
case PhaseNone:
return null;
}
}
function calculateScaledActiveTime(activeDuration, activeTime, startOffset, timing) {
return (timing.playbackRate < 0 ? activeTime - activeDuration : activeTime) * timing.playbackRate + startOffset;
}
function calculateIterationTime(iterationDuration, repeatedDuration, scaledActiveTime, startOffset, timing) {
if (scaledActiveTime === Infinity || scaledActiveTime === -Infinity || (scaledActiveTime - startOffset == repeatedDuration && timing.iterations && ((timing.iterations + timing.iterationStart) % 1 == 0))) {
return iterationDuration;
}
return scaledActiveTime % iterationDuration;
}
function calculateCurrentIteration(iterationDuration, iterationTime, scaledActiveTime, timing) {
if (scaledActiveTime === 0) {
return 0;
}
if (iterationTime == iterationDuration) {
return timing.iterationStart + timing.iterations - 1;
}
return Math.floor(scaledActiveTime / iterationDuration);
}
function calculateTransformedTime(currentIteration, iterationDuration, iterationTime, timing) {
var currentIterationIsOdd = currentIteration % 2 >= 1;
var currentDirectionIsForwards = timing.direction == 'normal' || timing.direction == (currentIterationIsOdd ? 'alternate-reverse' : 'alternate');
var directedTime = currentDirectionIsForwards ? iterationTime : iterationDuration - iterationTime;
var timeFraction = directedTime / iterationDuration;
return iterationDuration * timing.easing(timeFraction);
}
function calculateTimeFraction(activeDuration, localTime, timing) {
var phase = calculatePhase(activeDuration, localTime, timing);
var activeTime = calculateActiveTime(activeDuration, timing.fill, localTime, phase, timing.delay);
if (activeTime === null)
return null;
if (activeDuration === 0)
return phase === PhaseBefore ? 0 : 1;
var startOffset = timing.iterationStart * timing.duration;
var scaledActiveTime = calculateScaledActiveTime(activeDuration, activeTime, startOffset, timing);
var iterationTime = calculateIterationTime(timing.duration, repeatedDuration(timing), scaledActiveTime, startOffset, timing);
var currentIteration = calculateCurrentIteration(timing.duration, iterationTime, scaledActiveTime, timing);
return calculateTransformedTime(currentIteration, timing.duration, iterationTime, timing) / timing.duration;
}
shared.makeTiming = makeTiming;
shared.normalizeTimingInput = normalizeTimingInput;
shared.calculateActiveDuration = calculateActiveDuration;
shared.calculateTimeFraction = calculateTimeFraction;
shared.calculatePhase = calculatePhase;
shared.toTimingFunction = toTimingFunction;
if (WEB_ANIMATIONS_TESTING) {
testing.normalizeTimingInput = normalizeTimingInput;
testing.toTimingFunction = toTimingFunction;
testing.calculateActiveDuration = calculateActiveDuration;
testing.calculatePhase = calculatePhase;
testing.PhaseNone = PhaseNone;
testing.PhaseBefore = PhaseBefore;
testing.PhaseActive = PhaseActive;
testing.PhaseAfter = PhaseAfter;
testing.calculateActiveTime = calculateActiveTime;
testing.calculateScaledActiveTime = calculateScaledActiveTime;
testing.calculateIterationTime = calculateIterationTime;
testing.calculateCurrentIteration = calculateCurrentIteration;
testing.calculateTransformedTime = calculateTransformedTime;
}
})(webAnimationsShared, webAnimationsTesting);

View File

@@ -0,0 +1,262 @@
// 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(scope, testing) {
// This returns a function for converting transform functions to equivalent
// primitive functions, which will take an array of values from the
// derivative type and fill in the blanks (underscores) with them.
var _ = null;
function cast(pattern) {
return function(contents) {
var i = 0;
return pattern.map(function(x) { return x === _ ? contents[i++] : x; });
}
}
function id(x) { return x; }
var Opx = {px: 0};
var Odeg = {deg: 0};
// type: [argTypes, convertTo3D, convertTo2D]
// In the argument types string, lowercase characters represent optional arguments
var transformFunctions = {
matrix: ['NNNNNN', [_, _, 0, 0, _, _, 0, 0, 0, 0, 1, 0, _, _, 0, 1], id],
matrix3d: ['NNNNNNNNNNNNNNNN', id],
rotate: ['A'],
rotatex: ['A'],
rotatey: ['A'],
rotatez: ['A'],
rotate3d: ['NNNA'],
perspective: ['L'],
scale: ['Nn', cast([_, _, 1]), id],
scalex: ['N', cast([_, 1, 1]), cast([_, 1])],
scaley: ['N', cast([1, _, 1]), cast([1, _])],
scalez: ['N', cast([1, 1, _])],
scale3d: ['NNN', id],
skew: ['Aa', null, id],
skewx: ['A', null, cast([_, Odeg])],
skewy: ['A', null, cast([Odeg, _])],
translate: ['Tt', cast([_, _, Opx]), id],
translatex: ['T', cast([_, Opx, Opx]), cast([_, Opx])],
translatey: ['T', cast([Opx, _, Opx]), cast([Opx, _])],
translatez: ['L', cast([Opx, Opx, _])],
translate3d: ['TTL', id],
};
function parseTransform(string) {
string = string.toLowerCase().trim();
if (string == 'none')
return [];
// FIXME: Using a RegExp means calcs won't work here
var transformRegExp = /\s*(\w+)\(([^)]*)\)/g;
var result = [];
var match;
var prevLastIndex = 0;
while (match = transformRegExp.exec(string)) {
if (match.index != prevLastIndex)
return;
prevLastIndex = match.index + match[0].length;
var functionName = match[1];
var functionData = transformFunctions[functionName];
if (!functionData)
return;
var args = match[2].split(',');
var argTypes = functionData[0];
if (argTypes.length < args.length)
return;
var parsedArgs = [];
for (var i = 0; i < argTypes.length; i++) {
var arg = args[i];
var type = argTypes[i];
var parsedArg;
if (!arg)
parsedArg = ({a: Odeg,
n: parsedArgs[0],
t: Opx})[type];
else
parsedArg = ({A: function(s) { return s.trim() == '0' ? Odeg : scope.parseAngle(s); },
N: scope.parseNumber,
T: scope.parseLengthOrPercent,
L: scope.parseLength})[type.toUpperCase()](arg);
if (parsedArg === undefined)
return;
parsedArgs.push(parsedArg);
}
result.push({t: functionName, d: parsedArgs});
if (transformRegExp.lastIndex == string.length)
return result;
}
};
function numberToLongString(x) {
return x.toFixed(6).replace('.000000', '');
}
function mergeMatrices(left, right) {
if (left.decompositionPair !== right) {
left.decompositionPair = right;
var leftArgs = scope.makeMatrixDecomposition(left);
}
if (right.decompositionPair !== left) {
right.decompositionPair = left;
var rightArgs = scope.makeMatrixDecomposition(right);
}
if (leftArgs[0] == null || rightArgs[0] == null)
return [[false], [true], function(x) { return x ? right[0].d : left[0].d; }];
leftArgs[0].push(0);
rightArgs[0].push(1);
return [
leftArgs,
rightArgs,
function(list) {
var quat = scope.quat(leftArgs[0][3], rightArgs[0][3], list[5]);
var mat = scope.composeMatrix(list[0], list[1], list[2], quat, list[4]);
var stringifiedArgs = mat.map(numberToLongString).join(',');
return stringifiedArgs;
}
];
}
function typeTo2D(type) {
return type.replace(/[xy]/, '');
}
function typeTo3D(type) {
return type.replace(/(x|y|z|3d)?$/, '3d');
}
function mergeTransforms(left, right) {
var matrixModulesLoaded = scope.makeMatrixDecomposition && true;
var flipResults = false;
if (!left.length || !right.length) {
if (!left.length) {
flipResults = true;
left = right;
right = [];
}
for (var i = 0; i < left.length; i++) {
var type = left[i].t;
var args = left[i].d;
var defaultValue = type.substr(0, 5) == 'scale' ? 1 : 0;
right.push({t: type, d: args.map(function(arg) {
if (typeof arg == 'number')
return defaultValue;
var result = {};
for (var unit in arg)
result[unit] = defaultValue;
return result;
})});
}
}
var isMatrixOrPerspective = function(lt, rt) {
return ((lt == 'perspective') && (rt == 'perspective')) ||
((lt == 'matrix' || lt == 'matrix3d') && (rt == 'matrix' || rt == 'matrix3d'));
};
var leftResult = [];
var rightResult = [];
var types = [];
if (left.length != right.length) {
if (!matrixModulesLoaded)
return;
var merged = mergeMatrices(left, right);
leftResult = [merged[0]];
rightResult = [merged[1]];
types = [['matrix', [merged[2]]]];
} else {
for (var i = 0; i < left.length; i++) {
var leftType = left[i].t;
var rightType = right[i].t;
var leftArgs = left[i].d;
var rightArgs = right[i].d;
var leftFunctionData = transformFunctions[leftType];
var rightFunctionData = transformFunctions[rightType];
var type;
if (isMatrixOrPerspective(leftType, rightType)) {
if (!matrixModulesLoaded)
return;
var merged = mergeMatrices([left[i]], [right[i]]);
leftResult.push(merged[0]);
rightResult.push(merged[1]);
types.push(['matrix', [merged[2]]]);
continue;
} else if (leftType == rightType) {
type = leftType;
} else if (leftFunctionData[2] && rightFunctionData[2] && typeTo2D(leftType) == typeTo2D(rightType)) {
type = typeTo2D(leftType);
leftArgs = leftFunctionData[2](leftArgs);
rightArgs = rightFunctionData[2](rightArgs);
} else if (leftFunctionData[1] && rightFunctionData[1] && typeTo3D(leftType) == typeTo3D(rightType)) {
type = typeTo3D(leftType);
leftArgs = leftFunctionData[1](leftArgs);
rightArgs = rightFunctionData[1](rightArgs);
} else {
if (!matrixModulesLoaded)
return;
var merged = mergeMatrices(left, right);
leftResult = [merged[0]];
rightResult = [merged[1]];
types = [['matrix', [merged[2]]]];
break;
}
var leftArgsCopy = [];
var rightArgsCopy = [];
var stringConversions = [];
for (var j = 0; j < leftArgs.length; j++) {
var merge = typeof leftArgs[j] == 'number' ? scope.mergeNumbers : scope.mergeDimensions;
var merged = merge(leftArgs[j], rightArgs[j]);
leftArgsCopy[j] = merged[0];
rightArgsCopy[j] = merged[1];
stringConversions.push(merged[2]);
}
leftResult.push(leftArgsCopy);
rightResult.push(rightArgsCopy);
types.push([type, stringConversions]);
}
}
if (flipResults) {
var tmp = leftResult;
leftResult = rightResult;
rightResult = tmp;
}
return [leftResult, rightResult, function(list) {
return list.map(function(args, i) {
var stringifiedArgs = args.map(function(arg, j) {
return types[i][1][j](arg);
}).join(',');
if (types[i][0] == 'matrix' && stringifiedArgs.split(',').length == 16)
types[i][0] = 'matrix3d';
return types[i][0] + '(' + stringifiedArgs + ')';
}).join(' ');
}];
}
scope.addPropertiesHandler(parseTransform, mergeTransforms, ['transform']);
if (WEB_ANIMATIONS_TESTING)
testing.parseTransform = parseTransform;
})(webAnimationsMinifill, webAnimationsTesting);

View File

@@ -0,0 +1,29 @@
// 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(scope, testing) {
function merge(left, right) {
if (left != 'visible' && right != 'visible') return;
return [0, 1, function(x) {
if (x <= 0) return left;
if (x >= 1) return right;
return 'visible';
}];
}
scope.addPropertiesHandler(String, merge, ['visibility']);
})(webAnimationsMinifill);