1// Copyright 2014 Google Inc. All rights reserved. 2// 3// Licensed under the Apache License, Version 2.0 (the "License"); 4// you may not use this file except in compliance with the License. 5// You may obtain a copy of the License at 6// 7// http://www.apache.org/licenses/LICENSE-2.0 8// 9// Unless required by applicable law or agreed to in writing, software 10// distributed under the License is distributed on an "AS IS" BASIS, 11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12// See the License for the specific language governing permissions and 13// limitations under the License. 14 15(function(scope, testing) { 16 17 var sequenceNumber = 0; 18 19 var AnimationPlayerEvent = function(target, currentTime, timelineTime) { 20 this.target = target; 21 this.currentTime = currentTime; 22 this.timelineTime = timelineTime; 23 24 this.type = 'finish'; 25 this.bubbles = false; 26 this.cancelable = false; 27 this.currentTarget = target; 28 this.defaultPrevented = false; 29 this.eventPhase = Event.AT_TARGET; 30 this.timeStamp = Date.now(); 31 }; 32 33 scope.Player = function(source) { 34 this._sequenceNumber = sequenceNumber++; 35 this._currentTime = 0; 36 this._startTime = null; 37 this.paused = false; 38 this._playbackRate = 1; 39 this._inTimeline = true; 40 this._finishedFlag = false; 41 this.onfinish = null; 42 this._finishHandlers = []; 43 this._source = source; 44 this._inEffect = this._source._update(0); 45 this._idle = true; 46 this._currentTimePending = false; 47 }; 48 49 scope.Player.prototype = { 50 _ensureAlive: function() { 51 this._inEffect = this._source._update(this.currentTime); 52 if (!this._inTimeline && (this._inEffect || !this._finishedFlag)) { 53 this._inTimeline = true; 54 scope.timeline._players.push(this); 55 } 56 }, 57 _tickCurrentTime: function(newTime, ignoreLimit) { 58 if (newTime != this._currentTime) { 59 this._currentTime = newTime; 60 if (this.finished && !ignoreLimit) 61 this._currentTime = this._playbackRate > 0 ? this._totalDuration : 0; 62 this._ensureAlive(); 63 } 64 }, 65 get currentTime() { 66 if (this._idle || this._currentTimePending) 67 return null; 68 return this._currentTime; 69 }, 70 set currentTime(newTime) { 71 newTime = +newTime; 72 if (isNaN(newTime)) 73 return; 74 scope.restart(); 75 if (!this.paused && this._startTime != null) { 76 this._startTime = this._timeline.currentTime - newTime / this._playbackRate; 77 } 78 this._currentTimePending = false; 79 if (this._currentTime == newTime) 80 return; 81 this._tickCurrentTime(newTime, true); 82 scope.invalidateEffects(); 83 }, 84 get startTime() { 85 return this._startTime; 86 }, 87 set startTime(newTime) { 88 newTime = +newTime; 89 if (isNaN(newTime)) 90 return; 91 if (this.paused || this._idle) 92 return; 93 this._startTime = newTime; 94 this._tickCurrentTime((this._timeline.currentTime - this._startTime) * this.playbackRate); 95 scope.invalidateEffects(); 96 }, 97 get playbackRate() { 98 return this._playbackRate; 99 }, 100 set playbackRate(value) { 101 var oldCurrentTime = this.currentTime; 102 this._playbackRate = value; 103 if (oldCurrentTime != null) { 104 this.currentTime = oldCurrentTime; 105 } 106 }, 107 get finished() { 108 return !this._idle && (this._playbackRate > 0 && this._currentTime >= this._totalDuration || 109 this._playbackRate < 0 && this._currentTime <= 0); 110 }, 111 get _totalDuration() { return this._source._totalDuration; }, 112 get playState() { 113 if (this._idle) 114 return 'idle'; 115 if ((this._startTime == null && !this.paused && this.playbackRate != 0) || this._currentTimePending) 116 return 'pending'; 117 if (this.paused) 118 return 'paused'; 119 if (this.finished) 120 return 'finished'; 121 return 'running'; 122 }, 123 play: function() { 124 this.paused = false; 125 if (this.finished || this._idle) { 126 this._currentTime = this._playbackRate > 0 ? 0 : this._totalDuration; 127 this._startTime = null; 128 scope.invalidateEffects(); 129 } 130 this._finishedFlag = false; 131 scope.restart(); 132 this._idle = false; 133 this._ensureAlive(); 134 }, 135 pause: function() { 136 if (!this.finished && !this.paused && !this._idle) { 137 this._currentTimePending = true; 138 } 139 this._startTime = null; 140 this.paused = true; 141 }, 142 finish: function() { 143 if (this._idle) 144 return; 145 this.currentTime = this._playbackRate > 0 ? this._totalDuration : 0; 146 this._startTime = this._totalDuration - this.currentTime; 147 this._currentTimePending = false; 148 }, 149 cancel: function() { 150 this._inEffect = false; 151 this._idle = true; 152 this.currentTime = 0; 153 this._startTime = null; 154 }, 155 reverse: function() { 156 this._playbackRate *= -1; 157 this._startTime = null; 158 this.play(); 159 }, 160 addEventListener: function(type, handler) { 161 if (typeof handler == 'function' && type == 'finish') 162 this._finishHandlers.push(handler); 163 }, 164 removeEventListener: function(type, handler) { 165 if (type != 'finish') 166 return; 167 var index = this._finishHandlers.indexOf(handler); 168 if (index >= 0) 169 this._finishHandlers.splice(index, 1); 170 }, 171 _fireEvents: function(baseTime) { 172 var finished = this.finished; 173 if ((finished || this._idle) && !this._finishedFlag) { 174 var event = new AnimationPlayerEvent(this, this._currentTime, baseTime); 175 var handlers = this._finishHandlers.concat(this.onfinish ? [this.onfinish] : []); 176 setTimeout(function() { 177 handlers.forEach(function(handler) { 178 handler.call(event.target, event); 179 }); 180 }, 0); 181 } 182 this._finishedFlag = finished; 183 }, 184 _tick: function(timelineTime) { 185 if (!this._idle && !this.paused) { 186 if (this._startTime == null) 187 this.startTime = timelineTime - this._currentTime / this.playbackRate; 188 else if (!this.finished) 189 this._tickCurrentTime((timelineTime - this._startTime) * this.playbackRate); 190 } 191 192 this._currentTimePending = false; 193 this._fireEvents(timelineTime); 194 return !this._idle && (this._inEffect || !this._finishedFlag); 195 }, 196 }; 197 198 if (WEB_ANIMATIONS_TESTING) { 199 testing.Player = scope.Player; 200 } 201 202})(webAnimations1, webAnimationsTesting); 203