1/*! 2 * VERSION: 2.0.2 3 * DATE: 2018-08-27 4 * UPDATES AND DOCS AT: http://greensock.com 5 * 6 * Includes all of the following: TweenLite, TweenMax, TimelineLite, TimelineMax, EasePack, CSSPlugin, RoundPropsPlugin, BezierPlugin, AttrPlugin, DirectionalRotationPlugin 7 * 8 * @license Copyright (c) 2008-2018, GreenSock. All rights reserved. 9 * This work is subject to the terms at http://greensock.com/standard-license or for 10 * Club GreenSock members, the software agreement that was issued with your membership. 11 * 12 * @author: Jack Doyle, [email protected] 13 **/ 14var _gsScope = (typeof(module) !== "undefined" && module.exports && typeof(global) !== "undefined") ? global : this || window; //helps ensure compatibility with AMD/RequireJS and CommonJS/Node 15(_gsScope._gsQueue || (_gsScope._gsQueue = [])).push( function() { 16 17 "use strict"; 18 19 _gsScope._gsDefine("TweenMax", ["core.Animation","core.SimpleTimeline","TweenLite"], function(Animation, SimpleTimeline, TweenLite) { 20 21 var _slice = function(a) { //don't use [].slice because that doesn't work in IE8 with a NodeList that's returned by querySelectorAll() 22 var b = [], 23 l = a.length, 24 i; 25 for (i = 0; i !== l; b.push(a[i++])); 26 return b; 27 }, 28 _applyCycle = function(vars, targets, i) { 29 var alt = vars.cycle, 30 p, val; 31 for (p in alt) { 32 val = alt[p]; 33 vars[p] = (typeof(val) === "function") ? val(i, targets[i]) : val[i % val.length]; 34 } 35 delete vars.cycle; 36 }, 37 TweenMax = function(target, duration, vars) { 38 TweenLite.call(this, target, duration, vars); 39 this._cycle = 0; 40 this._yoyo = (this.vars.yoyo === true || !!this.vars.yoyoEase); 41 this._repeat = this.vars.repeat || 0; 42 this._repeatDelay = this.vars.repeatDelay || 0; 43 if (this._repeat) { 44 this._uncache(true); //ensures that if there is any repeat, the totalDuration will get recalculated to accurately report it. 45 } 46 this.render = TweenMax.prototype.render; //speed optimization (avoid prototype lookup on this "hot" method) 47 }, 48 _tinyNum = 0.0000000001, 49 TweenLiteInternals = TweenLite._internals, 50 _isSelector = TweenLiteInternals.isSelector, 51 _isArray = TweenLiteInternals.isArray, 52 p = TweenMax.prototype = TweenLite.to({}, 0.1, {}), 53 _blankArray = []; 54 55 TweenMax.version = "2.0.2"; 56 p.constructor = TweenMax; 57 p.kill()._gc = false; 58 TweenMax.killTweensOf = TweenMax.killDelayedCallsTo = TweenLite.killTweensOf; 59 TweenMax.getTweensOf = TweenLite.getTweensOf; 60 TweenMax.lagSmoothing = TweenLite.lagSmoothing; 61 TweenMax.ticker = TweenLite.ticker; 62 TweenMax.render = TweenLite.render; 63 64 p.invalidate = function() { 65 this._yoyo = (this.vars.yoyo === true || !!this.vars.yoyoEase); 66 this._repeat = this.vars.repeat || 0; 67 this._repeatDelay = this.vars.repeatDelay || 0; 68 this._yoyoEase = null; 69 this._uncache(true); 70 return TweenLite.prototype.invalidate.call(this); 71 }; 72 73 p.updateTo = function(vars, resetDuration) { 74 var curRatio = this.ratio, 75 immediate = this.vars.immediateRender || vars.immediateRender, 76 p; 77 if (resetDuration && this._startTime < this._timeline._time) { 78 this._startTime = this._timeline._time; 79 this._uncache(false); 80 if (this._gc) { 81 this._enabled(true, false); 82 } else { 83 this._timeline.insert(this, this._startTime - this._delay); //ensures that any necessary re-sequencing of Animations in the timeline occurs to make sure the rendering order is correct. 84 } 85 } 86 for (p in vars) { 87 this.vars[p] = vars[p]; 88 } 89 if (this._initted || immediate) { 90 if (resetDuration) { 91 this._initted = false; 92 if (immediate) { 93 this.render(0, true, true); 94 } 95 } else { 96 if (this._gc) { 97 this._enabled(true, false); 98 } 99 if (this._notifyPluginsOfEnabled && this._firstPT) { 100 TweenLite._onPluginEvent("_onDisable", this); //in case a plugin like MotionBlur must perform some cleanup tasks 101 } 102 if (this._time / this._duration > 0.998) { //if the tween has finished (or come extremely close to finishing), we just need to rewind it to 0 and then render it again at the end which forces it to re-initialize (parsing the new vars). We allow tweens that are close to finishing (but haven't quite finished) to work this way too because otherwise, the values are so small when determining where to project the starting values that binary math issues creep in and can make the tween appear to render incorrectly when run backwards. 103 var prevTime = this._totalTime; 104 this.render(0, true, false); 105 this._initted = false;
106 this.render(prevTime, true, false); 107 } else { 108 this._initted = false; 109 this._init(); 110 if (this._time > 0 || immediate) { 111 var inv = 1 / (1 - curRatio), 112 pt = this._firstPT, endValue; 113 while (pt) { 114 endValue = pt.s + pt.c; 115 pt.c *= inv; 116 pt.s = endValue - pt.c; 117 pt = pt._next; 118 } 119 } 120 } 121 } 122 } 123 return this; 124 }; 125 126 p.render = function(time, suppressEvents, force) { 127 if (!this._initted) if (this._duration === 0 && this.vars.repeat) { //zero duration tweens that render immediately have render() called from TweenLite's constructor, before TweenMax's constructor has finished setting _repeat, _repeatDelay, and _yoyo which are critical in determining totalDuration() so we need to call invalidate() which is a low-kb way to get those set properly. 128 this.invalidate(); 129 } 130 var totalDur = (!this._dirty) ? this._totalDuration : this.totalDuration(), 131 prevTime = this._time, 132 prevTotalTime = this._totalTime, 133 prevCycle = this._cycle, 134 duration = this._duration, 135 prevRawPrevTime = this._rawPrevTime, 136 isComplete, callback, pt, cycleDuration, r, type, pow, rawPrevTime, yoyoEase; 137 if (time >= totalDur - 0.0000001 && time >= 0) { //to work around occasional floating point math artifacts. 138 this._totalTime = totalDur; 139 this._cycle = this._repeat; 140 if (this._yoyo && (this._cycle & 1) !== 0) { 141 this._time = 0; 142 this.ratio = this._ease._calcEnd ? this._ease.getRatio(0) : 0; 143 } else { 144 this._time = duration; 145 this.ratio = this._ease._calcEnd ? this._ease.getRatio(1) : 1; 146 } 147 if (!this._reversed) { 148 isComplete = true; 149 callback = "onComplete"; 150 force = (force || this._timeline.autoRemoveChildren); //otherwise, if the animation is unpaused/activated after it's already finished, it doesn't get removed from the parent timeline. 151 } 152 if (duration === 0) if (this._initted || !this.vars.lazy || force) { //zero-duration tweens are tricky because we must discern the momentum/direction of time in order to determine whether the starting values should be rendered or the ending values. If the "playhead" of its timeline goes past the zero-duration tween in the forward direction or lands directly on it, the end values should be rendered, but if the timeline's "playhead" moves past it in the backward direction (from a postitive time to a negative time), the starting values must be rendered. 153 if (this._startTime === this._timeline._duration) { //if a zero-duration tween is at the VERY end of a timeline and that timeline renders at its end, it will typically add a tiny bit of cushion to the render time to prevent rounding errors from getting in the way of tweens rendering their VERY end. If we then reverse() that timeline, the zero-duration tween will trigger its onReverseComplete even though technically the playhead didn't pass over it again. It's a very specific edge case we must accommodate. 154 time = 0; 155 } 156 if (prevRawPrevTime < 0 || (time <= 0 && time >= -0.0000001) || (prevRawPrevTime === _tinyNum && this.data !== "isPause")) if (prevRawPrevTime !== time) { //note: when this.data is "isPause", it's a callback added by addPause() on a timeline that we should not be triggered when LEAVING its exact start time. In other words, tl.addPause(1).play(1) shouldn't pause. 157 force = true; 158 if (prevRawPrevTime > _tinyNum) { 159 callback = "onReverseComplete"; 160 } 161 } 162 this._rawPrevTime = rawPrevTime = (!suppressEvents || time || prevRawPrevTime === time) ? time : _tinyNum; //when the playhead arrives at EXACTLY time 0 (right on top) of a zero-duration tween, we need to discern if events are suppressed so that when the playhead moves again (next time), it'll trigger the callback. If events are NOT suppressed, obviously the callback would be triggered in this render. Basically, the callback should fire either when the playhead ARRIVES or LEAVES this exact spot, not both. Imagine doing a timeline.seek(0) and there's a callback that sits at 0. Since events are suppressed on that seek() by default, nothing will fire, but when the playhead moves off of that position, the callback should fire. This behavior is what people intuitively expect. We set the _rawPrevTime to be a precise tiny number to indicate this scenario rather than using another property/variable which would increase memory usage. This technique is less readable, but more efficient. 163 } 164 165 } else if (time < 0.0000001) { //to work around occasional floating point math artifacts, round super small values to 0. 166 this._totalTime = this._time = this._cycle = 0; 167 this.ratio = this._ease._calcEnd ? this._ease.getRatio(0) : 0; 168 if (prevTotalTime !== 0 || (duration === 0 && prevRawPrevTime > 0)) { 169 callback = "onReverseComplete"; 170 isComplete = this._reversed; 171 } 172 if (time < 0) { 173 this._active = false;
174 if (duration === 0) if (this._initted || !this.vars.lazy || force) { //zero-duration tweens are tricky because we must discern the momentum/direction of time in order to determine whether the starting values should be rendered or the ending values. If the "playhead" of its timeline goes past the zero-duration tween in the forward direction or lands directly on it, the end values should be rendered, but if the timeline's "playhead" moves past it in the backward direction (from a postitive time to a negative time), the starting values must be rendered. 175 if (prevRawPrevTime >= 0) { 176 force = true; 177 } 178 this._rawPrevTime = rawPrevTime = (!suppressEvents || time || prevRawPrevTime === time) ? time : _tinyNum; //when the playhead arrives at EXACTLY time 0 (right on top) of a zero-duration tween, we need to discern if events are suppressed so that when the playhead moves again (next time), it'll trigger the callback. If events are NOT suppressed, obviously the callback would be triggered in this render. Basically, the callback should fire either when the playhead ARRIVES or LEAVES this exact spot, not both. Imagine doing a timeline.seek(0) and there's a callback that sits at 0. Since events are suppressed on that seek() by default, nothing will fire, but when the playhead moves off of that position, the callback should fire. This behavior is what people intuitively expect. We set the _rawPrevTime to be a precise tiny number to indicate this scenario rather than using another property/variable which would increase memory usage. This technique is less readable, but more efficient. 179 } 180 } 181 if (!this._initted) { //if we render the very beginning (time == 0) of a fromTo(), we must force the render (normal tweens wouldn't need to render at a time of 0 when the prevTime was also 0). This is also mandatory to make sure overwriting kicks in immediately. 182 force = true; 183 } 184 } else { 185 this._totalTime = this._time = time; 186 if (this._repeat !== 0) { 187 cycleDuration = duration + this._repeatDelay; 188 this._cycle = (this._totalTime / cycleDuration) >> 0; //originally _totalTime % cycleDuration but floating point errors caused problems, so I normalized it. (4 % 0.8 should be 0 but some browsers report it as 0.79999999!) 189 if (this._cycle !== 0) if (this._cycle === this._totalTime / cycleDuration && prevTotalTime <= time) { 190 this._cycle--; //otherwise when rendered exactly at the end time, it will act as though it is repeating (at the beginning) 191 } 192 this._time = this._totalTime - (this._cycle * cycleDuration); 193 if (this._yoyo) if ((this._cycle & 1) !== 0) { 194 this._time = duration - this._time; 195 yoyoEase = this._yoyoEase || this.vars.yoyoEase; //note: we don't set this._yoyoEase in _init() like we do other properties because it's TweenMax-specific and doing it here allows us to optimize performance (most tweens don't have a yoyoEase). Note that we also must skip the this.ratio calculation further down right after we _init() in this function, because we're doing it here. 196 if (yoyoEase) { 197 if (!this._yoyoEase) { 198 if (yoyoEase === true && !this._initted) { //if it's not initted and yoyoEase is true, this._ease won't have been populated yet so we must discern it here. 199 yoyoEase = this.vars.ease; 200 this._yoyoEase = yoyoEase = !yoyoEase ? TweenLite.defaultEase : (yoyoEase instanceof Ease) ? yoyoEase : (typeof(yoyoEase) === "function") ? new Ease(yoyoEase, this.vars.easeParams) : Ease.map[yoyoEase] || TweenLite.defaultEase; 201 } else { 202 this._yoyoEase = yoyoEase = (yoyoEase === true) ? this._ease : (yoyoEase instanceof Ease) ? yoyoEase : Ease.map[yoyoEase]; 203 } 204 } 205 this.ratio = yoyoEase ? 1 - yoyoEase.getRatio((duration - this._time) / duration) : 0; 206 } 207 } 208 if (this._time > duration) { 209 this._time = duration; 210 } else if (this._time < 0) { 211 this._time = 0; 212 } 213 } 214 if (this._easeType && !yoyoEase) { 215 r = this._time / duration; 216 type = this._easeType; 217 pow = this._easePower; 218 if (type === 1 || (type === 3 && r >= 0.5)) { 219 r = 1 - r; 220 } 221 if (type === 3) { 222 r *= 2; 223 } 224 if (pow === 1) { 225 r *= r; 226 } else if (pow === 2) { 227 r *= r * r; 228 } else if (pow === 3) { 229 r *= r * r * r; 230 } else if (pow === 4) { 231 r *= r * r * r * r; 232 } 233 234 if (type === 1) { 235 this.ratio = 1 - r; 236 } else if (type === 2) { 237 this.ratio = r; 238 } else if (this._time / duration < 0.5) { 239 this.ratio = r / 2; 240 } else { 241 this.ratio = 1 - (r / 2); 242 } 243 244 } else if (!yoyoEase) { 245 this.ratio = this._ease.getRatio(this._time / duration); 246 } 247 248 } 249 250 if (prevTime === this._time && !force && prevCycle === this._cycle) { 251 if (prevTotalTime !== this._totalTime) if (this._onUpdate) if (!suppressEvents) { //so that onUp
251date fires even during the repeatDelay - as long as the totalTime changed, we should trigger onUpdate. 252 this._callback("onUpdate"); 253 } 254 return; 255 } else if (!this._initted) { 256 this._init(); 257 if (!this._initted || this._gc) { //immediateRender tweens typically won't initialize until the playhead advances (_time is greater than 0) in order to ensure that overwriting occurs properly. Also, if all of the tweening properties have been overwritten (which would cause _gc to be true, as set in _init()), we shouldn't continue otherwise an onStart callback could be called for example. 258 return; 259 } else if (!force && this._firstPT && ((this.vars.lazy !== false && this._duration) || (this.vars.lazy && !this._duration))) { //we stick it in the queue for rendering at the very end of the tick - this is a performance optimization because browsers invalidate styles and force a recalculation if you read, write, and then read style data (so it's better to read/read/read/write/write/write than read/write/read/write/read/write). The down side, of course, is that usually you WANT things to render immediately because you may have code running right after that which depends on the change. Like imagine running TweenLite.set(...) and then immediately after that, creating a nother tween that animates the same property to another value; the starting values of that 2nd tween wouldn't be accurate if lazy is true. 260 this._time = prevTime; 261 this._totalTime = prevTotalTime; 262 this._rawPrevTime = prevRawPrevTime; 263 this._cycle = prevCycle; 264 TweenLiteInternals.lazyTweens.push(this); 265 this._lazy = [time, suppressEvents]; 266 return; 267 } 268 //_ease is initially set to defaultEase, so now that init() has run, _ease is set properly and we need to recalculate the ratio. Overall this is faster than using conditional logic earlier in the method to avoid having to set ratio twice because we only init() once but renderTime() gets called VERY frequently. 269 if (this._time && !isComplete && !yoyoEase) { 270 this.ratio = this._ease.getRatio(this._time / duration); 271 } else if (isComplete && this._ease._calcEnd && !yoyoEase) { 272 this.ratio = this._ease.getRatio((this._time === 0) ? 0 : 1); 273 } 274 } 275 if (this._lazy !== false) { 276 this._lazy = false; 277 } 278 279 if (!this._active) if (!this._paused && this._time !== prevTime && time >= 0) { 280 this._active = true; //so that if the user renders a tween (as opposed to the timeline rendering it), the timeline is forced to re-render and align it with the proper time/frame on the next rendering cycle. Maybe the tween already finished but the user manually re-renders it as halfway done. 281 } 282 if (prevTotalTime === 0) { 283 if (this._initted === 2 && time > 0) { 284 //this.invalidate(); 285 this._init(); //will just apply overwriting since _initted of (2) means it was a from() tween that had immediateRender:true 286 } 287 if (this._startAt) { 288 if (time >= 0) { 289 this._startAt.render(time, true, force); 290 } else if (!callback) { 291 callback = "_dummyGS"; //if no callback is defined, use a dummy value just so that the condition at the end evaluates as true because _startAt should render AFTER the normal render loop when the time is negative. We could handle this in a more intuitive way, of course, but the render loop is the MOST important thing to optimize, so this technique allows us to avoid adding extra conditional logic in a high-frequency area. 292 } 293 } 294 if (this.vars.onStart) if (this._totalTime !== 0 || duration === 0) if (!suppressEvents) { 295 this._callback("onStart"); 296 } 297 } 298 299 pt = this._firstPT; 300 while (pt) { 301 if (pt.f) { 302 pt.t[pt.p](pt.c * this.ratio + pt.s); 303 } else { 304 pt.t[pt.p] = pt.c * this.ratio + pt.s; 305 } 306 pt = pt._next; 307 } 308 309 if (this._onUpdate) { 310 if (time < 0) if (this._startAt && this._startTime) { //if the tween is positioned at the VERY beginning (_startTime 0) of its parent timeline, it's illegal for the playhead to go back further, so we should not render the recorded startAt values. 311 this._startAt.render(time, true, force); //note: for performance reasons, we tuck this conditional logic inside less traveled areas (most tweens don't have an onUpdate). We'd just have it at the end before the onComplete, but the values should be updated before any onUpdate is called, so we ALSO put it here and then if it's not called, we do so later near the onComplete. 312 } 313 if (!suppressEvents) if (this._totalTime !== prevTotalTime || callback) {
314 this._callback("onUpdate"); 315 } 316 } 317 if (this._cycle !== prevCycle) if (!suppressEvents) if (!this._gc) if (this.vars.onRepeat) { 318 this._callback("onRepeat"); 319 } 320 if (callback) if (!this._gc || force) { //check gc because there's a chance that kill() could be called in an onUpdate 321 if (time < 0 && this._startAt && !this._onUpdate && this._startTime) { //if the tween is positioned at the VERY beginning (_startTime 0) of its parent timeline, it's illegal for the playhead to go back further, so we should not render the recorded startAt values. 322 this._startAt.render(time, true, force); 323 } 324 if (isComplete) { 325 if (this._timeline.autoRemoveChildren) { 326 this._enabled(false, false); 327 } 328 this._active = false; 329 } 330 if (!suppressEvents && this.vars[callback]) { 331 this._callback(callback); 332 } 333 if (duration === 0 && this._rawPrevTime === _tinyNum && rawPrevTime !== _tinyNum) { //the onComplete or onReverseComplete could trigger movement of the playhead and for zero-duration tweens (which must discern direction) that land directly back on their start time, we don't want to fire again on the next render. Think of several addPause()'s in a timeline that forces the playhead to a certain spot, but what if it's already paused and another tween is tweening the "time" of the timeline? Each time it moves [forward] past that spot, it would move back, and since suppressEvents is true, it'd reset _rawPrevTime to _tinyNum so that when it begins again, the callback would fire (so ultimately it could bounce back and forth during that tween). Again, this is a very uncommon sce
333nario, but possible nonetheless. 334 this._rawPrevTime = 0; 335 } 336 } 337 }; 338 339//---- STATIC FUNCTIONS ----------------------------------------------------------------------------------------------------------- 340 341 TweenMax.to = function(target, duration, vars) { 342 return new TweenMax(target, duration, vars); 343 }; 344 345 TweenMax.from = function(target, duration, vars) { 346 vars.runBackwards = true; 347 vars.immediateRender = (vars.immediateRender != false); 348 return new TweenMax(target, duration, vars); 349 }; 350 351 TweenMax.fromTo = function(target, duration, fromVars, toVars) { 352 toVars.startAt = fromVars; 353 toVars.immediateRender = (toVars.immediateRender != false && fromVars.immediateRender != false); 354 return new TweenMax(target, duration, toVars); 355 }; 356 357 TweenMax.staggerTo = TweenMax.allTo = function(targets, duration, vars, stagger, onCompleteAll, onCompleteAllParams, onCompleteAllScope) { 358 stagger = stagger || 0; 359 var delay = 0, 360 a = [], 361 finalComplete = function() { 362 if (vars.onComplete) { 363 vars.onComplete.apply(vars.onCompleteScope || this, arguments); 364 } 365 onCompleteAll.apply(onCompleteAllScope || vars.callbackScope || this, onCompleteAllParams || _blankArray); 366 }, 367 cycle = vars.cycle, 368 fromCycle = (vars.startAt && vars.startAt.cycle), 369 l, copy, i, p; 370 if (!_isArray(targets)) { 371 if (typeof(targets) === "string") { 372 targets = TweenLite.selector(targets) || targets; 373 } 374 if (_isSelector(targets)) { 375 targets = _slice(targets); 376 } 377 } 378 targets = targets || []; 379 if (stagger < 0) { 380 targets = _slice(targets); 381 targets.reverse(); 382 stagger *= -1; 383 } 384 l = targets.length - 1; 385 for (i = 0; i <= l; i++) { 386 copy = {}; 387 for (p in vars) { 388 copy[p] = vars[p]; 389 } 390 if (cycle) { 391 _applyCycle(copy, targets, i); 392 if (copy.duration != null) { 393 duration = copy.duration; 394 delete copy.duration; 395 } 396 } 397 if (fromCycle) { 398 fromCycle = copy.startAt = {}; 399 for (p in vars.startAt) { 400 fromCycle[p] = vars.startAt[p]; 401 } 402 _applyCycle(copy.startAt, targets, i); 403 } 404 copy.delay = delay + (copy.delay || 0); 405 if (i === l && onCompleteAll) { 406 copy.onComplete = finalComplete; 407 } 408 a[i] = new TweenMax(targets[i], duration, copy); 409 delay += stagger; 410 } 411 return a; 412 }; 413 414 TweenMax.staggerFrom = TweenMax.allFrom = function(targets, duration, vars, stagger, onCompleteAll, onCompleteAllParams, onCompleteAllScope) { 415 vars.runBackwards = true; 416 vars.immediateRender = (vars.immediateRender != false); 417 return TweenMax.staggerTo(targets, duration, vars, stagger, onCompleteAll, onCompleteAllParams, onCompleteAllScope); 418 }; 419 420 TweenMax.staggerFromTo = TweenMax.allFromTo = function(targets, duration, fromVars, toVars, stagger, onCompleteAll, onCompleteAllParams, onCompleteAllScope) { 421 toVars.startAt = fromVars; 422 toVars.immediateRender = (toVars.immediateRender != false && fromVars.immediateRender != false); 423 return TweenMax.staggerTo(targets, duration, toVars, stagger, onCompleteAll, onCompleteAllParams, onCompleteAllScope); 424 }; 425 426 TweenMax.delayedCall = function(delay, callback, params, scope, useFrames) { 427 return new TweenMax(callback, 0, {delay:delay, onComplete:callback, onCompleteParams:params, callbackScope:scope, onReverseComplete:callback, onReverseCompleteParams:params, immediateRender:false, useFrames:useFrames, overwrite:0}); 428 }; 429 430 TweenMax.set = function(target, vars) { 431 return new TweenMax(target, 0, vars); 432 }; 433 434 TweenMax.isTweening = function(target) { 435 return (TweenLite.getTweensOf(target, true).length > 0); 436 }; 437 438 var _getChildrenOf = function(timeline, includeTimelines) { 439 var a = [], 440 cnt = 0, 441 tween = timeline._first; 442 while (tween) { 443 if (tween instanceof TweenLite) { 444 a[cnt++] = tween; 445 } else { 446 if (includeTimelines) { 447 a[cnt++] = tween; 448 } 449 a = a.concat(_getChildrenOf(tween, includeTimelines)); 450 cnt = a.length; 451 } 452 tween = tween._next; 453 } 454 return a; 455 }, 456 getAllTweens = TweenMax.getAllTweens = function(includeTimelines) { 457 return _getChildrenOf(Animation._rootTimeline, includeTimelines).concat( _getChildrenOf(Animation._rootFramesTimeline, includeTimelines) ); 458 }; 459 460 TweenMax.killAll = function(complete, tweens, delayedCalls, timelines) { 461 if (tweens == null) { 462 tweens = true; 463 } 464 if (delayedCalls == null) { 465 delayedCalls = true; 466 } 467 var a = getAllTweens((timelines != false)), 468 l = a.length, 469 allTrue = (tweens && delayedCalls && timelines), 470 isDC, tween, i; 471 for (i = 0; i < l; i++) { 472 tween = a[i]; 473 if (allTrue || (tween instanceof SimpleTimeline) || ((isDC = (tween.target === tween.vars.onComplete)) && delayedCalls) || (tweens && !isDC)) { 474 if (complete) { 475 tween.totalTime(tween._reversed ? 0 : tween.totalDuration()); 476 } else { 477 tween._enabled(false, false); 478 } 479 } 480 } 481 }; 482 483 TweenMax.killChildTweensOf = function(parent, complete) { 484 if (parent == null) { 485 return; 486 } 487 var tl = TweenLiteInternals.tweenLookup, 488 a, curParent, p, i, l; 489 if (typeof(parent) === "string") { 490 parent = TweenLite.selector(parent) || parent; 491 } 492 if (_isSelector(parent)) { 493 parent = _slice(parent); 494 } 495 if (_isArray(parent)) { 496 i = parent.length; 497 while (--i > -1) { 498 TweenMax.killChildTweensOf(parent[i], complete); 499 } 500 return; 501 } 502 a = []; 503 for (p in tl) { 504 curParent = tl[p].target.parentNode; 505 while (curParent) { 506 if (curParent === parent) { 507 a = a.concat(tl[p].tweens); 508 } 509 curParent = curParent.parentNode; 510 } 511 } 512 l = a.length; 513 for (i = 0; i < l; i++) { 514 if (complete) { 515 a[i].totalTime(a[i].totalDuration()); 516 } 517 a[i]._enabled(false, false); 518 } 519 }; 520 521 var _changePause = function(pause, tweens, delayedCalls, timelines) { 522 tweens = (tweens !== false); 523 delayedCalls = (delayedCalls !== false); 524 timelines = (timelines !== false); 525 var a = getAllTweens(timelines), 526 allTrue = (tweens && delayedCalls && timelines), 527 i = a.length, 528 isDC, tween; 529 while (--i > -1) { 530 tween = a[i]; 531 if (allTrue || (tween instanceof SimpleTimeline) || ((isDC = (tween.target === tween.vars.onComplete)) && delayedCalls) || (tweens && !isDC)) { 532 tween.paused(pause); 533 } 534 } 535 }; 536 537 TweenMax.pauseAll = function(tweens, delayedCalls, timelines) { 538 _changePause(true, tweens, delayedCalls, timelines); 539 }; 540
541 TweenMax.resumeAll = function(tweens, delayedCalls, timelines) { 542 _changePause(false, tweens, delayedCalls, timelines); 543 }; 544 545 TweenMax.globalTimeScale = function(value) { 546 var tl = Animation._rootTimeline, 547 t = TweenLite.ticker.time; 548 if (!arguments.length) { 549 return tl._timeScale; 550 } 551 value = value || _tinyNum; //can't allow zero because it'll throw the math off 552 tl._startTime = t - ((t - tl._startTime) * tl._timeScale / value); 553 tl = Animation._rootFramesTimeline; 554 t = TweenLite.ticker.frame; 555 tl._startTime = t - ((t - tl._startTime) * tl._timeScale / value); 556 tl._timeScale = Animation._rootTimeline._timeScale = value; 557 return value; 558 }; 559 560 561//---- GETTERS / SETTERS ---------------------------------------------------------------------------------------------------------- 562 563 p.progress = function(value, suppressEvents) { 564 return (!arguments.length) ? this._time / this.duration() : this.totalTime( this.duration() * ((this._yoyo && (this._cycle & 1) !== 0) ? 1 - value : value) + (this._cycle * (this._duration + this._repeatDelay)), suppressEvents); 565 }; 566 567 p.totalProgress = function(value, suppressEvents) { 568 return (!arguments.length) ? this._totalTime / this.totalDuration() : this.totalTime( this.totalDuration() * value, suppressEvents); 569 }; 570 571 p.time = function(value, suppressEvents) { 572 if (!arguments.length) { 573 return this._time; 574 } 575 if (this._dirty) { 576 this.totalDuration(); 577 } 578 if (value > this._duration) { 579 value = this._duration; 580 } 581 if (this._yoyo && (this._cycle & 1) !== 0) { 582 value = (this._duration - value) + (this._cycle * (this._duration + this._repeatDelay)); 583 } else if (this._repeat !== 0) { 584 value += this._cycle * (this._duration + this._repeatDelay); 585 } 586 return this.totalTime(value, suppressEvents); 587 }; 588 589 p.duration = function(value) { 590 if (!arguments.length) { 591 return this._duration; //don't set _dirty = false because there could be repeats that haven't been factored into the _totalDuration yet. Otherwise, if you create a repeated TweenMax and then immediately check its duration(), it would cache the value and the totalDuration would not be correct, thus repeats wouldn't take effect. 592 } 593 return Animation.prototype.duration.call(this, value); 594 }; 595 596 p.totalDuration = function(value) { 597 if (!arguments.length) { 598 if (this._dirty) { 599 //instead of Infinity, we use 999999999999 so that we can accommodate reverses 600 this._totalDuration = (this._repeat === -1) ? 999999999999 : this._duration * (this._repeat + 1) + (this._repeatDelay * this._repeat); 601 this._dirty = false; 602 } 603 return this._totalDuration; 604 } 605 return (this._repeat === -1) ? this : this.duration( (value - (this._repeat * this._repeatDelay)) / (this._repeat + 1) ); 606 }; 607 608 p.repeat = function(value) { 609 if (!arguments.length) { 610 return this._repeat; 611 } 612 this._repeat = value; 613 return this._uncache(true); 614 }; 615 616 p.repeatDelay = function(value) { 617 if (!arguments.length) { 618 return this._repeatDelay; 619 } 620 this._repeatDelay = value; 621 return this._uncache(true); 622 }; 623 624 p.yoyo = function(value) { 625 if (!arguments.length) { 626 return this._yoyo; 627 } 628 this._yoyo = value; 629 return this; 630 }; 631 632 633 return TweenMax; 634 635 }, true); 636 637 638 639 640 641 642 643 644/* 645 * ---------------------------------------------------------------- 646 * TimelineLite 647 * ---------------------------------------------------------------- 648 */ 649 _gsScope._gsDefine("TimelineLite", ["core.Animation","core.SimpleTimeline","TweenLite"], function(Animation, SimpleTimeline, TweenLite) { 650 651 var TimelineLite = function(vars) { 652 SimpleTimeline.call(this, vars); 653 this._labels = {}; 654 this.autoRemoveChildren = (this.vars.autoRemoveChildren === true); 655 this.smoothChildTiming = (this.vars.smoothChildTiming === true); 656 this._sortChildren = true; 657 this._onUpdate = this.vars.onUpdate; 658 var v = this.vars, 659 val, p; 660 for (p in v) { 661 val = v[p]; 662 if (_isArray(val)) if (val.join("").indexOf("{self}") !== -1) { 663 v[p] = this._swapSelfInParams(val); 664 } 665 } 666 if (_isArray(v.tweens)) { 667 this.add(v.tweens, 0, v.align, v.stagger); 668 } 669 }, 670 _tinyNum = 0.0000000001, 671 TweenLiteInternals = TweenLite._internals, 672 _internals = TimelineLite._internals = {}, 673 _isSelector = TweenLiteInternals.isSelector, 674 _isArray = TweenLiteInternals.isArray, 675 _lazyTweens = TweenLiteInternals.lazyTweens, 676 _lazyRender = TweenLiteInternals.lazyRender, 677 _globals = _gsScope._gsDefine.globals, 678 _copy = function(vars) { 679 var copy = {}, p; 680 for (p in vars) { 681 copy[p] = vars[p]; 682 } 683 return copy; 684 }, 685 _applyCycle = function(vars, targets, i) { 686 var alt = vars.cycle, 687 p, val; 688 for (p in alt) { 689 val = alt[p]; 690 vars[p] = (typeof(val) === "function") ? val(i, targets[i]) : val[i % val.length]; 691 } 692 delete vars.cycle; 693 }, 694 _pauseCallback = _internals.pauseCallback = function() {}, 695 _slice = function(a) { //don't use [].slice because that doesn't work in IE8 with a NodeList that's returned by querySelectorAll() 696 var b = [], 697 l = a.length, 698 i; 699 for (i = 0; i !== l; b.push(a[i++])); 700 return b; 701 }, 702 p = TimelineLite.prototype = new SimpleTimeline(); 703
704 TimelineLite.version = "2.0.2"; 705 p.constructor = TimelineLite; 706 p.kill()._gc = p._forcingPlayhead = p._hasPause = false; 707 708 /* might use later... 709 //translates a local time inside an animation to the corresponding time on the root/global timeline, factoring in all nesting and timeScales. 710 function localToGlobal(time, animation) { 711 while (animation) { 712 time = (time / animation._timeScale) + animation._startTime; 713 animation = animation.timeline; 714 } 715 return time; 716 } 717 718 //translates the supplied time on the root/global timeline into the corresponding local time inside a particular animation, factoring in all nesting and timeScales 719 function globalToLocal(time, animation) { 720 var scale = 1; 721 time -= localToGlobal(0, animation); 722 while (animation) { 723 scale *= animation._timeScale; 724 animation = animation.timeline; 725 } 726 return time * scale; 727 } 728 */ 729 730 p.to = function(target, duration, vars, position) { 731 var Engine = (vars.repeat && _globals.TweenMax) || TweenLite; 732 return duration ? this.add( new Engine(target, duration, vars), position) : this.set(target, vars, position); 733 }; 734 735 p.from = function(target, duration, vars, position) { 736 return this.add( ((vars.repeat && _globals.TweenMax) || TweenLite).from(target, duration, vars), position); 737 }; 738 739 p.fromTo = function(target, duration, fromVars, toVars, position) { 740 var Engine = (toVars.repeat && _globals.TweenMax) || TweenLite; 741 return duration ? this.add( Engine.fromTo(target, duration, fromVars, toVars), position) : this.set(target, toVars, position); 742 }; 743 744 p.staggerTo = function(targets, duration, vars, stagger, position, onCompleteAll, onCompleteAllParams, onCompleteAllScope) { 745 var tl = new TimelineLite({onComplete:onCompleteAll, onCompleteParams:onCompleteAllParams, callbackScope:onCompleteAllScope, smoothChildTiming:this.smoothChildTiming}), 746 cycle = vars.cycle, 747 copy, i; 748 if (typeof(targets) === "string") { 749 targets = TweenLite.selector(targets) || targets; 750 } 751 targets = targets || []; 752 if (_isSelector(targets)) { //senses if the targets object is a selector. If it is, we should translate it into an array. 753 targets = _slice(targets); 754 } 755 stagger = stagger || 0; 756 if (stagger < 0) { 757 targets = _slice(targets); 758 targets.reverse(); 759 stagger *= -1; 760 } 761 for (i = 0; i < targets.length; i++) { 762 copy = _copy(vars); 763 if (copy.startAt) { 764 copy.startAt = _copy(copy.startAt); 765 if (copy.startAt.cycle) { 766 _applyCycle(copy.startAt, targets, i); 767 } 768 } 769 if (cycle) { 770 _applyCycle(copy, targets, i); 771 if (copy.duration != null) { 772 duration = copy.duration; 773 delete copy.duration; 774 } 775 } 776 tl.to(targets[i], duration, copy, i * stagger); 777 } 778 return this.add(tl, position); 779 }; 780 781 p.staggerFrom = function(targets, duration, vars, stagger, position, onCompleteAll, onCompleteAllParams, onCompleteAllScope) { 782 vars.immediateRender = (vars.immediateRender != false); 783 vars.runBackwards = true; 784 return this.staggerTo(targets, duration, vars, stagger, position, onCompleteAll, onCompleteAllParams, onCompleteAllScope); 785 }; 786 787 p.staggerFromTo = function(targets, duration, fromVars, toVars, stagger, position, onCompleteAll, onCompleteAllParams, onCompleteAllScope) { 788 toVars.startAt = fromVars; 789 toVars.immediateRender = (toVars.immediateRender != false && fromVars.immediateRender != false); 790 return this.staggerTo(targets, duration, toVars, stagger, position, onCompleteAll, onCompleteAllParams, onCompleteAllScope); 791 }; 792 793 p.call = function(callback, params, scope, position) { 794 return this.add( TweenLite.delayedCall(0, callback, params, scope), position); 795 }; 796 797 p.set = function(target, vars, position) { 798 position = this._parseTimeOrLabel(position, 0, true); 799 if (vars.immediateRender == null) { 800 vars.immediateRender = (position === this._time && !this._paused); 801 } 802 return this.add( new TweenLite(target, 0, vars), position); 803 }; 804 805 TimelineLite.exportRoot = function(vars, ignoreDelayedCalls) { 806 vars = vars || {}; 807 if (vars.smoothChildTiming == null) { 808 vars.smoothChildTiming = true; 809 } 810 var tl = new TimelineLite(vars), 811 root = tl._timeline, 812 hasNegativeStart, time, tween, next; 813 if (ignoreDelayedCalls == null) { 814 ignoreDelayedCalls = true; 815 } 816 root._remove(tl, true); 817 tl._startTime = 0; 818 tl._rawPrevTime = tl._time = tl._totalTime = root._time; 819 tween = root._first; 820 while (tween) { 821 next = tween._next; 822 if (!ignoreDelayedCalls || !(tween instanceof TweenLite && tween.target === tween.vars.onComplete)) { 823 time = tween._startTime - tween._delay; 824 if (time < 0) { 825 hasNegativeStart = 1; 826 } 827 tl.add(tween, time); 828 } 829 tween = next; 830 } 831 root.add(tl, 0); 832 if (hasNegativeStart) { //calling totalDuration() will force the adjustment necessary to shift the children forward so none of them start before zero, and moves the timeline backwards the same amount, so the playhead is still aligned where it should be globally, but the timeline doesn't have illegal children that start before zero. 833 tl.totalDuration(); 834 } 835 return tl; 836 }; 837 838 p.add = function(value, position, align, stagger) { 839 var curTime, l, i, child, tl, beforeRawTime; 840 if (typeof(position) !== "number") { 841 position = this._parseTimeOrLabel(position, 0, true, value); 842 } 843 if (!(value instanceof Animation)) { 844 if ((value instanceof Array) || (value && value.push && _isArray(value))) {
845 align = align || "normal"; 846 stagger = stagger || 0; 847 curTime = position; 848 l = value.length; 849 for (i = 0; i < l; i++) { 850 if (_isArray(child = value[i])) { 851 child = new TimelineLite({tweens:child}); 852 } 853 this.add(child, curTime); 854 if (typeof(child) !== "string" && typeof(child) !== "function") { 855 if (align === "sequence") { 856 curTime = child._startTime + (child.totalDuration() / child._timeScale); 857 } else if (align === "start") { 858 child._startTime -= child.delay(); 859 } 860 } 861 curTime += stagger; 862 } 863 return this._uncache(true); 864 } else if (typeof(value) === "string") { 865 return this.addLabel(value, position); 866 } else if (typeof(value) === "function") { 867 value = TweenLite.delayedCall(0, value); 868 } else { 869 throw("Cannot add " + value + " into the timeline; it is not a tween, timeline, function, or string."); 870 } 871 } 872 873 SimpleTimeline.prototype.add.call(this, value, position); 874 875 if (value._time) { //in case, for example, the _startTime is moved on a tween that has already rendered. Imagine it's at its end state, then the startTime is moved WAY later (after the end of this timeline), it should render at its beginning. 876 curTime = Math.max(0, Math.min(value.totalDuration(), (this.rawTime() - value._startTime) * value._timeScale)); 877 if (Math.abs(curTime - value._totalTime) > 0.00001) { //if an onComplete restarts the tween in a nested timeline, for example, there could be an endless loop without this logic (v2.0.2), like var masterTL = new TimelineMax({autoRemoveChildren: true}), tl = new TimelineMax(); tl.eventCallback("onComplete", function() { tl.restart() } );tl.fromTo('div', 1.1, { rotation: 0 }, { rotation: 360 }, 0);masterTL.add(tl); 878 value.render(curTime, false, false); 879 } 880 } 881 882 //if the timeline has already ended but the inserted tween/timeline extends the duration, we should enable this timeline again so that it renders properly. We should also align the playhead with the parent timeline's when appropriate. 883 if (this._gc || this._time === this._duration) if (!this._paused) if (this._duration < this.duration()) { 884 //in case any of the ancestors had completed but should now be enabled... 885 tl = this; 886 beforeRawTime = (tl.rawTime() > value._startTime); //if the tween is placed on the timeline so that it starts BEFORE the current rawTime, we should align the playhead (move the timeline). This is because sometimes users will create a timeline, let it finish, and much later append a tween and expect it to run instead of jumping to its end state. While technically one could argue that it should jump to its end state, that's not what users intuitively expect. 887 while (tl._timeline) { 888 if (beforeRawTime && tl._timeline.smoothChildTiming) { 889 tl.totalTime(tl._totalTime, true); //moves the timeline (shifts its startTime) if necessary, and also enables it. 890 } else if (tl._gc) { 891 tl._enabled(true, false); 892 } 893 tl = tl._timeline; 894 } 895 } 896 897 return this; 898 }; 899 900 p.remove = function(value) { 901 if (value instanceof Animation) { 902 this._remove(value, false); 903 var tl = value._timeline = value.vars.useFrames ? Animation._rootFramesTimeline : Animation._rootTimeline; //now that it's removed, default it to the root timeline so that if it gets played again, it doesn't jump back into this timeline. 904 value._startTime = (value._paused ? value._pauseTime : tl._time) - ((!value._reversed ? value._totalTime : value.totalDuration() - value._totalTime) / value._timeScale); //ensure that if it gets played again, the timing is correct. 905 return this; 906 } else if (value instanceof Array || (value && value.push && _isArray(value))) { 907 var i = value.length; 908 while (--i > -1) { 909 this.remove(value[i]); 910 } 911 return this; 912 } else if (typeof(value) === "string") { 913 return this.removeLabel(value); 914 } 915 return this.kill(null, value); 916 }; 917 918 p._remove = function(tween, skipDisable) { 919 SimpleTimeline.prototype._remove.call(this, tween, skipDisable); 920 var last = this._last; 921 if (!last) { 922 this._time = this._totalTime = this._duration = this._totalDuration = 0; 923 } else if (this._time > this.duration()) { 924 this._time = this._duration; 925 this._totalTime = this._totalDuration; 926 } 927 return this; 928 }; 929 930 p.append = function(value, offsetOrLabel) { 931 return this.add(value, this._parseTimeOrLabel(null, offsetOrLabel, true, value)); 932 }; 933 934 p.insert = p.insertMultiple = function(value, position, align, stagger) { 935 return this.add(value, position || 0, align, stagger); 936 }; 937 938 p.appendMultiple = function(tweens, offsetOrLabel, align, stagger) { 939 return this.add(tweens, this._parseTimeOrLabel(null, offsetOrLabel, true, tweens), align, stagger); 940 }; 941 942 p.addLabel = function(label, position) { 943 this._labels[label] = this._parseTimeOrLabel(position); 944 return this; 945 }; 946 947 p.addPause = function(position, callback, params, scope) { 948 var t = TweenLite.delayedCall(0, _pauseCallback, params, scope || this); 949 t.vars.onComplete = t.vars.onReverseComplete = callback;
950 t.data = "isPause"; 951 this._hasPause = true; 952 return this.add(t, position); 953 }; 954 955 p.removeLabel = function(label) { 956 delete this._labels[label]; 957 return this; 958 }; 959 960 p.getLabelTime = function(label) { 961 return (this._labels[label] != null) ? this._labels[label] : -1; 962 }; 963 964 p._parseTimeOrLabel = function(timeOrLabel, offsetOrLabel, appendIfAbsent, ignore) { 965 var clippedDuration, i; 966 //if we're about to add a tween/timeline (or an array of them) that's already a child of this timeline, we should remove it first so that it doesn't contaminate the duration(). 967 if (ignore instanceof Animation && ignore.timeline === this) { 968 this.remove(ignore); 969 } else if (ignore && ((ignore instanceof Array) || (ignore.push && _isArray(ignore)))) { 970 i = ignore.length; 971 while (--i > -1) { 972 if (ignore[i] instanceof Animation && ignore[i].timeline === this) { 973 this.remove(ignore[i]); 974 } 975 } 976 } 977 clippedDuration = (typeof(timeOrLabel) === "number" && !offsetOrLabel) ? 0 : (this.duration() > 99999999999) ? this.recent().endTime(false) : this._duration; //in case there's a child that infinitely repeats, users almost never intend for the insertion point of a new child to be based on a SUPER long value like that so we clip it and assume the most recently-added child's endTime should be used instead. 978 if (typeof(offsetOrLabel) === "string") { 979 return this._parseTimeOrLabel(offsetOrLabel, (appendIfAbsent && typeof(timeOrLabel) === "number" && this._labels[offsetOrLabel] == null) ? timeOrLabel - clippedDuration : 0, appendIfAbsent); 980 } 981 offsetOrLabel = offsetOrLabel || 0; 982 if (typeof(timeOrLabel) === "string" && (isNaN(timeOrLabel) || this._labels[timeOrLabel] != null)) { //if the string is a number like "1", check to see if there's a label with that name, otherwise interpret it as a number (absolute value). 983 i = timeOrLabel.indexOf("="); 984 if (i === -1) { 985 if (this._labels[timeOrLabel] == null) { 986 return appendIfAbsent ? (this._labels[timeOrLabel] = clippedDuration + offsetOrLabel) : offsetOrLabel; 987 } 988 return this._labels[timeOrLabel] + offsetOrLabel; 989 } 990 offsetOrLabel = parseInt(timeOrLabel.charAt(i-1) + "1", 10) * Number(timeOrLabel.substr(i+1)); 991 timeOrLabel = (i > 1) ? this._parseTimeOrLabel(timeOrLabel.substr(0, i-1), 0, appendIfAbsent) : clippedDuration; 992 } else if (timeOrLabel == null) { 993 timeOrLabel = clippedDuration; 994 } 995 return Number(timeOrLabel) + offsetOrLabel; 996 }; 997 998 p.seek = function(position, suppressEvents) { 999 return this.totalTime((typeof(position) === "number") ? position : this._parseTimeOrLabel(position), (suppressEvents !== false)); 1000 }; 1001 1002 p.stop = function() { 1003 return this.paused(true); 1004 }; 1005 1006 p.gotoAndPlay = function(position, suppressEvents) { 1007 return this.play(position, suppressEvents); 1008 }; 1009 1010 p.gotoAndStop = function(position, suppressEvents) { 1011 return this.pause(position, suppressEvents); 1012 }; 1013 1014 p.render = function(time, suppressEvents, force) { 1015 if (this._gc) { 1016 this._enabled(true, false); 1017 } 1018 var prevTime = this._time, 1019 totalDur = (!this._dirty) ? this._totalDuration : this.totalDuration(), 1020 prevStart = this._startTime, 1021 prevTimeScale = this._timeScale, 1022 prevPaused = this._paused, 1023 tween, isComplete, next, callback, internalForce, pauseTween, curTime; 1024 if (prevTime !== this._time) { //if totalDuration() finds a child with a negative startTime and smoothChildTiming is true, things get shifted around internally so we need to adjust the time accordingly. For example, if a tween starts at -30 we must shift EVERYTHING forward 30 seconds and move this timeline's startTime backward by 30 seconds so that things align with the playhead (no jump). 1025 time += this._time - prevTime; 1026 } 1027 if (time >= totalDur - 0.0000001 && time >= 0) { //to work around occasional floating point math artifacts. 1028 this._totalTime = this._time = totalDur; 1029 if (!this._reversed) if (!this._hasPausedChild()) { 1030 isComplete = true; 1031 callback = "onComplete"; 1032 internalForce = !!this._timeline.autoRemoveChildren; //otherwise, if the animation is unpaused/activated after it's already finished, it doesn't get removed from the parent timeline. 1033 if (this._duration === 0) if ((time <= 0 && time >= -0.0000001) || this._rawPrevTime < 0 || this._rawPrevTime === _tinyNum) if (this._rawPrevTime !== time && this._first) { 1034 internalForce = true; 1035 if (this._rawPrevTime > _tinyNum) { 1036 callback = "onReverseComplete"; 1037 } 1038 } 1039 } 1040 this._rawPrevTime = (this._duration || !suppressEvents || time || this._rawPrevTime === time) ? time : _tinyNum; //when the playhead arrives at EXACTLY time 0 (right on top) of a zero-duration timeline or tween, we need to discern if events are suppressed so that when the playhead moves again (next time), it'll trigger the callback. If events are NOT suppressed, obviously the callback would be triggered in this render. Basically, the callback should fire either when the playhead ARRIVES or LEAVES this exact spot, not both. Imagine doing a timeline.seek(0) and there's a callback that sits at 0. Since events are suppressed on that seek() by default, nothing will fire, but when the playhead moves off of that position, the callback should fire. This behavior is what people intuitively expect. We set the _rawPrevTime to be a precise tiny number to indicate this scenario rather than using another property/variable which would increase memory usage. This technique is less readable, but more efficient. 1041 time = totalDur + 0.0001; //to avoid occasional floating point rounding errors - sometimes child tweens/timelines were not being fully completed (their progress might be 0.999999999999998 instead of 1 because when _time - tween._startTime is performed, floating point errors would return a value that was SLIGHTLY off). Try (999999999999.7 - 999999999999) * 1 = 0.699951171875 instead of 0.7. 1042 1043 } else if (time < 0.0000001) { //to work around occasional floating point math artifacts, round super small values to 0. 1044 this._totalTime = this._time = 0; 1045 if (prevTime !== 0 || (this._duration === 0 && this._rawPrevTime !== _tinyNum && (this._rawPrevTime > 0 || (time < 0 && this._rawPrevTime >= 0)))) { 1046 callback = "onReverseComplete"; 1047 isComplete = this._reversed; 1048 } 1049 if (time < 0) { 1050 this._active = false;
1051 if (this._timeline.autoRemoveChildren && this._reversed) { //ensures proper GC if a timeline is resumed after it's finished reversing. 1052 internalForce = isComplete = true; 1053 callback = "onReverseComplete"; 1054 } else if (this._rawPrevTime >= 0 && this._first) { //when going back beyond the start, force a render so that zero-duration tweens that sit at the very beginning render their start values properly. Otherwise, if the parent timeline's playhead lands exactly at this timeline's startTime, and then moves backwards, the zero-duration tweens at the beginning would still be at their end state. 1055 internalForce = true; 1056 } 1057 this._rawPrevTime = time; 1058 } else { 1059 this._rawPrevTime = (this._duration || !suppressEvents || time || this._rawPrevTime === time) ? time : _tinyNum; //when the playhead arrives at EXACTLY time 0 (right on top) of a zero-duration timeline or tween, we need to discern if events are suppressed so that when the playhead moves again (next time), it'll trigger the callback. If events are NOT suppressed, obviously the callback would be triggered in this render. Basically, the callback should fire either when the playhead ARRIVES or LEAVES this exact spot, not both. Imagine doing a timeline.seek(0) and there's a callback that sits at 0. Since events are suppressed on that seek() by default, nothing will fire, but when the playhead moves off of that position, the callback should fire. This behavior is what people intuitively expect. We set the _rawPrevTime to be a precise tiny number to indicate this scenario rather than using another property/variable which would increase memory usage. This technique is less readable, but more efficient. 1060 if (time === 0 && isComplete) { //if there's a zero-duration tween at the very beginning of a timeline and the playhead lands EXACTLY at time 0, that tween will correctly render its end values, but we need to keep the timeline alive for one more render so that the beginning values render properly as the parent's playhead keeps moving beyond the begining. Imagine obj.x starts at 0 and then we do tl.set(obj, {x:100}).to(obj, 1, {x:200}) and then later we tl.reverse()...the goal is to have obj.x revert to 0. If the playhead happens to land on exactly 0, without this chunk of code, it'd complete the timeline and remove it from the rendering queue (not good). 1061 tween = this._first; 1062 while (tween && tween._startTime === 0) { 1063 if (!tween._duration) { 1064 isComplete = false; 1065 } 1066 tween = tween._next; 1067 } 1068 } 1069 time = 0; //to avoid occasional floating point rounding errors (could cause problems especially with zero-duration tweens at the very beginning of the timeline) 1070 if (!this._initted) { 1071 internalForce = true; 1072 } 1073 } 1074 1075 } else { 1076 1077 if (this._hasPause && !this._forcingPlayhead && !suppressEvents) { 1078 if (time >= prevTime) { 1079 tween = this._first; 1080 while (tween && tween._startTime <= time && !pauseTween) { 1081 if (!tween._duration) if (tween.data === "isPause" && !tween.ratio && !(tween._startTime === 0 && this._rawPrevTime === 0)) { 1082 pauseTween = tween; 1083 } 1084 tween = tween._next; 1085 } 1086 } else { 1087 tween = this._last; 1088 while (tween && tween._startTime >= time && !pauseTween) { 1089 if (!tween._duration) if (tween.data === "isPause" && tween._rawPrevTime > 0) { 1090 pauseTween = tween; 1091 } 1092 tween = tween._prev; 1093 } 1094 } 1095 if (pauseTween) { 1096 this._time = time = pauseTween._startTime; 1097 this._totalTime = time + (this._cycle * (this._totalDuration + this._repeatDelay)); 1098 } 1099 } 1100 1101 this._totalTime = this._time = this._rawPrevTime = time; 1102 } 1103 if ((this._time === prevTime || !this._first) && !force && !internalForce && !pauseTween) { 1104 return; 1105 } else if (!this._initted) { 1106 this._initted = true; 1107 } 1108 1109 if (!this._active) if (!this._paused && this._time !== prevTime && time > 0) { 1110 this._active = true; //so that if the user renders the timeline (as opposed to the parent timeline rendering it), it is forced to re-render and align it with the proper time/frame on the next rendering cycle. Maybe the timeline already finished but the user manually re-renders it as halfway done, for example. 1111 } 1112 1113 if (prevTime === 0) if (this.vars.onStart) if (this._time !== 0 || !this._duration) if (!suppressEvents) { 1114 this._callback("onStart"); 1115 } 1116 1117 curTime = this._time; 1118 if (curTime >= prevTime) { 1119 tween = this._first; 1120 while (tween) { 1121 next = tween._next; //record it here because the value could change after rendering... 1122 if (curTime !== this._time || (this._paused && !prevPaused)) { //in case a tween pauses or seeks the timeline when rendering, like inside of an onUpdate/onComplete 1123 break; 1124 } else if (tween._active || (tween._startTime <= curTime && !tween._paused && !tween._gc)) { 1125 if (pauseTween === tween) { 1126 this.pause(); 1127 } 1128 if (!tween._reversed) { 1129 tween.render((time - tween._startTime) * tween._timeScale, suppressEvents, force); 1130 } else { 1131 tween.render(((!tween._dirty) ? tween._totalDuration : tween.totalDuration()) - ((time - tween._startTime) * tween._timeScale), suppressEvents, force); 1132 } 1133 } 1134 tween = next; 1135 } 1136 } else { 1137 tween = this._last; 1138 while (tween) { 1139 next = tween._prev; //record it here because the value could change after rendering... 1140 if (curTime !== this._time || (this._paused && !prevPaused)) { //in case a tween pauses or seeks the timeline when rendering, like inside of an onUpdate/onComplete 1141 break; 1142 } else if (tween._active || (tween._startTime <= prevTime && !tween._paused && !tween._gc)) { 1143 if (pauseTween === tween) { 1144 pauseTween = tween._prev; //the linked list is organized by _startTime, thus it's possible that a tween could start BEFORE the pause and end after it, in which case it would be positioned before the pause tween in the linked list, but we should render it before we pause() the timeline and cease rendering. This is only a concern when going in reverse. 1145 while (pauseTween && pauseTween.endTime() > this._time) { 1146 pauseTween.render( (pauseTween._reversed ? pauseTween.totalDuration() - ((time - pauseTween._startTime) * pauseTween._timeScale) : (time - pauseTween._startTime) * pauseTween._timeScale), suppressEvents, force); 1147 pauseTween = pauseTween._prev; 1148 } 1149 pauseTween = null; 1150 this.pause(); 1151 } 1152 if (!tween._reversed) { 1153 tween.render((time - tween._startTime) * tween._timeScale, suppressEvents, force); 1154 } else { 1155 tween.render(((!tween._dirty) ? tween._totalDuration : tween.totalDuration()) - ((time - tween._startTime) * tween._timeScale), suppressEvents, force); 1156 } 1157 } 1158 tween = next; 1159 } 1160 } 1161 1162 if (this._onUpdate) if (!suppressEvents) { 1163 if (_lazyTweens.length) { //in case rendering caused any tweens to lazy-init, we should render them because typically when a timeline finishes, users expect things to have rendered fully. Imagine an onUpdate on a timeline that reports/checks tweened values. 1164 _lazyRender(); 1165 }
1166 this._callback("onUpdate"); 1167 } 1168 1169 if (callback) if (!this._gc) if (prevStart === this._startTime || prevTimeScale !== this._timeScale) if (this._time === 0 || totalDur >= this.totalDuration()) { //if one of the tweens that was rendered altered this timeline's startTime (like if an onComplete reversed the timeline), it probably isn't complete. If it is, don't worry, because whatever call altered the startTime would complete if it was necessary at the new time. The only exception is the timeScale property. Also check _gc because there's a chance that kill() could be called in an onUpdate 1170 if (isComplete) { 1171 if (_lazyTweens.length) { //in case rendering caused any tweens to lazy-init, we should render them because typically when a timeline finishes, users expect things to have rendered fully. Imagine an onComplete on a timeline that reports/checks tweened values. 1172 _lazyRender(); 1173 } 1174 if (this._timeline.autoRemoveChildren) { 1175 this._enabled(false, false); 1176 } 1177 this._active = false; 1178 } 1179 if (!suppressEvents && this.vars[callback]) { 1180 this._callback(callback); 1181 } 1182 } 1183 }; 1184 1185 p._hasPausedChild = function() { 1186 var tween = this._first; 1187 while (tween) { 1188 if (tween._paused || ((tween instanceof TimelineLite) && tween._hasPausedChild())) { 1189 return true; 1190 } 1191 tween = tween._next; 1192 } 1193 return false; 1194 }; 1195 1196 p.getChildren = function(nested, tweens, timelines, ignoreBeforeTime) { 1197 ignoreBeforeTime = ignoreBeforeTime || -9999999999; 1198 var a = [], 1199 tween = this._first, 1200 cnt = 0; 1201 while (tween) { 1202 if (tween._startTime < ignoreBeforeTime) { 1203 //do nothing 1204 } else if (tween instanceof TweenLite) { 1205 if (tweens !== false) { 1206 a[cnt++] = tween; 1207 } 1208 } else { 1209 if (timelines !== false) { 1210 a[cnt++] = tween; 1211 } 1212 if (nested !== false) { 1213 a = a.concat(tween.getChildren(true, tweens, timelines)); 1214 cnt = a.length; 1215 } 1216 } 1217 tween = tween._next; 1218 } 1219 return a; 1220 }; 1221 1222 p.getTweensOf = function(target, nested) { 1223 var disabled = this._gc, 1224 a = [], 1225 cnt = 0, 1226 tweens, i; 1227 if (disabled) { 1228 this._enabled(true, true); //getTweensOf() filters out disabled tweens, and we have to mark them as _gc = true when the timeline completes in order to allow clean garbage collection, so temporarily re-enable the timeline here. 1229 } 1230 tweens = TweenLite.getTweensOf(target); 1231 i = tweens.length; 1232 while (--i > -1) { 1233 if (tweens[i].timeline === this || (nested && this._contains(tweens[i]))) { 1234 a[cnt++] = tweens[i]; 1235 } 1236 } 1237 if (disabled) { 1238 this._enabled(false, true); 1239 } 1240 return a; 1241 }; 1242 1243 p.recent = function() { 1244 return this._recent; 1245 }; 1246 1247 p._contains = function(tween) { 1248 var tl = tween.timeline; 1249 while (tl) { 1250 if (tl === this) { 1251 return true; 1252 } 1253 tl = tl.timeline; 1254 } 1255 return false; 1256 }; 1257 1258 p.shiftChildren = function(amount, adjustLabels, ignoreBeforeTime) { 1259 ignoreBeforeTime = ignoreBeforeTime || 0; 1260 var tween = this._first, 1261 labels = this._labels, 1262 p; 1263 while (tween) { 1264 if (tween._startTime >= ignoreBeforeTime) { 1265 tween._startTime += amount; 1266 } 1267 tween = tween._next; 1268 }
1269 if (adjustLabels) { 1270 for (p in labels) { 1271 if (labels[p] >= ignoreBeforeTime) { 1272 labels[p] += amount; 1273 } 1274 } 1275 } 1276 return this._uncache(true); 1277 }; 1278 1279 p._kill = function(vars, target) { 1280 if (!vars && !target) { 1281 return this._enabled(false, false); 1282 } 1283 var tweens = (!target) ? this.getChildren(true, true, false) : this.getTweensOf(target), 1284 i = tweens.length, 1285 changed = false; 1286 while (--i > -1) { 1287 if (tweens[i]._kill(vars, target)) { 1288 changed = true; 1289 } 1290 } 1291 return changed; 1292 }; 1293 1294 p.clear = function(labels) { 1295 var tweens = this.getChildren(false, true, true), 1296 i = tweens.length; 1297 this._time = this._totalTime = 0; 1298 while (--i > -1) { 1299 tweens[i]._enabled(false, false); 1300 } 1301 if (labels !== false) { 1302 this._labels = {}; 1303 } 1304 return this._uncache(true); 1305 }; 1306 1307 p.invalidate = function() { 1308 var tween = this._first; 1309 while (tween) { 1310 tween.invalidate(); 1311 tween = tween._next; 1312 } 1313 return Animation.prototype.invalidate.call(this);; 1314 }; 1315 1316 p._enabled = function(enabled, ignoreTimeline) { 1317 if (enabled === this._gc) { 1318 var tween = this._first; 1319 while (tween) { 1320 tween._enabled(enabled, true); 1321 tween = tween._next; 1322 } 1323 } 1324 return SimpleTimeline.prototype._enabled.call(this, enabled, ignoreTimeline); 1325 }; 1326 1327 p.totalTime = function(time, suppressEvents, uncapped) { 1328 this._forcingPlayhead = true; 1329 var val = Animation.prototype.totalTime.apply(this, arguments); 1330 this._forcingPlayhead = false; 1331 return val; 1332 }; 1333 1334 p.duration = function(value) { 1335 if (!arguments.length) { 1336 if (this._dirty) { 1337 this.totalDuration(); //just triggers recalculation 1338 } 1339 return this._duration; 1340 } 1341 if (this.duration() !== 0 && value !== 0) { 1342 this.timeScale(this._duration / value); 1343 } 1344 return this; 1345 }; 1346 1347 p.totalDuration = function(value) { 1348 if (!arguments.length) { 1349 if (this._dirty) { 1350 var max = 0, 1351 tween = this._last, 1352 prevStart = 999999999999, 1353 prev, end; 1354 while (tween) { 1355 prev = tween._prev; //record it here in case the tween changes position in the sequence... 1356 if (tween._dirty) { 1357 tween.totalDuration(); //could change the tween._startTime, so make sure the tween's cache is clean before analyzing it. 1358 } 1359 if (tween._startTime > prevStart && this._sortChildren && !tween._paused && !this._calculatingDuration) { //in case one of the tweens shifted out of order, it needs to be re-inserted into the correct position in the sequence 1360 this._calculatingDuration = 1; //prevent endless recursive calls - there are methods that get triggered that check duration/totalDuration when we add(), like _parseTimeOrLabel(). 1361 this.add(tween, tween._startTime - tween._delay); 1362 this._calculatingDuration = 0; 1363 } else { 1364 prevStart = tween._startTime; 1365 } 1366 if (tween._startTime < 0 && !tween._paused) { //children aren't allowed to have negative startTimes unless smoothChildTiming is true, so adjust here if one is found. 1367 max -= tween._startTime; 1368 if (this._timeline.smoothChildTiming) { 1369 this._startTime += tween._startTime / this._timeScale; 1370 this._time -= tween._startTime; 1371 this._totalTime -= tween._startTime; 1372 this._rawPrevTime -= tween._startTime; 1373 } 1374 this.shiftChildren(-tween._startTime, false, -9999999999); 1375 prevStart = 0; 1376 } 1377 end = tween._startTime + (tween._totalDuration / tween._timeScale); 1378 if (end > max) { 1379 max = end; 1380 } 1381 tween = prev; 1382 } 1383 this._duration = this._totalDuration = max; 1384 this._dirty = false; 1385 } 1386 return this._totalDuration; 1387 } 1388 return (value && this.totalDuration()) ? this.timeScale(this._totalDuration / value) : this; 1389 }; 1390 1391 p.paused = function(value) { 1392 if (!value) { //if there's a pause directly at the spot from where we're unpausing, skip it. 1393 var tween = this._first, 1394 time = this._time; 1395 while (tween) { 1396 if (tween._startTime === time && tween.data === "isPause") { 1397 tween._rawPrevTime = 0; //remember, _rawPrevTime is how zero-duration tweens/callbacks sense directionality and determine whether or not to fire. If _rawPrevTime is the same as _startTime on the next render, it won't fire. 1398 } 1399 tween = tween._next; 1400 } 1401 } 1402 return Animation.prototype.paused.apply(this, arguments); 1403 }; 1404 1405 p.usesFrames = function() { 1406 var tl = this._timeline; 1407 while (tl._timeline) { 1408 tl = tl._timeline; 1409 } 1410 return (tl === Animation._rootFramesTimeline); 1411 }; 1412 1413 p.rawTime = function(wrapRepeats) { 1414 return (wrapRepeats && (this._paused || (this._repeat && this.time() > 0 && this.totalProgress() < 1))) ? this._totalTime % (this._duration + this._repeatDelay) : this._paused ? this._totalTime : (this._timeline.rawTime(wrapRepeats) - this._startTime) * this._timeScale; 1415 }; 1416 1417 return TimelineLite; 1418 1419 }, true); 1420 1421 1422 1423 1424 1425 1426 1427 1428 1429 1430 1431 1432 1433/* 1434 * ---------------------------------------------------------------- 1435 * TimelineMax 1436 * ---------------------------------------------------------------- 1437 */ 1438 _gsScope._gsDefine("TimelineMax", ["TimelineLite","TweenLite","easing.Ease"], function(TimelineLite, TweenLite, Ease) { 1439 1440 var TimelineMax = function(vars) { 1441 TimelineLite.call(this, vars); 1442 this._repeat = this.vars.repeat || 0; 1443 this._repeatDelay = this.vars.repeatDelay || 0; 1444 this._cycle = 0; 1445 this._yoyo = (this.vars.yoyo === true); 1446 this._dirty = true; 1447 }, 1448 _tinyNum = 0.0000000001, 1449 TweenLiteInternals = TweenLite._internals, 1450 _lazyTweens = TweenLiteInternals.lazyTweens, 1451 _lazyRender = TweenLiteInternals.lazyRender, 1452 _globals = _gsScope._gsDefine.globals,
1453 _easeNone = new Ease(null, null, 1, 0), 1454 p = TimelineMax.prototype = new TimelineLite(); 1455 1456 p.constructor = TimelineMax; 1457 p.kill()._gc = false; 1458 TimelineMax.version = "2.0.2"; 1459 1460 p.invalidate = function() { 1461 this._yoyo = (this.vars.yoyo === true); 1462 this._repeat = this.vars.repeat || 0; 1463 this._repeatDelay = this.vars.repeatDelay || 0; 1464 this._uncache(true); 1465 return TimelineLite.prototype.invalidate.call(this); 1466 }; 1467 1468 p.addCallback = function(callback, position, params, scope) { 1469 return this.add( TweenLite.delayedCall(0, callback, params, scope), position); 1470 }; 1471 1472 p.removeCallback = function(callback, position) { 1473 if (callback) { 1474 if (position == null) { 1475 this._kill(null, callback); 1476 } else { 1477 var a = this.getTweensOf(callback, false), 1478 i = a.length, 1479 time = this._parseTimeOrLabel(position); 1480 while (--i > -1) { 1481 if (a[i]._startTime === time) { 1482 a[i]._enabled(false, false); 1483 } 1484 } 1485 } 1486 } 1487 return this; 1488 }; 1489 1490 p.removePause = function(position) { 1491 return this.removeCallback(TimelineLite._internals.pauseCallback, position); 1492 }; 1493 1494 p.tweenTo = function(position, vars) { 1495 vars = vars || {}; 1496 var copy = {ease:_easeNone, useFrames:this.usesFrames(), immediateRender:false, lazy:false}, 1497 Engine = (vars.repeat && _globals.TweenMax) || TweenLite, 1498 duration, p, t; 1499 for (p in vars) { 1500 copy[p] = vars[p]; 1501 } 1502 copy.time = this._parseTimeOrLabel(position); 1503 duration = (Math.abs(Number(copy.time) - this._time) / this._timeScale) || 0.001; 1504 t = new Engine(this, duration, copy); 1505 copy.onStart = function() { 1506 t.target.paused(true); 1507 if (t.vars.time !== t.target.time() && duration === t.duration() && !t.isFromTo) { //don't make the duration zero - if it's supposed to be zero, don't worry because it's already initting the tween and will complete immediately, effectively making the duration zero anyway. If we make duration zero, the tween won't run at all. 1508 t.duration( Math.abs( t.vars.time - t.target.time()) / t.target._timeScale ).render(t.time(), true, true); //render() right away to ensure that things look right, especially in the case of .tweenTo(0). 1509 } 1510 if (vars.onStart) { //in case the user had an onStart in the vars - we don't want to overwrite it. 1511 vars.onStart.apply(vars.onStartScope || vars.callbackScope || t, vars.onStartParams || []); //don't use t._callback("onStart") or it'll point to the copy.onStart and we'll get a recursion error. 1512 } 1513 }; 1514 return t; 1515 }; 1516 1517 p.tweenFromTo = function(fromPosition, toPosition, vars) { 1518 vars = vars || {}; 1519 fromPosition = this._parseTimeOrLabel(fromPosition); 1520 vars.startAt = {onComplete:this.seek, onCompleteParams:[fromPosition], callbackScope:this}; 1521 vars.immediateRender = (vars.immediateRender !== false); 1522 var t = this.tweenTo(toPosition, vars); 1523 t.isFromTo = 1; //to ensure we don't mess with the duration in the onStart (we've got the start and end values here, so lock it in) 1524 return t.duration((Math.abs( t.vars.time - fromPosition) / this._timeScale) || 0.001); 1525 }; 1526 1527 p.render = function(time, suppressEvents, force) { 1528 if (this._gc) { 1529 this._enabled(true, false); 1530 } 1531 var prevTime = this._time, 1532 totalDur = (!this._dirty) ? this._totalDuration : this.totalDuration(), 1533 dur = this._duration, 1534 prevTotalTime = this._totalTime, 1535 prevStart = this._startTime, 1536 prevTimeScale = this._timeScale, 1537 prevRawPrevTime = this._rawPrevTime, 1538 prevPaused = this._paused, 1539 prevCycle = this._cycle, 1540 tween, isComplete, next, callback, internalForce, cycleDuration, pauseTween, curTime; 1541 if (prevTime !== this._time) { //if totalDuration() finds a child with a negative startTime and smoothChildTiming is true, things get shifted around internally so we need to adjust the time accordingly. For example, if a tween starts at -30 we must shift EVERYTHING forward 30 seconds and move this timeline's startTime backward by 30 seconds so that things align with the playhead (no jump). 1542 time += this._time - prevTime; 1543 } 1544 if (time >= totalDur - 0.0000001 && time >= 0) { //to work around occasional floating point math artifacts. 1545 if (!this._locked) { 1546 this._totalTime = totalDur; 1547 this._cycle = this._repeat; 1548 } 1549 if (!this._reversed) if (!this._hasPausedChild()) { 1550 isComplete = true; 1551 callback = "onComplete"; 1552 internalForce = !!this._timeline.autoRemoveChildren; //otherwise, if the animation is unpaused/activated after it's already finished, it doesn't get removed from the parent timeline. 1553 if (this._duration === 0) if ((time <= 0 && time >= -0.0000001) || prevRawPrevTime < 0 || prevRawPrevTime === _tinyNum) if (prevRawPrevTime !== time && this._first) { 1554 internalForce = true; 1555 if (prevRawPrevTime > _tinyNum) { 1556 callback = "onReverseComplete"; 1557 } 1558 } 1559 } 1560 this._rawPrevTime = (this._duration || !suppressEvents || time || this._rawPrevTime === time) ? time : _tinyNum; //when the playhead arrives at EXACTLY time 0 (right on top) of a zero-duration timeline or tween, we need to discern if events are suppressed so that when the playhead moves again (next time), it'll trigger the callback. If events are NOT suppressed, obviously the callback would be triggered in this render. Basically, the callback should fire either when the playhead ARRIVES or LEAVES this exact spot, not both. Imagine doing a timeline.seek(0) and there's a callback that sits at 0. Since events are suppressed on that seek() by default, nothing will fire, but when the playhead moves off of that position, the callback should fire. This behavior is what people intuitively expect. We set the _rawPrevTime to be a precise tiny number to indicate this scenario rather than using another property/variable which would increase memory usage. This technique is less readable, but more efficient. 1561 if (this._yoyo && (this._cycle & 1) !== 0) { 1562 this._time = time = 0; 1563 } else { 1564 this._time = dur; 1565 time = dur + 0.0001; //to avoid occasional floating point rounding errors - sometimes child tweens/timelines were not being fully completed (their progress might be 0.999999999999998 instead of 1 because when _time - tween._startTime is performed, floating point errors would return a value that was SLIGHTLY off). Try (999999999999.7 - 999999999999) * 1 = 0.699951171875 instead of 0.7. We cannot do less then 0.0001 because the same issue can occur when the duration is extremely large like 999999999999 in
1565which case adding 0.00000001, for example, causes it to act like nothing was added. 1566 } 1567 1568 } else if (time < 0.0000001) { //to work around occasional floating point math artifacts, round super small values to 0. 1569 if (!this._locked) { 1570 this._totalTime = this._cycle = 0; 1571 } 1572 this._time = 0; 1573 if (prevTime !== 0 || (dur === 0 && prevRawPrevTime !== _tinyNum && (prevRawPrevTime > 0 || (time < 0 && prevRawPrevTime >= 0)) && !this._locked)) { //edge case for checking time < 0 && prevRawPrevTime >= 0: a zero-duration fromTo() tween inside a zero-duration timeline (yeah, very rare) 1574 callback = "onReverseComplete"; 1575 isComplete = this._reversed; 1576 } 1577 if (time < 0) { 1578 this._active = false; 1579 if (this._timeline.autoRemoveChildren && this._reversed) { 1580 internalForce = isComplete = true; 1581 callback = "onReverseComplete"; 1582 } else if (prevRawPrevTime >= 0 && this._first) { //when going back beyond the start, force a render so that zero-duration tweens that sit at the very beginning render their start values properly. Otherwise, if the parent timeline's playhead lands exactly at this timeline's startTime, and then moves backwards, the zero-duration tweens at the beginning would still be at their end state. 1583 internalForce = true; 1584 } 1585 this._rawPrevTime = time; 1586 } else { 1587 this._rawPrevTime = (dur || !suppressEvents || time || this._rawPrevTime === time) ? time : _tinyNum; //when the playhead arrives at EXACTLY time 0 (right on top) of a zero-duration timeline or tween, we need to discern if events are suppressed so that when the playhead moves again (next time), it'll trigger the callback. If events are NOT suppressed, obviously the callback would be triggered in this render. Basically, the callback should fire either when the playhead ARRIVES or LEAVES this exact spot, not both. Imagine doing a timeline.seek(0) and there's a callback that sits at 0. Since events are suppressed on that seek() by default, nothing will fire, but when the playhead moves off of that position, the callback should fire. This behavior is what people intuitively expect. We set the _rawPrevTime to be a precise tiny number to indicate this scenario rather than using another property/variable which would increase memory usage. This technique is less readable, but more efficient. 1588 if (time === 0 && isComplete) { //if there's a zero-duration tween at the very beginning of a timeline and the playhead lands EXACTLY at time 0, that tween will correctly render its end values, but we need to keep the timeline alive for one more render so that the beginning values render properly as the parent's playhead keeps moving beyond the begining. Imagine obj.x starts at 0 and then we do tl.set(obj, {x:100}).to(obj, 1, {x:200}) and then later we tl.reverse()...the goal is to have obj.x revert to 0. If the playhead happens to land on exactly 0, without this chunk of code, it'd complete the timeline and remove it from the rendering queue (not good). 1589 tween = this._first; 1590 while (tween && tween._startTime === 0) { 1591 if (!tween._duration) { 1592 isComplete = false; 1593 } 1594 tween = tween._next; 1595 } 1596 } 1597 time = 0; //to avoid occasional floating point rounding errors (could cause problems especially with zero-duration tweens at the very beginning of the timeline) 1598 if (!this._initted) { 1599 internalForce = true; 1600 } 1601 } 1602 1603 } else { 1604 if (dur === 0 && prevRawPrevTime < 0) { //without this, zero-duration repeating timelines (like with a simple callback nested at the very beginning and a repeatDelay) wouldn't render the first time through. 1605 internalForce = true; 1606 } 1607 this._time = this._rawPrevTime = time; 1608 if (!this._locked) { 1609 this._totalTime = time; 1610 if (this._repeat !== 0) { 1611 cycleDuration = dur + this._repeatDelay; 1612 this._cycle = (this._totalTime / cycleDuration) >> 0; //originally _totalTime % cycleDuration but floating point errors caused problems, so I normalized it. (4 % 0.8 should be 0 but it gets reported as 0.79999999!) 1613 if (this._cycle !== 0) if (this._cycle === this._totalTime / cycleDuration && prevTotalTime <= time) { 1614 this._cycle--; //otherwise when rendered exactly at the end time, it will act as though it is repeating (at the beginning) 1615 } 1616 this._time = this._totalTime - (this._cycle * cycleDuration); 1617 if (this._yoyo) if ((this._cycle & 1) !== 0) { 1618 this._time = dur - this._time; 1619 } 1620 if (this._time > dur) { 1621 this._time = dur; 1622 time = dur + 0.0001; //to avoid occasional floating point rounding error 1623 } else if (this._time < 0) { 1624 this._time = time = 0; 1625 } else { 1626 time = this._time; 1627 } 1628 } 1629 } 1630 1631 if (this._hasPause && !this._forcingPlayhead && !suppressEvents) { 1632 time = this._time; 1633 if (time >= prevTime || (this._repeat && prevCycle !== this._cycle)) { 1634 tween = this._first; 1635 while (tween && tween._startTime <= time && !pauseTween) { 1636 if (!tween._duration) if (tween.data === "isPause" && !tween.ratio && !(tween._startTime === 0 && this._rawPrevTime === 0)) { 1637 pauseTween = tween; 1638 } 1639 tween = tween._next; 1640 } 1641 } else { 1642 tween = this._last; 1643 while (tween && tween._startTime >= time && !pauseTween) { 1644 if (!tween._duration) if (tween.data === "isPause" && tween._rawPrevTime > 0) { 1645 pauseTween = tween; 1646 } 1647 tween = tween._prev; 1648 } 1649 } 1650 if (pauseTween && pauseTween._startTime < dur) { 1651 this._time = time = pauseTween._startTime; 1652 this._totalTime = time + (this._cycle * (this._totalDuration + this._repeatDelay)); 1653 } 1654 } 1655 1656 } 1657 1658 if (this._cycle !== prevCycle) if (!this._locked) { 1659 /* 1660 make sure children at the end/beginning of the timeline are rendered properly. If, for example, 1661 a 3-second long timeline rendered at 2.9 seconds previously, and now renders at 3.2 seconds (which
1662 would get transated to 2.8 seconds if the timeline yoyos or 0.2 seconds if it just repeats), there 1663 could be a callback or a short tween that's at 2.95 or 3 seconds in which wouldn't render. So 1664 we need to push the timeline to the end (and/or beginning depending on its yoyo value). Also we must 1665 ensure that zero-duration tweens at the very beginning or end of the TimelineMax work. 1666 */ 1667 var backwards = (this._yoyo && (prevCycle & 1) !== 0), 1668 wrap = (backwards === (this._yoyo && (this._cycle & 1) !== 0)), 1669 recTotalTime = this._totalTime, 1670 recCycle = this._cycle, 1671 recRawPrevTime = this._rawPrevTime, 1672 recTime = this._time; 1673 1674 this._totalTime = prevCycle * dur; 1675 if (this._cycle < prevCycle) { 1676 backwards = !backwards; 1677 } else { 1678 this._totalTime += dur; 1679 } 1680 this._time = prevTime; //temporarily revert _time so that render() renders the children in the correct order. Without this, tweens won't rewind correctly. We could arhictect things in a "cleaner" way by splitting out the rendering queue into a separate method but for performance reasons, we kept it all inside this method. 1681 1682 this._rawPrevTime = (dur === 0) ? prevRawPrevTime - 0.0001 : prevRawPrevTime; 1683 this._cycle = prevCycle; 1684 this._locked = true; //prevents changes to totalTime and skips repeat/yoyo behavior when we recursively call render() 1685 prevTime = (backwards) ? 0 : dur; 1686 this.render(prevTime, suppressEvents, (dur === 0)); 1687 if (!suppressEvents) if (!this._gc) { 1688 if (this.vars.onRepeat) { 1689 this._cycle = recCycle; //in case the onRepeat alters the playhead or invalidates(), we shouldn't stay locked or use the previous cycle. 1690 this._locked = false; 1691 this._callback("onRepeat"); 1692 } 1693 } 1694 if (prevTime !== this._time) { //in case there's a callback like onComplete in a nested tween/timeline that changes the playhead position, like via seek(), we should just abort. 1695 return; 1696 } 1697 if (wrap) { 1698 this._cycle = prevCycle; //if there's an onRepeat, we reverted this above, so make sure it's set properly again. We also unlocked in that scenario, so reset that too. 1699 this._locked = true; 1700 prevTime = (backwards) ? dur + 0.0001 : -0.0001; 1701 this.render(prevTime, true, false); 1702 } 1703 this._locked = false; 1704 if (this._paused && !prevPaused) { //if the render() triggered callback that paused this timeline, we should abort (very rare, but possible) 1705 return; 1706 } 1707 this._time = recTime; 1708 this._totalTime = recTotalTime; 1709 this._cycle = recCycle; 1710 this._rawPrevTime = recRawPrevTime; 1711 } 1712 1713 if ((this._time === prevTime || !this._first) && !force && !internalForce && !pauseTween) { 1714 if (prevTotalTime !== this._totalTime) if (this._onUpdate) if (!suppressEvents) { //so that onUpdate fires even during the repeatDelay - as long as the totalTime changed, we should trigger onUpdate. 1715 this._callback("onUpdate"); 1716 } 1717 return; 1718 } else if (!this._initted) { 1719 this._initted = true; 1720 } 1721 1722 if (!this._active) if (!this._paused && this._totalTime !== prevTotalTime && time > 0) { 1723 this._active = true; //so that if the user renders the timeline (as opposed to the parent timeline rendering it), it is forced to re-render and align it with the proper time/frame on the next rendering cycle. Maybe the timeline already finished but the user manually re-renders it as halfway done, for example. 1724 } 1725 1726 if (prevTotalTime === 0) if (this.vars.onStart) if (this._totalTime !== 0 || !this._totalDuration) if (!suppressEvents) { 1727 this._callback("onStart"); 1728 } 1729 1730 curTime = this._time; 1731 if (curTime >= prevTime) { 1732 tween = this._first; 1733 while (tween) { 1734 next = tween._next; //record it here because the value could change after rendering... 1735 if (curTime !== this._time || (this._paused && !prevPaused)) { //in case a tween pauses or seeks the timeline when rendering, like inside of an onUpdate/onComplete 1736 break; 1737 } else if (tween._active || (tween._startTime <= this._time && !tween._paused && !tween._gc)) { 1738 if (pauseTween === tween) { 1739 this.pause(); 1740 } 1741 if (!tween._reversed) { 1742 tween.render((time - tween._startTime) * tween._timeScale, suppressEvents, force); 1743 } else { 1744 tween.render(((!tween._dirty) ? tween._totalDuration : tween.totalDuration()) - ((time - tween._startTime) * tween._timeScale), suppressEvents, force); 1745 } 1746 } 1747 tween = next; 1748 } 1749 } else { 1750 tween = this._last; 1751 while (tween) { 1752 next = tween._prev; //record it here because the value could change after rendering... 1753 if (curTime !== this._time || (this._paused && !prevPaused)) { //in case a tween pauses or seeks the timeline when rendering, like inside of an onUpdate/onComplete 1754 break; 1755 } else if (tween._active || (tween._startTime <= prevTime && !tween._paused && !tween._gc)) { 1756 if (pauseTween === tween) { 1757 pauseTween = tween._prev; //the linked list is organized by _startTime, thus it's possible that a tween could start BEFORE the pause and end after it, in which case it would be positioned before the pause tween in the linked list, but we should render it before we pause() the timeline and cease rendering. This is only a concern when going in reverse. 1758 while (pauseTween && pauseTween.endTime() > this._time) { 1759 pauseTween.render( (pauseTween._reversed ? pauseTween.totalDuration() - ((time - pauseTween._startTime) * pauseTween._timeScale) : (time - pauseTween._startTime) * pauseTween._timeScale), suppressEvents, force); 1760 pauseTween = pauseTween._prev; 1761 } 1762 pauseTween = null; 1763 this.pause(); 1764 } 1765 if (!tween._reversed) { 1766 tween.render((time - tween._startTime) * tween._timeScale, suppressEvents, force); 1767 } else { 1768 tween.render(((!tween._dirty) ? tween._totalDuration : tween.totalDuration()) - ((time - tween._startTime) * tween._timeScale), suppressEvents, force); 1769 } 1770 } 1771 tween = next; 1772 } 1773 } 1774 1775 if (this._onUpdate) if (!suppressEvents) { 1776 if (_lazyTweens.length) { //in case rendering caused any tweens to lazy-init, we should render them because typically when a timeline finishes, users expect things to have rendered fully. Imagine an onUpdate on a timeline that reports/checks tweened values. 1777 _lazyRender(); 1778 }
1779 this._callback("onUpdate"); 1780 } 1781 if (callback) if (!this._locked) if (!this._gc) if (prevStart === this._startTime || prevTimeScale !== this._timeScale) if (this._time === 0 || totalDur >= this.totalDuration()) { //if one of the tweens that was rendered altered this timeline's startTime (like if an onComplete reversed the timeline), it probably isn't complete. If it is, don't worry, because whatever call altered the startTime would complete if it was necessary at the new time. The only exception is the timeScale property. Also check _gc because there's a chance that kill() could be called in an onUpdate 1782 if (isComplete) { 1783 if (_lazyTweens.length) { //in case rendering caused any tweens to lazy-init, we should render them because typically when a timeline finishes, users expect things to have rendered fully. Imagine an onComplete on a timeline that reports/checks tweened values. 1784 _lazyRender(); 1785 } 1786 if (this._timeline.autoRemoveChildren) { 1787 this._enabled(false, false); 1788 } 1789 this._active = false; 1790 } 1791 if (!suppressEvents && this.vars[callback]) { 1792 this._callback(callback); 1793 } 1794 } 1795 }; 1796 1797 p.getActive = function(nested, tweens, timelines) { 1798 if (nested == null) { 1799 nested = true; 1800 } 1801 if (tweens == null) { 1802 tweens = true; 1803 } 1804 if (timelines == null) { 1805 timelines = false; 1806 } 1807 var a = [], 1808 all = this.getChildren(nested, tweens, timelines), 1809 cnt = 0, 1810 l = all.length, 1811 i, tween; 1812 for (i = 0; i < l; i++) { 1813 tween = all[i]; 1814 if (tween.isActive()) { 1815 a[cnt++] = tween; 1816 } 1817 } 1818 return a; 1819 }; 1820 1821 1822 p.getLabelAfter = function(time) { 1823 if (!time) if (time !== 0) { //faster than isNan() 1824 time = this._time; 1825 } 1826 var labels = this.getLabelsArray(), 1827 l = labels.length, 1828 i; 1829 for (i = 0; i < l; i++) { 1830 if (labels[i].time > time) { 1831 return labels[i].name; 1832 } 1833 } 1834 return null; 1835 }; 1836 1837 p.getLabelBefore = function(time) { 1838 if (time == null) { 1839 time = this._time; 1840 } 1841 var labels = this.getLabelsArray(), 1842 i = labels.length; 1843 while (--i > -1) { 1844 if (labels[i].time < time) { 1845 return labels[i].name; 1846 } 1847 } 1848 return null; 1849 }; 1850 1851 p.getLabelsArray = function() { 1852 var a = [], 1853 cnt = 0, 1854 p; 1855 for (p in this._labels) { 1856 a[cnt++] = {time:this._labels[p], name:p}; 1857 } 1858 a.sort(function(a,b) { 1859 return a.time - b.time; 1860 }); 1861 return a; 1862 }; 1863 1864 p.invalidate = function() { 1865 this._locked = false; //unlock and set cycle in case invalidate() is called from inside an onRepeat 1866 return TimelineLite.prototype.invalidate.call(this); 1867 }; 1868 1869 1870//---- GETTERS / SETTERS ------------------------------------------------------------------------------------------------------- 1871 1872 p.progress = function(value, suppressEvents) { 1873 return (!arguments.length) ? (this._time / this.duration()) || 0 : this.totalTime( this.duration() * ((this._yoyo && (this._cycle & 1) !== 0) ? 1 - value : value) + (this._cycle * (this._duration + this._repeatDelay)), suppressEvents); 1874 }; 1875 1876 p.totalProgress = function(value, suppressEvents) { 1877 return (!arguments.length) ? (this._totalTime / this.totalDuration()) || 0 : this.totalTime( this.totalDuration() * value, suppressEvents); 1878 }; 1879 1880 p.totalDuration = function(value) { 1881 if (!arguments.length) { 1882 if (this._dirty) { 1883 TimelineLite.prototype.totalDuration.call(this); //just forces refresh 1884 //Instead of Infinity, we use 999999999999 so that we can accommodate reverses. 1885 this._totalDuration = (this._repeat === -1) ? 999999999999 : this._duration * (this._repeat + 1) + (this._repeatDelay * this._repeat); 1886 } 1887 return this._totalDuration; 1888 } 1889 return (this._repeat === -1 || !value) ? this : this.timeScale( this.totalDuration() / value ); 1890 }; 1891 1892 p.time = function(value, suppressEvents) { 1893 if (!arguments.length) { 1894 return this._time; 1895 } 1896 if (this._dirty) { 1897 this.totalDuration(); 1898 } 1899 if (value > this._duration) { 1900 value = this._duration; 1901 } 1902 if (this._yoyo && (this._cycle & 1) !== 0) { 1903 value = (this._duration - value) + (this._cycle * (this._duration + this._repeatDelay)); 1904 } else if (this._repeat !== 0) { 1905 value += this._cycle * (this._duration + this._repeatDelay); 1906 } 1907 return this.totalTime(value, suppressEvents); 1908 }; 1909 1910 p.repeat = function(value) { 1911 if (!arguments.length) { 1912 return this._repeat; 1913 } 1914 this._repeat = value; 1915 return this._uncache(true); 1916 }; 1917 1918 p.repeatDelay = function(value) { 1919 if (!arguments.length) { 1920 return this._repeatDelay; 1921 } 1922 this._repeatDelay = value; 1923 return this._uncache(true); 1924 }; 1925 1926 p.yoyo = function(value) { 1927 if (!arguments.length) { 1928 return this._yoyo; 1929 } 1930 this._yoyo = value; 1931 return this; 1932 }; 1933 1934 p.currentLabel = function(value) { 1935 if (!arguments.length) { 1936 return this.getLabelBefore(this._time + 0.00000001); 1937 } 1938 return this.seek(value, true); 1939 }; 1940 1941 return TimelineMax; 1942 1943 }, true); 1944 1945 1946 1947 1948 1949 1950 1951 1952 1953 1954 1955 1956/* 1957 * ---------------------------------------------------------------- 1958 * BezierPlugin 1959 * ---------------------------------------------------------------- 1960 */ 1961 (function() { 1962 1963 var _RAD2DEG = 180 / Math.PI, 1964 _r1 = [], 1965 _r2 = [], 1966 _r3 = [], 1967 _corProps = {}, 1968 _globals = _gsScope._gsDefine.globals, 1969 Segment = function(a, b, c, d) { 1970 if (c === d) { //if c and d match, the final autoRotate value could lock at -90 degrees, so differentiate them slightly. 1971 c = d - (d - b) / 1000000; 1972 } 1973 if (a === b) { //if a and b match, the starting autoRotate value could lock at -90 degrees, so differentiate them slightly. 1974 b = a + (c - a) / 1000000; 1975 } 1976 this.a = a; 1977 this.b = b; 1978 this.c = c; 1979 this.d = d; 1980 this.da = d - a; 1981 this.ca = c - a; 1982 this.ba = b - a; 1983 }, 1984 _correlate = ",x,y,z,left,top,right,bottom,marginTop,marginLeft,marginRight,marginBottom,paddingLeft,paddingTop,paddingRight,paddingBottom,backgroundPosition,backgroundPosition_y,", 1985 cubicToQuadratic = function(a, b, c, d) { 1986 var q1 = {a:a}, 1987 q2 = {}, 1988 q3 = {}, 1989 q4 = {c:d}, 1990 mab = (a + b) / 2, 1991 mbc = (b + c) / 2, 1992 mcd = (c + d) / 2, 1993 mabc = (mab + mbc) / 2, 1994 mbcd = (mbc + mcd) / 2, 1995 m8 = (mbcd - mabc) / 8; 1996 q1.b = mab + (a - mab) / 4; 1997 q2.b = mabc + m8; 1998 q1.c = q2.a = (q1.b + q2.b) / 2; 1999 q2.c = q3.a = (mabc + mbcd) / 2; 2000 q3.b = mbcd - m8; 2001 q4.b = mcd + (d - mcd) / 4; 2002 q3.c = q4.a = (q3.b + q4.b) / 2; 2003 return [q1, q2, q3, q4]; 2004 }, 2005 _calculateControlPoints = function(a, curviness, quad, basic, correlate) { 2006 var l = a.length - 1, 2007 ii = 0, 2008 cp1 = a[0].a, 2009 i, p1, p2, p3, seg, m1, m2, mm, cp2, qb, r1, r2, tl; 2010 for (i = 0; i < l; i++) { 2011 seg = a[ii]; 2012 p1 = seg.a; 2013 p2 = seg.d; 2014 p3 = a[ii+1].d; 2015 2016 if (correlate) { 2017 r1 = _r1[i]; 2018 r2 = _r2[i]; 2019 tl = ((r2 + r1) * curviness * 0.25) / (basic ? 0.5 : _r3[i] || 0.5); 2020 m1 = p2 - (p2 - p1) * (basic ? curviness * 0.5 : (r1 !== 0 ? tl / r1 : 0)); 2021 m2 = p2 + (p3 - p2) * (basic ? curviness * 0.5 : (r2 !== 0 ? tl / r2 : 0)); 2022 mm = p2 - (m1 + (((m2 - m1) * ((r1 * 3 / (r1 + r2)) + 0.5) / 4) || 0)); 2023 } else { 2024 m1 = p2 - (p2 - p1) * curviness * 0.5; 2025 m2 = p2 + (p3 - p2) * curviness * 0.5; 2026 mm = p2 - (m1 + m2) / 2; 2027 } 2028 m1 += mm; 2029 m2 += mm; 2030 2031 seg.c = cp2 = m1; 2032 if (i !== 0) { 2033 seg.b = cp1; 2034 } else { 2035 seg.b = cp1 = seg.a + (seg.c - seg.a) * 0.6; //instead of placing b on a exactly, we move it inline with c so that if the user specifies an ease like Back.easeIn or Elastic.easeIn which goes BEYOND the beginning, it will do so smoothly. 2036 } 2037 2038 seg.da = p2 - p1; 2039 seg.ca = cp2 - p1; 2040 seg.ba = cp1 - p1; 2041 2042 if (quad) { 2043 qb = cubicToQuadratic(p1, cp1, cp2, p2); 2044 a.splice(ii, 1, qb[0], qb[1], qb[2], qb[3]); 2045 ii += 4; 2046 } else { 2047 ii++; 2048 } 2049 2050 cp1 = m2; 2051 } 2052 seg = a[ii]; 2053 seg.b = cp1; 2054 seg.c = cp1 + (seg.d - cp1) * 0.4; //instead of placing c on d exactly, we move it inline with b so that if the user specifies an ease like Back.easeOut or Elastic.easeOut which goes BEYOND the end, it will do so smoothly. 2055 seg.da = seg.d - seg.a; 2056 seg.ca = seg.c - seg.a; 2057 seg.ba = cp1 - seg.a; 2058 if (quad) { 2059 qb = cubicToQuadratic(seg.a, cp1, seg.c, seg.d); 2060 a.splice(ii, 1, qb[0], qb[1], qb[2], qb[3]); 2061 } 2062 }, 2063 _parseAnchors = function(values, p, correlate, prepend) { 2064 var a = [], 2065 l, i, p1, p2, p3, tmp; 2066 if (prepend) {
2067 values = [prepend].concat(values); 2068 i = values.length; 2069 while (--i > -1) { 2070 if (typeof( (tmp = values[i][p]) ) === "string") if (tmp.charAt(1) === "=") { 2071 values[i][p] = prepend[p] + Number(tmp.charAt(0) + tmp.substr(2)); //accommodate relative values. Do it inline instead of breaking it out into a function for speed reasons 2072 } 2073 } 2074 } 2075 l = values.length - 2; 2076 if (l < 0) { 2077 a[0] = new Segment(values[0][p], 0, 0, values[0][p]); 2078 return a; 2079 } 2080 for (i = 0; i < l; i++) { 2081 p1 = values[i][p]; 2082 p2 = values[i+1][p]; 2083 a[i] = new Segment(p1, 0, 0, p2); 2084 if (correlate) { 2085 p3 = values[i+2][p]; 2086 _r1[i] = (_r1[i] || 0) + (p2 - p1) * (p2 - p1); 2087 _r2[i] = (_r2[i] || 0) + (p3 - p2) * (p3 - p2); 2088 } 2089 } 2090 a[i] = new Segment(values[i][p], 0, 0, values[i+1][p]); 2091 return a; 2092 }, 2093 bezierThrough = function(values, curviness, quadratic, basic, correlate, prepend) { 2094 var obj = {}, 2095 props = [], 2096 first = prepend || values[0], 2097 i, p, a, j, r, l, seamless, last; 2098 correlate = (typeof(correlate) === "string") ? ","+correlate+"," : _correlate; 2099 if (curviness == null) { 2100 curviness = 1; 2101 } 2102 for (p in values[0]) { 2103 props.push(p); 2104 } 2105 //check to see if the last and first values are identical (well, within 0.05). If so, make seamless by appending the second element to the very end of the values array and the 2nd-to-last element to the very beginning (we'll remove those segments later) 2106 if (values.length > 1) { 2107 last = values[values.length - 1]; 2108 seamless = true; 2109 i = props.length; 2110 while (--i > -1) { 2111 p = props[i]; 2112 if (Math.abs(first[p] - last[p]) > 0.05) { //build in a tolerance of +/-0.05 to accommodate rounding errors. 2113 seamless = false; 2114 break; 2115 } 2116 } 2117 if (seamless) { 2118 values = values.concat(); //duplicate the array to avoid contaminating the original which the user may be reusing for other tweens 2119 if (prepend) { 2120 values.unshift(prepend); 2121 } 2122 values.push(values[1]); 2123 prepend = values[values.length - 3]; 2124 } 2125 } 2126 _r1.length = _r2.length = _r3.length = 0; 2127 i = props.length; 2128 while (--i > -1) { 2129 p = props[i]; 2130 _corProps[p] = (correlate.indexOf(","+p+",") !== -1); 2131 obj[p] = _parseAnchors(values, p, _corProps[p], prepend); 2132 } 2133 i = _r1.length; 2134 while (--i > -1) { 2135 _r1[i] = Math.sqrt(_r1[i]); 2136 _r2[i] = Math.sqrt(_r2[i]); 2137 } 2138 if (!basic) { 2139 i = props.length; 2140 while (--i > -1) { 2141 if (_corProps[p]) { 2142 a = obj[props[i]]; 2143 l = a.length - 1; 2144 for (j = 0; j < l; j++) { 2145 r = (a[j+1].da / _r2[j] + a[j].da / _r1[j]) || 0; 2146 _r3[j] = (_r3[j] || 0) + r * r; 2147 } 2148 } 2149 } 2150 i = _r3.length; 2151 while (--i > -1) { 2152 _r3[i] = Math.sqrt(_r3[i]); 2153 } 2154 } 2155 i = props.length; 2156 j = quadratic ? 4 : 1; 2157 while (--i > -1) { 2158 p = props[i]; 2159 a = obj[p]; 2160 _calculateControlPoints(a, curviness, quadratic, basic, _corProps[p]); //this method requires that _parseAnchors() and _setSegmentRatios() ran first so that _r1, _r2, and _r3 values are populated for all properties 2161 if (seamless) { 2162 a.splice(0, j); 2163 a.splice(a.length - j, j); 2164 } 2165 } 2166 return obj; 2167 }, 2168 _parseBezierData = function(values, type, prepend) { 2169 type = type || "soft"; 2170 var obj = {}, 2171 inc = (type === "cubic") ? 3 : 2, 2172 soft = (type === "soft"), 2173 props = [], 2174 a, b, c, d, cur, i, j, l, p, cnt, tmp; 2175 if (soft && prepend) { 2176 values = [prepend].concat(values); 2177 } 2178 if (values == null || values.length < inc + 1) { throw "invalid Bezier data"; } 2179 for (p in values[0]) { 2180 props.push(p); 2181 } 2182 i = props.length; 2183 while (--i > -1) { 2184 p = props[i]; 2185 obj[p] = cur = []; 2186 cnt = 0; 2187 l = values.length; 2188 for (j = 0; j < l; j++) { 2189 a = (prepend == null) ? values[j][p] : (typeof( (tmp = values[j][p]) ) === "string" && tmp.charAt(1) === "=") ? prepend[p] + Number(tmp.charAt(0) + tmp.substr(2)) : Number(tmp); 2190 if (soft) if (j > 1) if (j < l - 1) { 2191 cur[cnt++] = (a + cur[cnt-2]) / 2; 2192 } 2193 cur[cnt++] = a; 2194 } 2195 l = cnt - inc + 1; 2196 cnt = 0; 2197 for (j = 0; j < l; j += inc) { 2198 a = cur[j]; 2199 b = cur[j+1]; 2200 c = cur[j+2]; 2201 d = (inc === 2) ? 0 : cur[j+3]; 2202 cur[cnt++] = tmp = (inc === 3) ? new Segment(a, b, c, d) : new Segment(a, (2 * b + a) / 3, (2 * b + c) / 3, c); 2203 } 2204 cur.length = cnt; 2205 } 2206 return obj; 2207 }, 2208 _addCubicLengths = function(a, steps, resolution) { 2209 var inc = 1 / resolution, 2210 j = a.length, 2211 d, d1, s, da, ca, ba, p, i, inv, bez, index; 2212 while (--j > -1) { 2213 bez = a[j]; 2214 s = bez.a; 2215 da = bez.d - s; 2216 ca = bez.c - s; 2217 ba = bez.b - s; 2218 d = d1 = 0; 2219 for (i = 1; i <= resolution; i++) { 2220 p = inc * i; 2221 inv = 1 - p; 2222 d = d1 - (d1 = (p * p * da + 3 * inv * (p * ca + inv * ba)) * p); 2223 index = j * resolution + i - 1; 2224 steps[index] = (steps[index] || 0) + d * d; 2225 } 2226 } 2227 }, 2228 _parseLengthData = function(obj, resolution) { 2229 resolution = resolution >> 0 || 6; 2230 var a = [], 2231 lengths = [], 2232 d = 0, 2233 total = 0, 2234 threshold = resolution - 1, 2235 segments = [], 2236 curLS = [], //current length segments array 2237 p, i, l, index; 2238 for (p in obj) { 2239 _addCubicLengths(obj[p], a, resolution); 2240 } 2241 l = a.length; 2242 for (i = 0; i < l; i++) { 2243 d += Math.sqrt(a[i]); 2244 index = i % resolution; 2245 curLS[index] = d; 2246 if (index === threshold) { 2247 total += d; 2248 index = (i / resolution) >> 0; 2249 segments[index] = curLS; 2250 lengths[index] = total; 2251 d = 0; 2252 curLS = []; 2253 } 2254 } 2255 return {length:total, lengths:lengths, segments:segments}; 2256 }, 2257 2258 2259 2260 BezierPlugin = _gsScope._gsDefine.plugin({ 2261 propName: "bezier", 2262 priority: -1, 2263 version: "1.3.8", 2264 API: 2, 2265 global:true, 2266 2267 //gets called when the tween renders for the first time. This is where initial values should be recorded and any setup routines should run. 2268 init: function(target, vars, tween) { 2269 this._target = target; 2270 if (vars instanceof Array) { 2271 vars = {values:vars}; 2272 } 2273 this._func = {}; 2274 this._mod = {}; 2275 this._props = []; 2276 this._timeRes = (vars.timeResolution == null) ? 6 : parseInt(vars.timeResolution, 10); 2277 var values = vars.values || [], 2278 first = {}, 2279 second = values[0], 2280 autoRotate = vars.autoRotate || tween.vars.orientToBezier, 2281 p, isFunc, i, j, prepend; 2282 2283 this._autoRotate = autoRotate ? (autoRotate instanceof Array) ? autoRotate : [["x","y","rotation",((autoRotate === true) ? 0 : Number(autoRotate) || 0)]] : null; 2284 for (p in second) { 2285 this._props.push(p); 2286 } 2287 2288 i = this._props.length; 2289 while (--i > -1) { 2290 p = this._props[i]; 2291 2292 this._overwriteProps.push(p); 2293 isFunc = this._func[p] = (typeof(target[p]) === "function"); 2294 first[p] = (!isFunc) ? parseFloat(target[p]) : target[ ((p.indexOf("set") || typeof(target["get" + p.substr(3)]) !== "function") ? p : "get" + p.substr(3)) ](); 2295 if (!prepend) if (first[p] !== values[0][p]) { 2296 prepend = first; 2297 } 2298 } 2299 this._beziers = (vars.type !== "cubic" && vars.type !== "quadratic" && vars.type !== "soft") ? bezierThrough(values, isNaN(vars.curviness) ? 1 : vars.curviness, false, (vars.type === "thruBasic"), vars.correlate, prepend) : _parseBezierData(values, vars.type, first); 2300 this._segCount = this._beziers[p].length; 2301 2302 if (this._timeRes) { 2303 var ld = _parseLengthData(this._beziers, this._timeRes); 2304 this._length = ld.length; 2305 this._lengths = ld.lengths; 2306 this._segments = ld.segments; 2307 this._l1 = this._li = this._s1 = this._si = 0; 2308 this._l2 = this._lengths[0]; 2309 this._curSeg = this._segments[0]; 2310 this._s2 = this._curSeg[0]; 2311 this._prec = 1 / this._curSeg.length; 2312 } 2313 2314 if ((autoRotate = this._autoRotate)) { 2315 this._initialRotations = []; 2316 if (!(autoRotate[0] instanceof Array)) { 2317 this._autoRotate = autoRotate = [autoRotate]; 2318 } 2319 i = autoRotate.length; 2320 while (--i > -1) { 2321 for (j = 0; j < 3; j++) { 2322 p = autoRotate[i][j]; 2323 this._func[p] = (typeof(target[p]) === "function") ? target[ ((p.indexOf("set") || typeof(target["get" + p.substr(3)]) !== "function") ? p : "get" + p.substr(3)) ] : false;
2324 } 2325 p = autoRotate[i][2]; 2326 this._initialRotations[i] = (this._func[p] ? this._func[p].call(this._target) : this._target[p]) || 0; 2327 this._overwriteProps.push(p); 2328 } 2329 } 2330 this._startRatio = tween.vars.runBackwards ? 1 : 0; //we determine the starting ratio when the tween inits which is always 0 unless the tween has runBackwards:true (indicating it's a from() tween) in which case it's 1. 2331 return true; 2332 }, 2333 2334 //called each time the values should be updated, and the ratio gets passed as the only parameter (typically it's a value between 0 and 1, but it can exceed those when using an ease like Elastic.easeOut or Back.easeOut, etc.) 2335 set: function(v) { 2336 var segments = this._segCount, 2337 func = this._func, 2338 target = this._target, 2339 notStart = (v !== this._startRatio), 2340 curIndex, inv, i, p, b, t, val, l, lengths, curSeg; 2341 if (!this._timeRes) { 2342 curIndex = (v < 0) ? 0 : (v >= 1) ? segments - 1 : (segments * v) >> 0; 2343 t = (v - (curIndex * (1 / segments))) * segments; 2344 } else { 2345 lengths = this._lengths; 2346 curSeg = this._curSeg; 2347 v *= this._length; 2348 i = this._li; 2349 //find the appropriate segment (if the currently cached one isn't correct) 2350 if (v > this._l2 && i < segments - 1) { 2351 l = segments - 1; 2352 while (i < l && (this._l2 = lengths[++i]) <= v) { } 2353 this._l1 = lengths[i-1]; 2354 this._li = i; 2355 this._curSeg = curSeg = this._segments[i]; 2356 this._s2 = curSeg[(this._s1 = this._si = 0)]; 2357 } else if (v < this._l1 && i > 0) { 2358 while (i > 0 && (this._l1 = lengths[--i]) >= v) { } 2359 if (i === 0 && v < this._l1) { 2360 this._l1 = 0; 2361 } else { 2362 i++; 2363 } 2364 this._l2 = lengths[i]; 2365 this._li = i; 2366 this._curSeg = curSeg = this._segments[i]; 2367 this._s1 = curSeg[(this._si = curSeg.length - 1) - 1] || 0; 2368 this._s2 = curSeg[this._si]; 2369 } 2370 curIndex = i; 2371 //now find the appropriate sub-segment (we split it into the number of pieces that was defined by "precision" and measured each one) 2372 v -= this._l1; 2373 i = this._si; 2374 if (v > this._s2 && i < curSeg.length - 1) { 2375 l = curSeg.length - 1; 2376 while (i < l && (this._s2 = curSeg[++i]) <= v) { } 2377 this._s1 = curSeg[i-1]; 2378 this._si = i; 2379 } else if (v < this._s1 && i > 0) { 2380 while (i > 0 && (this._s1 = curSeg[--i]) >= v) { } 2381 if (i === 0 && v < this._s1) { 2382 this._s1 = 0; 2383 } else { 2384 i++; 2385 } 2386 this._s2 = curSeg[i]; 2387 this._si = i; 2388 } 2389 t = ((i + (v - this._s1) / (this._s2 - this._s1)) * this._prec) || 0; 2390 } 2391 inv = 1 - t; 2392 2393 i = this._props.length; 2394 while (--i > -1) { 2395 p = this._props[i]; 2396 b = this._beziers[p][curIndex]; 2397 val = (t * t * b.da + 3 * inv * (t * b.ca + inv * b.ba)) * t + b.a; 2398 if (this._mod[p]) { 2399 val = this._mod[p](val, target); 2400 } 2401 if (func[p]) { 2402 target[p](val); 2403 } else { 2404 target[p] = val; 2405 } 2406 } 2407 2408 if (this._autoRotate) { 2409 var ar = this._autoRotate, 2410 b2, x1, y1, x2, y2, add, conv; 2411 i = ar.length; 2412 while (--i > -1) { 2413 p = ar[i][2]; 2414 add = ar[i][3] || 0; 2415 conv = (ar[i][4] === true) ? 1 : _RAD2DEG; 2416 b = this._beziers[ar[i][0]]; 2417 b2 = this._beziers[ar[i][1]]; 2418 2419 if (b && b2) { //in case one of the properties got overwritten. 2420 b = b[curIndex]; 2421 b2 = b2[curIndex]; 2422 2423 x1 = b.a + (b.b - b.a) * t; 2424 x2 = b.b + (b.c - b.b) * t; 2425 x1 += (x2 - x1) * t; 2426 x2 += ((b.c + (b.d - b.c) * t) - x2) * t; 2427 2428 y1 = b2.a + (b2.b - b2.a) * t; 2429 y2 = b2.b + (b2.c - b2.b) * t; 2430 y1 += (y2 - y1) * t; 2431 y2 += ((b2.c + (b2.d - b2.c) * t) - y2) * t; 2432 2433 val = notStart ? Math.atan2(y2 - y1, x2 - x1) * conv + add : this._initialRotations[i]; 2434 2435 if (this._mod[p]) { 2436 val = this._mod[p](val, target); //for modProps 2437 } 2438 2439 if (func[p]) { 2440 target[p](val); 2441 } else { 2442 target[p] = val; 2443 } 2444 } 2445 } 2446 } 2447 } 2448 }), 2449 p = BezierPlugin.prototype; 2450 2451 2452 BezierPlugin.bezierThrough = bezierThrough; 2453 BezierPlugin.cubicToQuadratic = cubicToQuadratic; 2454 BezierPlugin._autoCSS = true; //indicates that this plugin can be inserted into the "css" object using the autoCSS feature of TweenLite 2455 BezierPlugin.quadraticToCubic = function(a, b, c) { 2456 return new Segment(a, (2 * b + a) / 3, (2 * b + c) / 3, c); 2457 }; 2458 2459 BezierPlugin._cssRegister = function() { 2460 var CSSPlugin = _globals.CSSPlugin; 2461 if (!CSSPlugin) { 2462 return; 2463 } 2464 var _internals = CSSPlugin._internals, 2465 _parseToProxy = _internals._parseToProxy, 2466 _setPluginRatio = _internals._setPluginRatio, 2467 CSSPropTween = _internals.CSSPropTween; 2468 _internals._registerComplexSpecialProp("bezier", {parser:function(t, e, prop, cssp, pt, plugin) { 2469 if (e instanceof Array) { 2470 e = {values:e}; 2471 } 2472 plugin = new BezierPlugin(); 2473 var values = e.values, 2474 l = values.length - 1, 2475 pluginValues = [], 2476 v = {}, 2477 i, p, data; 2478 if (l < 0) { 2479 return pt; 2480 } 2481 for (i = 0; i <= l; i++) { 2482 data = _parseToProxy(t, values[i], cssp, pt, plugin, (l !== i)); 2483 pluginValues[i] = data.end; 2484 } 2485 for (p in e) { 2486 v[p] = e[p]; //duplicate the vars object because we need to alter some things which w
2486ould cause problems if the user plans to reuse the same vars object for another tween. 2487 } 2488 v.values = pluginValues; 2489 pt = new CSSPropTween(t, "bezier", 0, 0, data.pt, 2); 2490 pt.data = data; 2491 pt.plugin = plugin; 2492 pt.setRatio = _setPluginRatio; 2493 if (v.autoRotate === 0) { 2494 v.autoRotate = true; 2495 } 2496 if (v.autoRotate && !(v.autoRotate instanceof Array)) { 2497 i = (v.autoRotate === true) ? 0 : Number(v.autoRotate); 2498 v.autoRotate = (data.end.left != null) ? [["left","top","rotation",i,false]] : (data.end.x != null) ? [["x","y","rotation",i,false]] : false; 2499 } 2500 if (v.autoRotate) { 2501 if (!cssp._transform) { 2502 cssp._enableTransforms(false); 2503 } 2504 data.autoRotate = cssp._target._gsTransform; 2505 data.proxy.rotation = data.autoRotate.rotation || 0; 2506 cssp._overwriteProps.push("rotation"); 2507 } 2508 plugin._onInitTween(data.proxy, v, cssp._tween); 2509 return pt; 2510 }}); 2511 }; 2512 2513 p._mod = function(lookup) { 2514 var op = this._overwriteProps, 2515 i = op.length, 2516 val; 2517 while (--i > -1) { 2518 val = lookup[op[i]]; 2519 if (val && typeof(val) === "function") { 2520 this._mod[op[i]] = val; 2521 } 2522 } 2523 }; 2524 2525 p._kill = function(lookup) { 2526 var a = this._props, 2527 p, i; 2528 for (p in this._beziers) { 2529 if (p in lookup) { 2530 delete this._beziers[p]; 2531 delete this._func[p]; 2532 i = a.length; 2533 while (--i > -1) { 2534 if (a[i] === p) { 2535 a.splice(i, 1); 2536 } 2537 } 2538 } 2539 } 2540 a = this._autoRotate; 2541 if (a) { 2542 i = a.length; 2543 while (--i > -1) { 2544 if (lookup[a[i][2]]) { 2545 a.splice(i, 1); 2546 } 2547 } 2548 } 2549 return this._super._kill.call(this, lookup); 2550 }; 2551 2552 }()); 2553 2554 2555 2556 2557 2558 2559 2560 2561 2562 2563 2564 2565 2566 2567/* 2568 * ---------------------------------------------------------------- 2569 * CSSPlugin 2570 * ---------------------------------------------------------------- 2571 */ 2572 _gsScope._gsDefine("plugins.CSSPlugin", ["plugins.TweenPlugin","TweenLite"], function(TweenPlugin, TweenLite) { 2573 2574 /** @constructor **/ 2575 var CSSPlugin = function() { 2576 TweenPlugin.call(this, "css"); 2577 this._overwriteProps.length = 0; 2578 this.setRatio = CSSPlugin.prototype.setRatio; //speed optimization (avoid prototype lookup on this "hot" method) 2579 }, 2580 _globals = _gsScope._gsDefine.globals, 2581 _hasPriority, //turns true whenever a CSSPropTween instance is created that has a priority other than 0. This helps us discern whether or not we should spend the time organizing the linked list or not after a CSSPlugin's _onInitTween() method is called. 2582 _suffixMap, //we set this in _onInitTween() each time as a way to have a persistent variable we can use in other methods like _parse() without having to pass it around as a parameter and we keep _parse() decoupled from a particular CSSPlugin instance 2583 _cs, //computed style (we store this in a shared variable to conserve memory and make minification tighter 2584 _overwriteProps, //alias to the currently instantiating CSSPlugin's _overwriteProps array. We use this closure in order to avoid having to pass a reference around from method to method and aid in minification. 2585 _specialProps = {}, 2586 p = CSSPlugin.prototype = new TweenPlugin("css"); 2587 2588 p.constructor = CSSPlugin; 2589 CSSPlugin.version = "2.0.2"; 2590 CSSPlugin.API = 2; 2591 CSSPlugin.defaultTransformPerspective = 0; 2592 CSSPlugin.defaultSkewType = "compensated"; 2593 CSSPlugin.defaultSmoothOrigin = true; 2594 p = "px"; //we'll reuse the "p" variable to keep file size down 2595 CSSPlugin.suffixMap = {top:p, right:p, bottom:p, left:p, width:p, height:p, fontSize:p, padding:p, margin:p, perspective:p, lineHeight:""}; 2596 2597 2598 var _numExp = /(?:\-|\.|\b)(\d|\.|e\-)+/g, 2599 _relNumExp = /(?:\d|\-\d|\.\d|\-\.\d|\+=\d|\-=\d|\+=.\d|\-=\.\d)+/g, 2600 _valuesExp = /(?:\+=|\-=|\-|\b)[\d\-\.]+[a-zA-Z0-9]*(?:%|\b)/gi, //finds all the values that begin with numbers or += or -= and then a number. Includes suffixes. We use this to split complex values apart like "1px 5px 20px rgb(255,102,51)" 2601 _NaNExp = /(?![+-]?\d*\.?\d+|[+-]|e[+-]\d+)[^0-9]/g, //also allows scientific notation and doesn't kill the leading -/+ in -= and += 2602 _suffixExp = /(?:\d|\-|\+|=|#|\.)*/g, 2603 _opacityExp = /opacity *= *([^)]*)/i, 2604 _opacityValExp = /opacity:([^;]*)/i,
2605 _alphaFilterExp = /alpha\(opacity *=.+?\)/i, 2606 _rgbhslExp = /^(rgb|hsl)/, 2607 _capsExp = /([A-Z])/g, 2608 _camelExp = /-([a-z])/gi, 2609 _urlExp = /(^(?:url\(\"|url\())|(?:(\"\))$|\)$)/gi, //for pulling out urls from url(...) or url("...") strings (some browsers wrap urls in quotes, some don't when reporting things like backgroundImage) 2610 _camelFunc = function(s, g) { return g.toUpperCase(); }, 2611 _horizExp = /(?:Left|Right|Width)/i, 2612 _ieGetMatrixExp = /(M11|M12|M21|M22)=[\d\-\.e]+/gi, 2613 _ieSetMatrixExp = /progid\:DXImageTransform\.Microsoft\.Matrix\(.+?\)/i, 2614 _commasOutsideParenExp = /,(?=[^\)]*(?:\(|$))/gi, //finds any commas that are not within parenthesis 2615 _complexExp = /[\s,\(]/i, //for testing a string to find if it has a space, comma, or open parenthesis (clues that it's a complex value) 2616 _DEG2RAD = Math.PI / 180, 2617 _RAD2DEG = 180 / Math.PI, 2618 _forcePT = {}, 2619 _dummyElement = {style:{}}, 2620 _doc = _gsScope.document || {createElement: function() {return _dummyElement;}}, 2621 _createElement = function(type, ns) { 2622 return _doc.createElementNS ? _doc.createElementNS(ns || "http://www.w3.org/1999/xhtml", type) : _doc.createElement(type); 2623 }, 2624 _tempDiv = _createElement("div"), 2625 _tempImg = _createElement("img"), 2626 _internals = CSSPlugin._internals = {_specialProps:_specialProps}, //provides a hook to a few internal methods that we need to access from inside other plugins 2627 _agent = (_gsScope.navigator || {}).userAgent || "", 2628 _autoRound, 2629 _reqSafariFix, //we won't apply the Safari transform fix until we actually come across a tween that affects a transform property (to maintain best performance). 2630 2631 _isSafari, 2632 _isFirefox, //Firefox has a bug that causes 3D transformed elements to randomly disappear unless a repaint is forced after each update on each element. 2633 _isSafariLT6, //Safari (and Android 4 which uses a flavor of Safari) has a bug that prevents changes to "top" and "left" properties from rendering properly if changed on the same frame as a transform UNLESS we set the element's WebkitBackfaceVisibility to hidden (weird, I know). Doing this for Android 3 and earlier seems to actually cause other problems, though (fun!) 2634 _ieVers, 2635 _supportsOpacity = (function() { //we set _isSafari, _ieVers, _isFirefox, and _supportsOpacity all in one function here to reduce file size slightly, especially in the minified version. 2636 var i = _agent.indexOf("Android"), 2637 a = _createElement("a"); 2638 _isSafari = (_agent.indexOf("Safari") !== -1 && _agent.indexOf("Chrome") === -1 && (i === -1 || parseFloat(_agent.substr(i+8, 2)) > 3)); 2639 _isSafariLT6 = (_isSafari && (parseFloat(_agent.substr(_agent.indexOf("Version/")+8, 2)) < 6)); 2640 _isFirefox = (_agent.indexOf("Firefox") !== -1); 2641 if ((/MSIE ([0-9]{1,}[\.0-9]{0,})/).exec(_agent) || (/Trident\/.*rv:([0-9]{1,}[\.0-9]{0,})/).exec(_agent)) { 2642 _ieVers = parseFloat( RegExp.$1 ); 2643 } 2644 if (!a) { 2645 return false; 2646 } 2647 a.style.cssText = "top:1px;opacity:.55;"; 2648 return /^0.55/.test(a.style.opacity); 2649 }()), 2650 _getIEOpacity = function(v) { 2651 return (_opacityExp.test( ((typeof(v) === "string") ? v : (v.currentStyle ? v.currentStyle.filter : v.style.filter) || "") ) ? ( parseFloat( RegExp.$1 ) / 100 ) : 1); 2652 }, 2653 _log = function(s) {//for logging messages, but in a way that won't throw errors in old versions of IE. 2654 if (_gsScope.console) { 2655 console.log(s); 2656 } 2657 }, 2658 _target, //when initting a CSSPlugin, we set this variable so that we can access it from within many other functions without having to pass it around as params 2659 _index, //when initting a CSSPlugin, we set this variable so that we can access it from within many other functions without having to pass it around as params 2660 2661 _prefixCSS = "", //the non-camelCase vendor prefix like "-o-", "-moz-", "-ms-", or "-webkit-" 2662 _prefix = "", //camelCase vendor prefix like "O", "ms", "Webkit", or "Moz". 2663 2664 // @private feed in a camelCase property name like "transform" and it will check to see if it is valid as-is or if it needs a vendor prefix. It returns the corrected camelCase property name (i.e. "WebkitTransform" or "MozTransform" or "transform" or null if no such property is found, like if the browser is IE8 or before, "transform" won't be found at all) 2665 _checkPropPrefix = function(p, e) { 2666 e = e || _tempDiv; 2667 var s = e.style, 2668 a, i; 2669 if (s[p] !== undefined) { 2670 return p; 2671 } 2672 p = p.charAt(0).toUpperCase() + p.substr(1); 2673 a = ["O","Moz","ms","Ms","Webkit"]; 2674 i = 5; 2675 while (--i > -1 && s[a[i]+p] === undefined) { } 2676 if (i >= 0) { 2677 _prefix = (i === 3) ? "ms" : a[i]; 2678 _prefixCSS = "-" + _prefix.toLowerCase() + "-"; 2679 return _prefix + p; 2680 } 2681 return null; 2682 }, 2683 2684 _getComputedStyle = (typeof(window) !== "undefined" ? window : _doc.defaultView || {getComputedStyle:function() {}}).getComputedStyle, 2685 2686 /** 2687 * @private Returns the css style for a particular property of an element. For example, to get whatever the current "left" css value for an element with an ID of "myElement", you could do: 2688 * var currentLeft = CSSPlugin.getStyle( document.getElementById("myElement"), "left"); 2689 * 2690 * @param {!Object} t Target element whose style property you want to query 2691 * @param {!string} p Property name (like "left" or "top" or "marginTop", etc.) 2692 * @param {Object=} cs Computed style object. This just provides a way to speed processing if you're going to get several properties on the same element in quick succession - you can reuse the result of the getComputedStyle() call. 2693 * @param {boolean=} calc If true, the value will not be read directly from the element's "style" property (if it exists there), but instead the getComputedStyle() result will be used. This can be useful when you want to ensure that the browser itself is interpreting the value. 2694 * @param {string=} dflt Default value that should be returned in the place of null, "none", "auto" or "auto auto". 2695 * @return {?string} The current property value 2696 */ 2697 _getStyle = CSSPlugin.getStyle = function(t, p, cs, calc, dflt) { 2698 var rv; 2699 if (!_supportsOpacity) if (p === "opacity") { //several versions of IE don't use the standard "opacity" property - they use things like filter:alpha(opacity=50), so we parse that here. 2700 return _getIEOpacity(t); 2701 } 2702 if (!calc && t.style[p]) { 2703 rv = t.style[p]; 2704 } else if ((cs = cs || _getComputedStyle(t))) { 2705 rv = cs[p] || cs.getPropertyValue(p) || cs.getPropertyValue(p.replace(_capsExp, "-$1").toLowerCase()); 2706 } else if (t.currentStyle) { 2707 rv = t.currentStyle[p]; 2708 } 2709 return (dflt != null && (!rv || rv === "none" || rv === "auto" || rv === "auto auto")) ? dflt : rv; 2710 }, 2711 2712 /** 2713 * @private Pass the target element, the property name, the numeric value, and the suffix (like "%", "em", "px", etc.) and it will spit back the equivalent pixel number. 2714 * @param {!Object} t Target element 2715 * @param {!string} p Property name (like "left", "top", "marginLeft", etc.) 2716 * @param {!number} v Value 2717 * @param {string=} sfx Suffix (like "px" or "%" or "em") 2718 * @param {boolean=} recurse If true, the call is a recursive one. In some browsers (like IE7/8), occasionally the value isn't accurately reported initially, but if we run the function again it will take effect. 2719 * @return {number} value in pixels 2720 */ 2721 _convertToPixels = _internals.convertToPixels = function(t, p, v, sfx, recurse) { 2722 if (sfx === "px" || (!sfx && p !== "lineHeight")) { return v; } 2723 if (sfx === "auto" || !v) { return 0; } 2724 var horiz = _horizExp.test(p), 2725 node = t, 2726 style = _tempDiv.style, 2727 neg = (v < 0), 2728 precise = (v === 1), 2729 pix, cache, time; 2730 if (neg) { 2731 v = -v; 2732 } 2733 if (precise) { 2734 v *= 100; 2735 } 2736 if (p === "lineHeight" && !sfx) { //special case of when a simple lineHeight (without a unit) is used. Set it to the value, read back the computed value, and then revert. 2737 cache = _getComputedStyle(t).lineHeight; 2738 t.style.lineHeight = v; 2739 pix = parseFloat(_getComputedStyle(t).lineHeight); 2740 t.style.lineHeight = cache; 2741 } else if (sfx === "%" && p.indexOf("border") !== -1) { 2742 pix = (v / 100) * (horiz ? t.clientWidth : t.clientHeight); 2743 } else { 2744 style.cssText = "border:0 solid red;position:" + _getStyle(t, "position") + ";line-height:0;"; 2745 if (sfx === "%" || !node.appendChild || sfx.charAt(0) === "v" || sfx === "rem") { 2746 node = t.parentNode || _doc.body; 2747 if (_getStyle(node, "display").indexOf("flex") !== -1) { //Edge and IE11 have a bug that causes offsetWidth to report as 0 if the container has display:flex and the child is position:relative. Switching to position: absolute solves it. 2748 style.position = "absolute"; 2749 } 2750 cache = node._gsCache; 2751 time = TweenLite.ticker.frame; 2752 if (cache && horiz && cache.time === time) { //performance optimization: we record the width of elements along with the ticker frame so that we can quickly get it again on the same tick (seems relatively safe to assume it wouldn't change on the same tick) 2753 return cache.width * v / 100; 2754 } 2755 style[(horiz ? "width" : "height")] = v + sfx; 2756 } else { 2757 style[(horiz ? "borderLeftWidth" : "borderTopWidth")] = v + sfx; 2758 } 2759 node.appendChild(_tempDiv); 2760 pix = parseFloat(_tempDiv[(horiz ? "offsetWidth" : "offsetHeight")]); 2761 node.removeChild(_tempDiv); 2762 if (horiz && sfx === "%" && CSSPlugin.cacheWidths !== false) { 2763 cache = node._gsCache = node._gsCache || {}; 2764 cache.time = time; 2765 cache.width = pix / v * 100; 2766 } 2767 if (pix === 0 && !recurse) { 2768 pix = _convertToPixels(t, p, v, sfx, true); 2769 } 2770 } 2771 if (precise) { 2772 pix /= 100; 2773 } 2774 return neg ? -pix : pix; 2775 }, 2776 _calculateOffset = _internals.calculateOffset = function(t, p, cs) { //for figuring out "top" or "left" in px when it's "auto". We need to factor in margin with the offsetLeft/offsetTop 2777 if (_getStyle(t, "position", cs) !== "absolute") { return 0; } 2778 var dim = ((p === "left") ? "Left" : "Top"),
2779 v = _getStyle(t, "margin" + dim, cs); 2780 return t["offset" + dim] - (_convertToPixels(t, p, parseFloat(v), v.replace(_suffixExp, "")) || 0); 2781 }, 2782 2783 // @private returns at object containing ALL of the style properties in camelCase and their associated values. 2784 _getAllStyles = function(t, cs) { 2785 var s = {}, 2786 i, tr, p; 2787 if ((cs = cs || _getComputedStyle(t, null))) { 2788 if ((i = cs.length)) { 2789 while (--i > -1) { 2790 p = cs[i]; 2791 if (p.indexOf("-transform") === -1 || _transformPropCSS === p) { //Some webkit browsers duplicate transform values, one non-prefixed and one prefixed ("transform" and "WebkitTransform"), so we must weed out the extra one here. 2792 s[p.replace(_camelExp, _camelFunc)] = cs.getPropertyValue(p); 2793 } 2794 } 2795 } else { //some browsers behave differently - cs.length is always 0, so we must do a for...in loop. 2796 for (i in cs) { 2797 if (i.indexOf("Transform") === -1 || _transformProp === i) { //Some webkit browsers duplicate transform values, one non-prefixed and one prefixed ("transform" and "WebkitTransform"), so we must weed out the extra one here. 2798 s[i] = cs[i]; 2799 } 2800 } 2801 } 2802 } else if ((cs = t.currentStyle || t.style)) { 2803 for (i in cs) { 2804 if (typeof(i) === "string" && s[i] === undefined) { 2805 s[i.replace(_camelExp, _camelFunc)] = cs[i]; 2806 } 2807 } 2808 } 2809 if (!_supportsOpacity) { 2810 s.opacity = _getIEOpacity(t); 2811 } 2812 tr = _getTransform(t, cs, false); 2813 s.rotation = tr.rotation; 2814 s.skewX = tr.skewX; 2815 s.scaleX = tr.scaleX; 2816 s.scaleY = tr.scaleY; 2817 s.x = tr.x; 2818 s.y = tr.y; 2819 if (_supports3D) { 2820 s.z = tr.z; 2821 s.rotationX = tr.rotationX; 2822 s.rotationY = tr.rotationY; 2823 s.scaleZ = tr.scaleZ; 2824 } 2825 if (s.filters) { 2826 delete s.filters; 2827 } 2828 return s; 2829 }, 2830 2831 // @private analyzes two style objects (as returned by _getAllStyles()) and only looks for differences between them that contain tweenable values (like a number or color). It returns an object with a "difs" property which refers to an object c
2831ontaining only those isolated properties and values for tweening, and a "firstMPT" property which refers to the first MiniPropTween instance in a linked list that recorded all the starting values of the different properties so that we can revert to them at the end or beginning of the tween - we don't want the cascading to get messed up. The forceLookup parameter is an optional generic object with properties that should be forced into the results - this is necessary for className tweens that are overwriting others because imagine a scenario where a rollover/rollout adds/removes a class and the user swipes the mouse over the target SUPER fast, thus nothing actually changed yet and the subsequent comparison of the properties would indicate they match (especially when px rounding is taken into consideration), thus no tweening is necessary even though it SHOULD tween and remove those properties after the tween (otherwise the inline styles will contaminate things). See the className SpecialProp code for details. 2832 _cssDif = function(t, s1, s2, vars, forceLookup) { 2833 var difs = {}, 2834 style = t.style, 2835 val, p, mpt; 2836 for (p in s2) { 2837 if (p !== "cssText") if (p !== "length") if (isNaN(p)) if (s1[p] !== (val = s2[p]) || (forceLookup && forceLookup[p])) if (p.indexOf("Origin") === -1) if (typeof(val) === "number" || typeof(val) === "string") { 2838 difs[p] = (val === "auto" && (p === "left" || p === "top")) ? _calculateOffset(t, p) : ((val === "" || val === "auto" || val === "none") && typeof(s1[p]) === "string" && s1[p].replace(_NaNExp, "") !== "") ? 0 : val; //if the ending value is defaulting ("" or "auto"), we check the starting value and if it can be parsed into a number (a string which could have a suffix too, like 700px), then we swap in 0 for "" or "auto" so that things actually tween. 2839 if (style[p] !== undefined) { //for className tweens, we must remember which properties already existed inline - the ones that didn't should be removed when the tween isn't in progress because they were only introduced to facilitate the transition between classes. 2840 mpt = new MiniPropTween(style, p, style[p], mpt); 2841 } 2842 } 2843 } 2844 if (vars) { 2845 for (p in vars) { //copy properties (except className) 2846 if (p !== "className") { 2847 difs[p] = vars[p]; 2848 } 2849 } 2850 } 2851 return {difs:difs, firstMPT:mpt}; 2852 }, 2853 _dimensions = {width:["Left","Right"], height:["Top","Bottom"]}, 2854 _margins = ["marginLeft","marginRight","marginTop","marginBottom"], 2855 2856 /** 2857 * @private Gets the width or height of an element 2858 * @param {!Object} t Target element 2859 * @param {!string} p Property name ("width" or "height") 2860 * @param {Object=} cs Computed style object (if one exists). Just a speed optimization. 2861 * @return {number} Dimension (in pixels) 2862 */ 2863 _getDimension = function(t, p, cs) { 2864 if ((t.nodeName + "").toLowerCase() === "svg") { //Chrome no longer supports offsetWidth/offsetHeight on SVG elements. 2865 return (cs || _getComputedStyle(t))[p] || 0; 2866 } else if (t.getCTM && _isSVG(t)) { 2867 return t.getBBox()[p] || 0; 2868 } 2869 var v = parseFloat((p === "width") ? t.offsetWidth : t.offsetHeight), 2870 a = _dimensions[p], 2871 i = a.length; 2872 cs = cs || _getComputedStyle(t, null); 2873 while (--i > -1) { 2874 v -= parseFloat( _getStyle(t, "padding" + a[i], cs, true) ) || 0; 2875 v -= parseFloat( _getStyle(t, "border" + a[i] + "Width", cs, true) ) || 0; 2876 } 2877 return v; 2878 }, 2879 2880 // @private Parses position-related complex strings like "top left" or "50px 10px" or "70% 20%", etc. which are used for things like transformOrigin or backgroundPosition. Optionally decorates a supplied object (recObj) with the following properties: "ox" (offsetX), "oy" (offsetY), "oxp" (if true, "ox" is a percentage not a pixel value), and "oxy" (if true, "oy" is a percentage not a pixel value) 2881 _parsePosition = function(v, recObj) { 2882 if (v === "contain" || v === "auto" || v === "auto auto") { //note: Firefox uses "auto auto" as default whereas Chrome uses "auto". 2883 return v + " "; 2884 } 2885 if (v == null || v === "") { 2886 v = "0 0"; 2887 } 2888 var a = v.split(" "), 2889 x = (v.indexOf("left") !== -1) ? "0%" : (v.indexOf("right") !== -1) ? "100%" : a[0], 2890 y = (v.indexOf("top") !== -1) ? "0%" : (v.indexOf("bottom") !== -1) ? "100%" : a[1], 2891 i; 2892 if (a.length > 3 && !recObj) { //multiple positions 2893 a = v.split(", ").join(",").split(","); 2894 v = []; 2895 for (i = 0; i < a.length; i++) { 2896 v.push(_parsePosition(a[i])); 2897 } 2898 return v.join(","); 2899 } 2900 if (y == null) { 2901 y = (x === "center") ? "50%" : "0"; 2902 } else if (y === "center") { 2903 y = "50%"; 2904 } 2905 if (x === "center" || (isNaN(parseFloat(x)) && (x + "").indexOf("=") === -1)) { //remember, the user could flip-flop the values and say "bottom center" or "center bottom", etc. "center" is ambiguous because it c
2905ould be used to describe horizontal or vertical, hence the isNaN(). If there's an "=" sign in the value, it's relative. 2906 x = "50%"; 2907 } 2908 v = x + " " + y + ((a.length > 2) ? " " + a[2] : ""); 2909 if (recObj) { 2910 recObj.oxp = (x.indexOf("%") !== -1); 2911 recObj.oyp = (y.indexOf("%") !== -1); 2912 recObj.oxr = (x.charAt(1) === "="); 2913 recObj.oyr = (y.charAt(1) === "="); 2914 recObj.ox = parseFloat(x.replace(_NaNExp, "")); 2915 recObj.oy = parseFloat(y.replace(_NaNExp, "")); 2916 recObj.v = v; 2917 } 2918 return recObj || v; 2919 }, 2920 2921 /** 2922 * @private Takes an ending value (typically a string, but can be a number) and a starting value and returns the change between the two, looking for relative value indicators like += and -= and it also ignores suffixes (but make sure the ending value starts with a number or +=/-= and that the starting value is a NUMBER!) 2923 * @param {(number|string)} e End value which is typically a string, but could be a number 2924 * @param {(number|string)} b Beginning value which is typically a string but could be a number 2925 * @return {number} Amount of change between the beginning and ending values (relative values that have a "+=" or "-=" are recognized) 2926 */ 2927 _parseChange = function(e, b) { 2928 if (typeof(e) === "function") { 2929 e = e(_index, _target); 2930 } 2931 return (typeof(e) === "string" && e.charAt(1) === "=") ? parseInt(e.charAt(0) + "1", 10) * parseFloat(e.substr(2)) : (parseFloat(e) - parseFloat(b)) || 0; 2932 }, 2933 2934 /** 2935 * @private Takes a value and a default number, checks if the value is relative, null, or numeric and spits back a normalized number accordingly. Primarily used in the _parseTransform() function. 2936 * @param {Object} v Value to be parsed 2937 * @param {!number} d Default value (which is also used for relative calculations if "+=" or "-=" is found in the first parameter) 2938 * @return {number} Parsed value 2939 */ 2940 _parseVal = function(v, d) { 2941 if (typeof(v) === "function") { 2942 v = v(_index, _target); 2943 } 2944 var isRelative = (typeof(v) === "string" && v.charAt(1) === "="); 2945 if (typeof(v) === "string" && v.charAt(v.length - 2) === "v") { //convert vw and vh into px-equivalents. 2946 v = (isRelative ? v.substr(0, 2) : 0) + (window["inner" + ((v.substr(-2) === "vh") ? "Height" : "Width")] * (parseFloat(isRelative ? v.substr(2) : v) / 100)); 2947 } 2948 return (v == null) ? d : isRelative ? parseInt(v.charAt(0) + "1", 10) * parseFloat(v.substr(2)) + d : parseFloat(v) || 0; 2949 }, 2950 2951 /** 2952 * @private Translates strings like "40deg" or "40" or 40rad" or "+=40deg" or "270_short" or "-90_cw" or "+=45_ccw" to a numeric radian angle. Of course a starting/default value must be fed in too so that relative values can be calculated properly. 2953 * @param {Object} v Value to be parsed 2954 * @param {!number} d Default value (which is also used for relative calculations if "+=" or "-=" is found in the first parameter) 2955 * @param {string=} p property name for directionalEnd (optional - only used when the parsed value is directional ("_short", "_cw", or "_ccw" suffix). We need a way to store the uncompensated value so that at the end of the tween, we set it to exactly what was requested with no directional compensation). Property name would be "rotation", "rotationX", or "rotationY" 2956 * @param {Object=} directionalEnd An object that will store the raw end values for directional angles ("_short", "_cw", or "_ccw" suffix). We need a way to store the uncompensated value so that at the end of the tween, we set it to exactly what was requested with no directional compensation. 2957 * @return {number} parsed angle in radians 2958 */ 2959 _parseAngle = function(v, d, p, directionalEnd) { 2960 var min = 0.000001, 2961 cap, split, dif, result, isRelative; 2962 if (typeof(v) === "function") { 2963 v = v(_index, _target); 2964 } 2965 if (v == null) { 2966 result = d; 2967 } else if (typeof(v) === "number") { 2968 result = v; 2969 } else { 2970 cap = 360; 2971 split = v.split("_"); 2972 isRelative = (v.charAt(1) === "="); 2973 dif = (isRelative ? parseInt(v.charAt(0) + "1", 10) * parseFloat(split[0].substr(2)) : parseFloat(split[0])
2973) * ((v.indexOf("rad") === -1) ? 1 : _RAD2DEG) - (isRelative ? 0 : d); 2974 if (split.length) { 2975 if (directionalEnd) { 2976 directionalEnd[p] = d + dif; 2977 } 2978 if (v.indexOf("short") !== -1) { 2979 dif = dif % cap; 2980 if (dif !== dif % (cap / 2)) { 2981 dif = (dif < 0) ? dif + cap : dif - cap; 2982 } 2983 } 2984 if (v.indexOf("_cw") !== -1 && dif < 0) { 2985 dif = ((dif + cap * 9999999999) % cap) - ((dif / cap) | 0) * cap; 2986 } else if (v.indexOf("ccw") !== -1 && dif > 0) { 2987 dif = ((dif - cap * 9999999999) % cap) - ((dif / cap) | 0) * cap; 2988 } 2989 } 2990 result = d + dif; 2991 } 2992 if (result < min && result > -min) { 2993 result = 0; 2994 } 2995 return result; 2996 }, 2997 2998 _colorLookup = {aqua:[0,255,255], 2999 lime:[0,255,0], 3000 silver:[192,192,192], 3001 black:[0,0,0], 3002 maroon:[128,0,0], 3003 teal:[0,128,128], 3004 blue:[0,0,255], 3005 navy:[0,0,128], 3006 white:[255,255,255], 3007 fuchsia:[255,0,255], 3008 olive:[128,128,0], 3009 yellow:[255,255,0], 3010 orange:[255,165,0], 3011 gray:[128,128,128], 3012 purple:[128,0,128], 3013 green:[0,128,0], 3014 red:[255,0,0], 3015 pink:[255,192,203], 3016 cyan:[0,255,255], 3017 transparent:[255,255,255,0]}, 3018 3019 _hue = function(h, m1, m2) { 3020 h = (h < 0) ? h + 1 : (h > 1) ? h - 1 : h; 3021 return ((((h * 6 < 1) ? m1 + (m2 - m1) * h * 6 : (h < 0.5) ? m2 : (h * 3 < 2) ? m1 + (m2 - m1) * (2 / 3 - h) * 6 : m1) * 255) + 0.5) | 0; 3022 }, 3023 3024 /** 3025 * @private Parses a color (like #9F0, #FF9900, rgb(255,51,153) or hsl(108, 50%, 10%)) into an array with 3 elements for red, green, and blue or if toHSL parameter is true, it will populate the array with hue, saturation, and lightness values. If a relative value is found in an hsl() or hsla() string, it will preserve those relative prefixes and all the values in the array will be strings instead of numbers (in all other cases it will be populated with numbers). 3026 * @param {(string|number)} v The value the should be parsed which could be a string like #9F0 or rgb(255,102,51) or rgba(255,0,0,0.5) or it could be a number like 0xFF00CC or even a named color like red, blue, purple, etc. 3027 * @param {(boolean)} toHSL If true, an hsl() or hsla() value will be returned instead of rgb() or rgba() 3028 * @return {Array.<number>} An array containing red, green, and blue (and optionally alpha) in that order, or if the toHSL parameter was true, the array will contain hue, saturation and lightness (and optionally alpha) in that order. Always numbers unless there's a relative prefix found in an hsl() or hsla() string and toHSL is true. 3029 */ 3030 _parseColor = CSSPlugin.parseColor = function(v, toHSL) { 3031 var a, r, g, b, h, s, l, max, min, d, wasHSL; 3032 if (!v) { 3033 a = _colorLookup.black; 3034 } else if (typeof(v) === "number") { 3035 a = [v >> 16, (v >> 8) & 255, v & 255]; 3036 } else { 3037 if (v.charAt(v.length - 1) === ",") { //sometimes a trailing comma is included and we should chop it off (typically from a comma-delimited list of values like a textShadow:"2px 2px 2px blue, 5px 5px 5px rgb(255,0,0)" - in this example "blue," has a trailing comma. We could strip it out inside parseComplex() but we'd need to do it to the beginning and ending values plus it wouldn't provide protection from other potential scenarios like if the user passes in a similar value. 3038 v = v.substr(0, v.length - 1); 3039 } 3040 if (_colorLookup[v]) { 3041 a = _colorLookup[v]; 3042 } else if (v.charAt(0) === "#") { 3043 if (v.length === 4) { //for shorthand like #9F0 3044 r = v.charAt(1); 3045 g = v.charAt(2); 3046 b = v.charAt(3); 3047 v = "#" + r + r + g + g + b + b; 3048 } 3049 v = parseInt(v.substr(1), 16); 3050 a = [v >> 16, (v >> 8) & 255, v & 255]; 3051 } else if (v.substr(0, 3) === "hsl") { 3052 a = wasHSL = v.match(_numExp); 3053 if (!toHSL) { 3054 h = (Number(a[0]) % 360) / 360;
3055 s = Number(a[1]) / 100; 3056 l = Number(a[2]) / 100; 3057 g = (l <= 0.5) ? l * (s + 1) : l + s - l * s; 3058 r = l * 2 - g; 3059 if (a.length > 3) { 3060 a[3] = Number(a[3]); 3061 } 3062 a[0] = _hue(h + 1 / 3, r, g); 3063 a[1] = _hue(h, r, g); 3064 a[2] = _hue(h - 1 / 3, r, g); 3065 } else if (v.indexOf("=") !== -1) { //if relative values are found, just return the raw strings with the relative prefixes in place. 3066 return v.match(_relNumExp); 3067 } 3068 } else { 3069 a = v.match(_numExp) || _colorLookup.transparent; 3070 } 3071 a[0] = Number(a[0]); 3072 a[1] = Number(a[1]); 3073 a[2] = Number(a[2]); 3074 if (a.length > 3) { 3075 a[3] = Number(a[3]); 3076 } 3077 } 3078 if (toHSL && !wasHSL) { 3079 r = a[0] / 255; 3080 g = a[1] / 255; 3081 b = a[2] / 255; 3082 max = Math.max(r, g, b); 3083 min = Math.min(r, g, b); 3084 l = (max + min) / 2; 3085 if (max === min) { 3086 h = s = 0; 3087 } else { 3088 d = max - min; 3089 s = l > 0.5 ? d / (2 - max - min) : d / (max + min); 3090 h = (max === r) ? (g - b) / d + (g < b ? 6 : 0) : (max === g) ? (b - r) / d + 2 : (r - g) / d + 4; 3091 h *= 60; 3092 } 3093 a[0] = (h + 0.5) | 0; 3094 a[1] = (s * 100 + 0.5) | 0; 3095 a[2] = (l * 100 + 0.5) | 0; 3096 } 3097 return a; 3098 }, 3099 _formatColors = function(s, toHSL) { 3100 var colors = s.match(_colorExp) || [], 3101 charIndex = 0, 3102 parsed = "", 3103 i, color, temp; 3104 if (!colors.length) { 3105 return s; 3106 } 3107 for (i = 0; i < colors.length; i++) { 3108 color = colors[i]; 3109 temp = s.substr(charIndex, s.indexOf(color, charIndex)-charIndex); 3110 charIndex += temp.length + color.length; 3111 color = _parseColor(color, toHSL); 3112 if (color.length === 3) { 3113 color.push(1); 3114 } 3115 parsed += temp + (toHSL ? "hsla(" + color[0] + "," + color[1] + "%," + color[2] + "%," + color[3] : "rgba(" + color.join(",")) + ")"; 3116 } 3117 return parsed + s.substr(charIndex); 3118 }, 3119 _colorExp = "(?:\\b(?:(?:rgb|rgba|hsl|hsla)\\(.+?\\))|\\B#(?:[0-9a-f]{3}){1,2}\\b"; //we'll dynamically build this Regular Expression to conserve file size. After building it, it will be able to find rgb(), rgba(), # (hexadecimal), and named color values like red, blue, purple, etc. 3120 3121 for (p in _colorLookup) { 3122 _colorExp += "|" + p + "\\b"; 3123 } 3124 _colorExp = new RegExp(_colorExp+")", "gi"); 3125 3126 CSSPlugin.colorStringFilter = function(a) { 3127 var combined = a[0] + " " + a[1], 3128 toHSL; 3129 if (_colorExp.test(combined)) { 3130 toHSL = (combined.indexOf("hsl(") !== -1 || combined.indexOf("hsla(") !== -1); 3131 a[0] = _formatColors(a[0], toHSL); 3132 a[1] = _formatColors(a[1], toHSL); 3133 } 3134 _colorExp.lastIndex = 0; 3135 }; 3136 3137 if (!TweenLite.defaultStringFilter) { 3138 TweenLite.defaultStringFilter = CSSPlugin.colorStringFilter; 3139 } 3140 3141 /** 3142 * @private Returns a formatter function that handles taking a string (or number in some cases) and returning a consistently formatted one in terms of delimiters, quantity of values, etc. For example, we may get boxShadow values defined as "0px red" or "0px 0px 10px rgb(255,0,0)" or "0px 0px 20px 20px #F00" and we need to ensure that what we get back is described with 4 numbers and a color. This allows us to feed it into the _parseComplex() method and split the values up appropriately. The neat thing about this _getFormatter() function is that the dflt defines a pattern as well as a default, so for example, _getFormatter("0px 0px 0px 0px #777", true) not only sets the default as 0px for all distances and #777 for the color, but also sets the pattern such that 4 numbers and a color will always get returned. 3143 * @param {!string} dflt The default value and pattern to follow. So "0px 0px 0px 0px #777" will ensure that 4 numbers and a color will always get returned. 3144 * @param {boolean=} clr If true, the values should be searched for color-related data. For example, boxShadow values typically contain a color whereas borderRadius don't. 3145 * @param {boolean=} collapsible If true, the value is a top/left/right/bottom style one that acts like margin or padding, where if only one value is received, it's used for all 4; if 2 are received, the first is duplicated for 3rd (bottom) and the 2nd is duplicated for the 4th spot (left), etc. 3146 * @return {Function} formatter function 3147 */ 3148 var _getFormatter = function(dflt, clr, collapsible, multi) { 3149 if (dflt == null) { 3150 return function(v) {return v;}; 3151 } 3152 var dColor = clr ? (dflt.match(_colorExp) || [""])[0] : "", 3153 dVals = dflt.split(dColor).join("").match(_valuesExp) || [], 3154 pfx = dflt.substr(0, dflt.indexOf(dVals[0])), 3155 sfx = (dflt.charAt(dflt.length - 1) === ")") ? ")" : "", 3156 delim = (dflt.indexOf(" ") !== -1) ? " " : ",", 3157 numVals = dVals.length, 3158 dSfx = (numVals > 0) ? dVals[0].replace(_numExp, "") : "", 3159 formatter; 3160 if (!numVals) { 3161 return function(v) {return v;}; 3162 } 3163 if (clr) { 3164 formatter = function(v) { 3165 var color, vals, i, a; 3166 if (typeof(v) === "number") { 3167 v += dSfx; 3168 } else if (multi && _commasOutsideParenExp.test(v)) { 3169 a = v.replace(_commasOutsideParenExp, "|").split("|"); 3170 for (i = 0; i < a.length; i++) { 3171 a[i] = formatter(a[i]); 3172 } 3173 return a.join(","); 3174 } 3175 color = (v.match(_colorExp) || [dColor])[0]; 3176 vals = v.split(color).join("").match(_valuesExp) || []; 3177 i = vals.length; 3178 if (numVals > i--) { 3179 while (++i < numVals) { 3180 vals[i] = collapsible ? vals[(((i - 1) / 2) | 0)] : dVals[i]; 3181 } 3182 } 3183 return pfx + vals.join(delim) + delim + color + sfx + (v.indexOf("inset") !== -1 ? " inset" : ""); 3184 }; 3185 return formatter; 3186 3187 } 3188 formatter = function(v) { 3189 var vals, a, i; 3190 if (typeof(v) === "number") { 3191 v += dSfx; 3192 } else if (multi && _commasOutsideParenExp.test(v)) { 3193 a = v.replace(_commasOutsideParenExp, "|").split("|"); 3194 for (i = 0; i < a.length; i++) { 3195 a[i] = formatter(a[i]); 3196 } 3197 return a.join(","); 3198 } 3199 vals = v.match(_valuesExp) || []; 3200 i = vals.length; 3201 if (numVals > i--) { 3202 while (++i < numVals) { 3203 vals[i] = collapsible ? vals[(((i - 1) / 2) | 0)] : dVals[i]; 3204 } 3205 } 3206 return pfx + vals.join(delim) + sfx; 3207 }; 3208 return formatter; 3209 }, 3210 3211 /** 3212 * @private returns a formatter function that's used for edge-related values like marginTop, marginLeft, paddingBottom, paddingRight, etc. Just pass a comma-delimited list of property names related to the edges. 3213 * @param {!string} props a comma-delimited list of property names in order from top to left, like "marginTop,marginRight,marginBottom,marginLeft" 3214 * @return {Function} a formatter function 3215 */ 3216 _getEdgeParser = function(props) { 3217 props = props.split(","); 3218 return function(t, e, p, cssp, pt, plugin, vars) { 3219 var a = (e + "").split(" "), 3220 i; 3221 vars = {}; 3222 for (i = 0; i < 4; i++) { 3223 vars[props[i]] = a[i] = a[i] || a[(((i - 1) / 2) >> 0)]; 3224 } 3225 return cssp.parse(t, vars, pt, plugin); 3226 }; 3227 }, 3228 3229 // @private used when other plugins must tween values first, like BezierPlugin or ThrowPropsPlugin, etc. That plugin's setRatio() gets called first so that the values are updated, and then we loop through the MiniPropTweens which handle copying the values into their appropriate slots so that they can then be applied correctly in the main CSSPlugin setRatio() method. Remember, we typically create a proxy object that has a bunch of uniquely-named properties that we feed to the sub-plugin and it does its magic normally, and then we must interpret those values and apply them to the css because often numbers must get combined/concatenated, suffixes added, etc. to work with css, like boxShadow could have 4 values plus a color. 3230 _setPluginRatio = _internals._setPluginRatio = function(v) { 3231 this.plugin.setRatio(v); 3232 var d = this.data, 3233 proxy = d.proxy, 3234 mpt = d.firstMPT, 3235 min = 0.000001, 3236 val, pt, i, str, p; 3237 while (mpt) { 3238 val = proxy[mpt.v]; 3239 if (mpt.r) { 3240 val = mpt.r(val); 3241 } else if (val < min && val > -min) { 3242 val = 0; 3243 } 3244 mpt.t[mpt.p] = val; 3245 mpt = mpt._next; 3246 } 3247 if (d.autoRotate) { 3248 d.autoRotate.rotation = d.mod ? d.mod.call(this._tween, proxy.rotation, this.t, this._tween) : proxy.rotation; //special case for ModifyPlugin to hook into an auto-rotating bezier 3249 } 3250 //at the end, we must set the CSSPropTween's "e" (end) value dynamically here because that's what is used in the final setRatio() method. Same for "b" at the beginning. 3251 if (v === 1 || v === 0) { 3252 mpt = d.firstMPT; 3253 p = (v === 1) ? "e" : "b"; 3254 while (mpt) { 3255 pt = mpt.t; 3256 if (!pt.type) { 3257 pt[p] = pt.s + pt.xs0; 3258 } else if (pt.type === 1) { 3259 str = pt.xs0 + pt.s + pt.xs1; 3260 for (i = 1; i < pt.l; i++) {
3261 str += pt["xn"+i] + pt["xs"+(i+1)]; 3262 } 3263 pt[p] = str; 3264 } 3265 mpt = mpt._next; 3266 } 3267 } 3268 }, 3269 3270 /** 3271 * @private @constructor Used by a few SpecialProps to hold important values for proxies. For example, _parseToProxy() creates a MiniPropTween instance for each property that must get tweened on the proxy, and we record the original property name as well as the unique one we create for the proxy, plus whether or not the value needs to be rounded plus the original value. 3272 * @param {!Object} t target object whose property we're tweening (often a CSSPropTween) 3273 * @param {!string} p property name 3274 * @param {(number|string|object)} v value 3275 * @param {MiniPropTween=} next next MiniPropTween in the linked list 3276 * @param {boolean=} r if true, the tweened value should be rounded to the nearest integer 3277 */ 3278 MiniPropTween = function(t, p, v, next, r) { 3279 this.t = t; 3280 this.p = p; 3281 this.v = v; 3282 this.r = r; 3283 if (next) { 3284 next._prev = this; 3285 this._next = next; 3286 } 3287 }, 3288 3289 /** 3290 * @private Most other plugins (like BezierPlugin and ThrowPropsPlugin and others) can only tween numeric values, but CSSPlugin must accommodate special values that have a bunch of extra data (like a suffix or strings between numeric values, etc.). For example, boxShadow has values like "10px 10px 20px 30px rgb(255,0,0)" which would utterly confuse other plugins. This method allows us to split that data apart and grab only the numeric data and attach it to uniquely-named properties of a generic proxy object ({}) so that we can feed that to virtually any plugin to have the numbers tweened. However, we must also keep track of which properties from the proxy go with which CSSPropTween values and instances. So we create a linked list of MiniPropTweens. Each one records a target (the original CSSPropTween), property (like "s" or "xn1" or "xn2") that we're tweening and the unique property name that was used for the proxy (like "boxShadow_xn1" and "boxShadow_xn2") and whether or not they need to be rounded. That way, in the _setPluginRatio() method we can simply copy the values over from the proxy to the CSSPropTween instance(s). Then, when the main CSSPlugin setRatio() method runs and applies the CSSPropTween values accordingly, they're updated nicely. So the external plugin tweens the numbers, _setPluginRatio() copies them over, and setRatio() acts normally, applying css-specific values to the element. 3291 * This method returns an object that has the following properties:
3292 * - proxy: a generic object containing the starting values for all the properties that will be tweened by the external plugin. This is what we feed to the external _onInitTween() as the target 3293 * - end: a generic object containing the ending values for all the properties that will be tweened by the external plugin. This is what we feed to the external plugin's _onInitTween() as the destination values 3294 * - firstMPT: the first MiniPropTween in the linked list 3295 * - pt: the first CSSPropTween in the linked list that was created when parsing. If shallow is true, this linked list will NOT attach to the one passed into the _parseToProxy() as the "pt" (4th) parameter. 3296 * @param {!Object} t target object to be tweened 3297 * @param {!(Object|string)} vars the object containing the information about the tweening values (typically the end/destination values) that should be parsed 3298 * @param {!CSSPlugin} cssp The CSSPlugin instance 3299 * @param {CSSPropTween=} pt the next CSSPropTween in the linked list 3300 * @param {TweenPlugin=} plugin the external TweenPlugin instance that will be handling tweening the numeric values 3301 * @param {boolean=} shallow if true, the resulting linked list from the parse will NOT be attached to the CSSPropTween that was passed in as the "pt" (4th) parameter. 3302 * @return An object containing the following properties: proxy, end, firstMPT, and pt (see above for descriptions) 3303 */ 3304 _parseToProxy = _internals._parseToProxy = function(t, vars, cssp, pt, plugin, shallow) { 3305 var bpt = pt, 3306 start = {}, 3307 end = {}, 3308 transform = cssp._transform, 3309 oldForce = _forcePT, 3310 i, p, xp, mpt, firstPT; 3311 cssp._transform = null; 3312 _forcePT = vars; 3313 pt = firstPT = cssp.parse(t, vars, pt, plugin); 3314 _forcePT = oldForce; 3315 //break off from the linked list so the new ones are isolated. 3316 if (shallow) { 3317 cssp._transform = transform; 3318 if (bpt) { 3319 bpt._prev = null; 3320 if (bpt._prev) { 3321 bpt._prev._next = null; 3322 } 3323 } 3324 } 3325 while (pt && pt !== bpt) { 3326 if (pt.type <= 1) { 3327 p = pt.p; 3328 end[p] = pt.s + pt.c; 3329 start[p] = pt.s; 3330 if (!shallow) { 3331 mpt = new MiniPropTween(pt, "s", p, mpt, pt.r); 3332 pt.c = 0; 3333 } 3334 if (pt.type === 1) { 3335 i = pt.l; 3336 while (--i > 0) { 3337 xp = "xn" + i; 3338 p = pt.p + "_" + xp; 3339 end[p] = pt.data[xp]; 3340 start[p] = pt[xp]; 3341 if (!shallow) { 3342 mpt = new MiniPropTween(pt, xp, p, mpt, pt.rxp[xp]); 3343 } 3344 } 3345 } 3346 } 3347 pt = pt._next; 3348 } 3349 return {proxy:start, end:end, firstMPT:mpt, pt:firstPT}; 3350 }, 3351 3352 3353 3354 /** 3355 * @constructor Each property that is tweened has at least one CSSPropTween associated with it. These instances store important information like the target, property, starting value, amount of change, etc. They can also optionally have a number of "extra" strings and numeric values named xs1, xn1, xs2, xn2, xs3, xn3, etc. where "s" indicates string and "n" indicates number. These can be pieced together in a complex-value tween (type:1) that has alternating types of data like a string, number, string, number, etc. For example, boxShadow could be "5px 5px 8px rgb(102, 102, 51)". In that value, there are 6 numbers that may need to tween and then pieced back together into a string again with spaces, suffixes, etc. xs0 is special in that it stores the suffix for standard (type:0) tweens, -OR- the first string (prefix) in a complex-value (type:1) CSSPropTween -OR- it can be the non-tweening value in a type:-1 CSSPropTween. We do this to conserve memory. 3356 * CSSPropTweens have the following optional properties as well (not defined through the constructor): 3357 * - l: Length in terms of the number of extra properties that the CSSPropTween has (default: 0). For example, for a boxShadow we may need to tween 5 numbers in which case l would be 5; Keep in mind that the start/end values for the first number that's tweened are always stored in the s and c properties to conserve memory. All additional values thereafter are stored in xn1, xn2, etc. 3358 * - xfirst: The first instance of any sub-CSSPropTweens that are tweening properties of this instance. For example, we may split up a boxShadow tween so that there's a main CSSPropTween of type:1 that has various xs* and xn* values associated with the h-shadow, v-shadow, blur, color, etc. Then we spawn a CSSPropTween for each of those that has a higher priority and runs BEFORE the main CSSPropTween so that the values are all set by the time it needs to re-assemble them. The xfirst gives us an easy way to identify the first one in that chain which typically ends at the main one (because they're all prepende to the linked list) 3359 * - plugin: The TweenPlugin instance that will handle the tweening of any complex values. For example, sometimes we don't want to use normal subt
3359weens (like xfirst refers to) to tween the values - we might want ThrowPropsPlugin or BezierPlugin some other plugin to do the actual tweening, so we create a plugin instance and store a reference here. We need this reference so that if we get a request to round values or disable a tween, we can pass along that request. 3360 * - data: Arbitrary data that needs to be stored with the CSSPropTween. Typically if we're going to have a plugin handle the tweening of a complex-value tween, we create a generic object that stores the END values that we're tweening to and the CSSPropTween's xs1, xs2, etc. have the starting values. We store that object as data. That way, we can simply pass that object to the plugin and use the CSSPropTween as the target. 3361 * - setRatio: Only used for type:2 tweens that require custom functionality. In this case, we call the CSSPropTween's setRatio() method and pass the ratio each time the tween updates. This isn't quite as efficient as doing things directly in the CSSPlugin's setRatio() method, but it's very convenient and flexible. 3362 * @param {!Object} t Target object whose property will be tweened. Often a DOM element, but not always. It could be anything. 3363 * @param {string} p Property to tween (name). For example, to tween element.width, p would be "width". 3364 * @param {number} s Starting numeric value 3365 * @param {number} c Change in numeric value over the course of the entire tween. For example, if element.width starts at 5 and should end at 100, c would be 95. 3366 * @param {CSSPropTween=} next The next CSSPropTween in the linked list. If one is defined, we will define its _prev as the new instance, and the new instance's _next will be pointed at it. 3367 * @param {number=} type The type of CSSPropTween where -1 = a non-tweening value, 0 = a standard simple tween, 1 = a complex value (like one that has multiple numbers in a comma- or space-delimited string like border:"1px solid red"), and 2 = one that uses a custom setRatio function that does all of the work of applying the values on each update. 3368 * @param {string=} n Name of the property that should be used for overwriting purposes which is typically the same as p but not always. For example, we may need to create a subtween for the 2nd part of a "clip:rect(...)" tween in which case "p" might be xs1 but "n" is still "clip" 3369 * @param {boolean=} r If true, the value(s) should be rounded 3370 * @param {number=} pr Priority in the linked list order. Higher priority CSSPropTweens will be updated before lower priority ones. The default priority is 0. 3371 * @param {string=} b Beginning value. We store this to ensure that it is EXACTLY what it was when the tween began without any risk of interpretation issues. 3372 * @param {string=} e Ending value. We store this to ensure that it is EXACTLY what the user defined at the end of the tween without any risk of interpretation issues. 3373 */ 3374 CSSPropTween = _internals.CSSPropTween = function(t, p, s, c, next, type, n, r, pr, b, e) { 3375 this.t = t; //target 3376 this.p = p; //property 3377 this.s = s; //starting value 3378 this.c = c; //change value 3379 this.n = n || p; //name that this CSSPropTween should be associated to (usually the same as p, but not always - n is what overwriting looks at) 3380 if (!(t instanceof CSSPropTween)) { 3381 _overwriteProps.push(this.n); 3382 } 3383 this.r = !r ? r : (typeof(r) === "function") ? r : Math.round; //round (boolean) 3384 this.type = type || 0; //0 = normal tween, -1 = non-tweening (in which case xs0 will be applied to the target's property, like tp.t[tp.p] = tp.xs0), 1 = complex-value SpecialProp, 2 = custom setRatio() that does all the work 3385 if (pr) { 3386 this.pr = pr; 3387 _hasPriority = true; 3388 } 3389 this.b = (b === undefined) ? s : b; 3390 this.e = (e === undefined) ? s + c : e; 3391 if (next) { 3392 this._next = next; 3393 next._prev = this; 3394 } 3395 }, 3396 3397 _addNonTweeningNumericPT = function(target, prop, start, end, next, overwriteProp) { //cleans up some code redundancies and helps minification. Just a fast way to add a NUMERIC non-tweening CSSPropTween
3398 var pt = new CSSPropTween(target, prop, start, end - start, next, -1, overwriteProp); 3399 pt.b = start; 3400 pt.e = pt.xs0 = end; 3401 return pt; 3402 }, 3403 3404 /** 3405 * Takes a target, the beginning value and ending value (as strings) and parses them into a CSSPropTween (possibly with child CSSPropTweens) that accommodates multiple numbers, colors, comma-delimited values, etc. For example: 3406 * sp.parseComplex(element, "boxShadow", "5px 10px 20px rgb(255,102,51)", "0px 0px 0px red", true, "0px 0px 0px rgb(0,0,0,0)", pt); 3407 * It will walk through the beginning and ending values (which should be in the same format with the same number and type of values) and figure out which parts are numbers, what strings separate the numeric/tweenable values, and then create the CSSPropTweens accordingly. If a plugin is defined, no child CSSPropTweens will be created. Instead, the ending values will be stored in the "data" property of the returned CSSPropTween like: {s:-5, xn1:-10, xn2:-20, xn3:255, xn4:0, xn5:0} so that it can be fed to any other plugin and it'll be plain numeric tweens but the recomposition of the complex value will be handled inside CSSPlugin's setRatio(). 3408 * If a setRatio is defined, the type of the CSSPropTween will be set to 2 and recomposition of the values will be the responsibility of that method. 3409 * 3410 * @param {!Object} t Target whose property will be tweened 3411 * @param {!string} p Property that will be tweened (its name, like "left" or "backgroundColor" or "boxShadow") 3412 * @param {string} b Beginning value 3413 * @param {string} e Ending value 3414 * @param {boolean} clrs If true, the value could contain a color value like "rgb(255,0,0)" or "#F00" or "red". The default is false, so no colors will be recognized (a performance optimization) 3415 * @param {(string|number|Object)} dflt The default beginning value that should be used if no valid beginning value is defined or if the number of values inside the complex beginning and ending values don't match 3416 * @param {?CSSPropTween} pt CSSPropTween instance that is the current head of the linked list (we'll prepend to this). 3417 * @param {number=} pr Priority in the linked list order. Higher priority properties will be updated before lower priority ones. The default priority is 0. 3418 * @param {TweenPlugin=} plugin If a plugin should handle the tweening of extra properties, pass the plugin instance here. If one is defined, then NO subtweens will be created for any extra properties (the properties will be created - just not additional CSSPropTween instances to tween them) because the plugin is expected to do so. However, the end values WILL be populated in the "data" property, like {s:100, xn1:50, xn2:300} 3419 * @param {function(number)=} setRatio If values should be set in a custom function instead of being pieced together in a type:1 (complex-value) CSSPropTween, define that custom function here. 3420 * @return {CSSPropTween} The first CSSPropTween in the linked list which includes the new one(s) added by the parseComplex() call. 3421 */ 3422 _parseComplex = CSSPlugin.parseComplex = function(t, p, b, e, clrs, dflt, pt, pr, plugin, setRatio) { 3423 //DEBUG: _log("parseComplex: "+p+", b: "+b+", e: "+e); 3424 b = b || dflt || ""; 3425 if (typeof(e) === "function") { 3426 e = e(_index, _target); 3427 } 3428 pt = new CSSPropTween(t, p, 0, 0, pt, (setRatio ? 2 : 1), null, false, pr, b, e); 3429 e += ""; //ensures it's a string 3430 if (clrs && _colorExp.test(e + b)) { //if colors are found, normalize the formatting to rgba() or hsla(). 3431 e = [b, e]; 3432 CSSPlugin.colorStringFilter(e); 3433 b = e[0]; 3434 e = e[1]; 3435 } 3436 var ba = b.split(", ").join(",").split(" "), //beginning array 3437 ea = e.split(", ").join(",").split(" "), //ending array 3438 l = ba.length, 3439 autoRound = (_autoRound !== false), 3440 i, xi, ni, bv, ev, bnums, enums, bn, hasAlpha, temp, cv, str, useHSL; 3441 if (e.indexOf(",") !== -1 || b.indexOf(",") !== -1) { 3442 if ((e + b).indexOf("rgb") !== -1 || (e + b).indexOf("hsl") !== -1) { //keep rgb(), rgba(), hsl(), and hsla() values together! (remember, we're splitting on spaces) 3443 ba = ba.join(" ").replace(_commasOutsideParenExp, ", ").split(" "); 3444 ea = ea.join(" ").replace(_commasOutsideParenExp, ", ").split(" "); 3445 } else { 3446 ba = ba.join(" ").split(",").join(", ").split(" "); 3447 ea = ea.join(" ").split(",").join(", ").split(" "); 3448 } 3449 l = ba.length; 3450 } 3451 if (l !== ea.length) { 3452 //DEBUG: _log("mismatched formatting detected on " + p + " (" + b + " vs " + e + ")"); 3453 ba = (dflt || "").split(" "); 3454 l = ba.length; 3455 } 3456 pt.plugin = plugin; 3457 pt.setRatio = setRatio; 3458 _colorExp.lastIndex = 0; 3459 for (i = 0; i < l; i++) { 3460 bv = ba[i]; 3461 ev = ea[i] + ""; 3462 bn = parseFloat(bv); 3463 //if the value begins with a number (most common). It's fine if it has a suffix like px 3464 if (bn || bn === 0) { 3465 pt.appendXtra("", bn, _parseChange(ev, bn), ev.replace(_relNumExp, ""), (autoRound && ev.indexOf("px") !== -1) ? Math.round : false, true); 3466 3467 //if the value is a color 3468 } else if (clrs && _colorExp.test(bv)) { 3469 str = ev.indexOf(")") + 1; 3470 str = ")" + (str ? ev.substr(str) : ""); //if there's a comma or ) at the end, retain it. 3471 useHSL = (ev.indexOf("hsl") !== -1 && _supportsOpacity); 3472 temp = ev; //original string value so we can look for any prefix later. 3473 bv = _parseColor(bv, useHSL); 3474 ev = _parseColor(ev, useHSL); 3475 hasAlpha = (bv.length + ev.length > 6); 3476 if (hasAlpha && !_supportsOpacity && ev[3] === 0) { //older versions of IE don't support rgba(), so if the destination alpha is 0, just use "transparent" for the end color 3477 pt["xs" + pt.l] += pt.l ? " transparent" : "transparent";
3478 pt.e = pt.e.split(ea[i]).join("transparent"); 3479 } else { 3480 if (!_supportsOpacity) { //old versions of IE don't support rgba(). 3481 hasAlpha = false; 3482 } 3483 if (useHSL) { 3484 pt.appendXtra(temp.substr(0, temp.indexOf("hsl")) + (hasAlpha ? "hsla(" : "hsl("), bv[0], _parseChange(ev[0], bv[0]), ",", false, true) 3485 .appendXtra("", bv[1], _parseChange(ev[1], bv[1]), "%,", false) 3486 .appendXtra("", bv[2], _parseChange(ev[2], bv[2]), (hasAlpha ? "%," : "%" + str), false); 3487 } else { 3488 pt.appendXtra(temp.substr(0, temp.indexOf("rgb")) + (hasAlpha ? "rgba(" : "rgb("), bv[0], ev[0] - bv[0], ",", Math.round, true) 3489 .appendXtra("", bv[1], ev[1] - bv[1], ",", Math.round) 3490 .appendXtra("", bv[2], ev[2] - bv[2], (hasAlpha ? "," : str), Math.round); 3491 } 3492 3493 if (hasAlpha) { 3494 bv = (bv.length < 4) ? 1 : bv[3]; 3495 pt.appendXtra("", bv, ((ev.length < 4) ? 1 : ev[3]) - bv, str, false); 3496 } 3497 } 3498 _colorExp.lastIndex = 0; //otherwise the test() on the RegExp could move the lastIndex and taint future results. 3499 3500 } else { 3501 bnums = bv.match(_numExp); //gets each group of numbers in the beginning value string and drops them into an array 3502 3503 //if no number is found, treat it as a non-tweening value and just append the string to the current xs. 3504 if (!bnums) { 3505 pt["xs" + pt.l] += (pt.l || pt["xs" + pt.l]) ? " " + ev : ev; 3506 3507 //loop through all the numbers that are found and construct the extra values on the pt. 3508 } else { 3509 enums = ev.match(_relNumExp); //get each group of numbers in the end value string and drop them into an array. We allow relative values too, like +=50 or -=.5 3510 if (!enums || enums.length !== bnums.length) { 3511 //DEBUG: _log("mismatched formatting detected on " + p + " (" + b + " vs " + e + ")"); 3512 return pt; 3513 } 3514 ni = 0; 3515 for (xi = 0; xi < bnums.length; xi++) { 3516 cv = bnums[xi]; 3517 temp = bv.indexOf(cv, ni); 3518 pt.appendXtra(bv.substr(ni, temp - ni), Number(cv), _parseChange(enums[xi], cv), "", (autoRound && bv.substr(temp + cv.length, 2) === "px") ? Math.round : false, (xi === 0)); 3519 ni = temp + cv.length; 3520 } 3521 pt["xs" + pt.l] += bv.substr(ni); 3522 } 3523 } 3524 } 3525 //if there are relative values ("+=" or "-=" prefix), we need to adjust the ending value to eliminate the prefixes and combine the values properly. 3526 if (e.indexOf("=") !== -1) if (pt.data) { 3527 str = pt.xs0 + pt.data.s; 3528 for (i = 1; i < pt.l; i++) { 3529 str += pt["xs" + i] + pt.data["xn" + i]; 3530 } 3531 pt.e = str + pt["xs" + i]; 3532 } 3533 if (!pt.l) { 3534 pt.type = -1; 3535 pt.xs0 = pt.e; 3536 } 3537 return pt.xfirst || pt; 3538 }, 3539 i = 9; 3540 3541 3542 p = CSSPropTween.prototype; 3543 p.l = p.pr = 0; //length (number of extra properties like xn1, xn2, xn3, etc. 3544 while (--i > 0) { 3545 p["xn" + i] = 0; 3546 p["xs" + i] = ""; 3547 } 3548 p.xs0 = ""; 3549 p._next = p._prev = p.xfirst = p.data = p.plugin = p.setRatio = p.rxp = null; 3550 3551 3552 /** 3553 * Appends and extra tweening value to a CSSPropTween and automatically manages any prefix and suffix strings. The first extra value is stored in the s and c of the main CSSPropTween instance, but thereafter any extras are stored in the xn1, xn2, xn3, etc. The prefixes and suffixes are stored in the xs0, xs1, xs2, etc. properties. For example, if I walk through a clip value like "rect(10px, 5px, 0px, 20px)", the values would be stored like this: 3554 * xs0:"rect(", s:10, xs1:"px, ", xn1:5, xs2:"px, ", xn2:0, xs3:"px, ", xn3:20, xn4:"px)" 3555 * And they'd all get joined together when the CSSPlugin renders (in the setRatio() method). 3556 * @param {string=} pfx Prefix (if any) 3557 * @param {!number} s Starting value 3558 * @param {!number} c Change in numeric value over the course of the entire tween. For example, if the start is 5 and the end is 100, the change would be 95. 3559 * @param {string=} sfx Suffix (if any) 3560 * @param {boolean=} r Round (if true). 3561 * @param {boolean=} pad If true, this extra value should be separated by the previous one by a space. If there is no previous extra and pad is true, it will automatically drop the space. 3562 * @return {CSSPropTween} returns itself so that multiple methods can be chained together. 3563 */ 3564 p.appendXtra = function(pfx, s, c, sfx, r, pad) { 3565 var pt = this, 3566 l = pt.l; 3567 pt["xs" + l] += (pad && (l || pt["xs" + l])) ? " " + pfx : pfx || ""; 3568 if (!c) if (l !== 0 && !pt.plugin) { //typically we'll combine non-changing values right into the xs to optimize performance, but we don't combine them when there's a plugin that will be tweening the values because it may depend on the values being split apart, like for a bezier, if a value doesn't change between the first and second iteration but then it does on the 3rd, we'll run into trouble because there's no xn slot for that value! 3569 pt["xs" + l] += s + (sfx || ""); 3570 return pt; 3571 } 3572 pt.l++; 3573 pt.type = pt.setRatio ? 2 : 1; 3574 pt["xs" + pt.l] = sfx || ""; 3575 if (l > 0) { 3576 pt.data["xn" + l] = s + c; 3577 pt.rxp["xn" + l] = r; //round extra property (we need to tap into this in the _parseToProxy() method) 3578 pt["xn" + l] = s; 3579 if (!pt.plugin) { 3580 pt.xfirst = new CSSPropTween(pt, "xn" + l, s, c, pt.xfirst || pt, 0, pt.n, r, pt.pr); 3581 pt.xfirst.xs0 = 0; //just to ensure that the property stays numeric which helps modern browsers speed up processing. Remember, in the setRatio() method, we do pt.t[pt.p] = val + pt.xs0 so if pt.xs0 is "" (the default), it'll cast the end value as a string. When a property is a number sometimes and a string sometimes, it prevents the compiler from locking in the data type, slowing things down slightly. 3582 } 3583 return pt; 3584 } 3585 pt.data = {s:s + c}; 3586 pt.rxp = {}; 3587 pt.s = s; 3588 pt.c = c; 3589 pt.r = r; 3590 return pt; 3591 }; 3592 3593 /** 3594 * @constructor A SpecialProp is basically a css property that needs to be treated in a non-standard way, like if it may contain a complex value like boxShadow:"5px 10px 15px rgb(255, 102, 51)" or if it is associated with another plugin like ThrowPropsPlugin or BezierPlugin. Every SpecialProp is associated with a particular property name like "boxShadow" or "throwProps" or "bezier" and it will intercept those values in the vars object that's passed to the CSSPlugin and handle them accordingly. 3595 * @param {!string} p Property name (like "boxShadow" or "throwProps") 3596 * @param {Object=}
3596 options An object containing any of the following configuration options: 3597 * - defaultValue: the default value 3598 * - parser: A function that should be called when the associated property name is found in the vars. This function should return a CSSPropTween instance and it should ensure that it is properly inserted into the linked list. It will receive 4 paramters: 1) The target, 2) The value defined in the vars, 3) The CSSPlugin instance (whose _firstPT should be used for the linked list), and 4) A computed style object if one was calculated (this is a speed optimization that allows retrieval of starting values quicker) 3599 * - formatter: a function that formats any value received for this special property (for example, boxShadow could take "5px 5px red" and format it to "5px 5px 0px 0px red" so that both the beginning and ending values have a common order and quantity of values.) 3600 * - prefix: if true, we'll determine whether or not this property requires a vendor prefix (like Webkit or Moz or ms or O) 3601 * - color: set this to true if the value for this SpecialProp may contain color-related values like rgb(), rgba(), etc. 3602 * - priority: priority in the linked list order. Higher priority SpecialProps will be updated before lower priority ones. The default priority is 0. 3603 * - multi: if true, the formatter should accommodate a comma-delimited list of values, like boxShadow could have multiple boxShadows listed out. 3604 * - collapsible: if true, the formatter should treat the value like it's a top/right/bottom/left value that could be collapsed, like "5px" would apply to all, "5px, 10px" would use 5px for top/bottom and 10px for right/left, etc. 3605 * - keyword: a special keyword that can [optionally] be found inside the value (like "inset" for boxShadow). This allows us to validate beginning/ending values to make sure they match (if the keyword is found in one, it'll be added to the other for consistency by default). 3606 */ 3607 var SpecialProp = function(p, options) { 3608 options = options || {}; 3609 this.p = options.prefix ? _checkPropPrefix(p) || p : p; 3610 _specialProps[p] = _specialProps[this.p] = this; 3611 this.format = options.formatter || _getFormatter(options.defaultValue, options.color, options.collapsible, options.multi); 3612 if (options.parser) { 3613 this.parse = options.parser; 3614 } 3615 this.clrs = options.color; 3616 this.multi = options.multi; 3617 this.keyword = options.keyword; 3618 this.dflt = options.defaultValue; 3619 this.pr = options.priority || 0; 3620 }, 3621 3622 //shortcut for creating a new SpecialProp that can accept multiple properties as a comma-delimited list (helps minification). dflt can be an array for multiple values (we don't do a comma-delimited list because the default value may contain commas, like rect(0px,0px,0px,0px)). We attach this method to the SpecialProp class/object instead of using a private _createSpecialProp() method so that we can tap into it externally if necessary, like from another plugin. 3623 _registerComplexSpecialProp = _internals._registerComplexSpecialProp = function(p, options, defaults) { 3624 if (typeof(options) !== "object") { 3625 options = {parser:defaults}; //to make backwards compatible with older versions of BezierPlugin and ThrowPropsPlugin 3626 } 3627 var a = p.split(","), 3628 d = options.defaultValue, 3629 i, temp; 3630 defaults = defaults || [d]; 3631 for (i = 0; i < a.length; i++) { 3632 options.prefix = (i === 0 && options.prefix); 3633 options.defaultValue = defaults[i] || d; 3634 temp = new SpecialProp(a[i], options); 3635 } 3636 }, 3637 3638 //creates a placeholder special prop for a plugin so that the property gets caught the first time a tween of it is attempted, and at that time it makes the plugin register itself, thus taking over for all future tweens of that property. This allows us to not mandate that things load in a particular order and it also allows us to log() an error that informs the user when they attempt to tween an external plugin-related property without loading its .js file. 3639 _registerPluginProp = _internals._registerPluginProp = function(p) { 3640 if (!_specialProps[p]) { 3641 var pluginName = p.charAt(0).toUpperCase() + p.substr(1) + "Plugin"; 3642 _registerComplexSpecialProp(p, {parser:function(t, e, p, cssp, pt, plugin, vars) { 3643 var pluginClass = _globals.com.greensock.plugins[pluginName]; 3644 if (!pluginClass) { 3645 _log("Error: " + pluginName + " js file not loaded."); 3646 return pt; 3647 } 3648 pluginClass._cssRegister(); 3649 return _specialProps[p].parse(t, e, p, cssp, pt, plugin, vars); 3650 }}); 3651 } 3652 }; 3653 3654 3655 p = SpecialProp.prototype; 3656 3657 /** 3658 * Alias for _parseComplex() that automatically plugs in certain values for this SpecialProp, like its property name, whether or not colors should be sensed, the default value, and priority. It also looks for any keyword that the SpecialProp defines (like "inset" for boxSh
3658adow) and ensures that the beginning and ending values have the same number of values for SpecialProps where multi is true (like boxShadow and textShadow can have a comma-delimited list) 3659 * @param {!Object} t target element 3660 * @param {(string|number|object)} b beginning value 3661 * @param {(string|number|object)} e ending (destination) value 3662 * @param {CSSPropTween=} pt next CSSPropTween in the linked list 3663 * @param {TweenPlugin=} plugin If another plugin will be tweening the complex value, that TweenPlugin instance goes here. 3664 * @param {function=} setRatio If a custom setRatio() method should be used to handle this complex value, that goes here. 3665 * @return {CSSPropTween=} First CSSPropTween in the linked list 3666 */ 3667 p.parseComplex = function(t, b, e, pt, plugin, setRatio) { 3668 var kwd = this.keyword, 3669 i, ba, ea, l, bi, ei; 3670 //if this SpecialProp's value can contain a comma-delimited list of values (like boxShadow or textShadow), we must parse them in a special way, and look for a keyword (like "inset" for boxShadow) and ensure that the beginning and ending BOTH have it if the end defines it as such. We also must ensure that there are an equal number of values specified (we can't tween 1 boxShadow to 3 for example) 3671 if (this.multi) if (_commasOutsideParenExp.test(e) || _commasOutsideParenExp.test(b)) { 3672 ba = b.replace(_commasOutsideParenExp, "|").split("|"); 3673 ea = e.replace(_commasOutsideParenExp, "|").split("|"); 3674 } else if (kwd) { 3675 ba = [b]; 3676 ea = [e]; 3677 } 3678 if (ea) { 3679 l = (ea.length > ba.length) ? ea.length : ba.length; 3680 for (i = 0; i < l; i++) { 3681 b = ba[i] = ba[i] || this.dflt; 3682 e = ea[i] = ea[i] || this.dflt; 3683 if (kwd) { 3684 bi = b.indexOf(kwd); 3685 ei = e.indexOf(kwd); 3686 if (bi !== ei) { 3687 if (ei === -1) { //if the keyword isn't in the end value, remove it from the beginning one. 3688 ba[i] = ba[i].split(kwd).join(""); 3689 } else if (bi === -1) { //if the keyword isn't in the beginning, add it. 3690 ba[i] += " " + kwd; 3691 } 3692 } 3693 } 3694 } 3695 b = ba.join(", "); 3696 e = ea.join(", "); 3697 } 3698 return _parseComplex(t, this.p, b, e, this.clrs, this.dflt, pt, this.pr, plugin, setRatio); 3699 }; 3700 3701 /** 3702 * Accepts a target and end value and spits back a CSSPropTween that has been inserted into the CSSPlugin's linked list and conforms with all the conventions we use internally, like type:-1, 0, 1, or 2, setting up any extra property tweens, priority, etc. For example, if we have a boxShadow SpecialProp and call: 3703 * this._firstPT = sp.parse(element, "5px 10px 20px rgb(2550,102,51)", "boxShadow", this); 3704 * It should figure out the starting value of the element's boxShadow, compare it to the provided end value and create all the necessary CSSPropTweens of the appropriate types to tween the boxShadow. The CSSPropTween that gets spit back should already be inserted into the linked list (the 4th parameter is the current head, so prepend to that). 3705 * @param {!Object} t Target object whose property is being tweened 3706 * @param {Object} e End value as provided in the vars object (typically a string, but not always - like a throwProps would be an object). 3707 * @param {!string} p Property name 3708 * @param {!CSSPlugin} cssp The CSSPlugin instance that should be associated with this tween. 3709 * @param {?CSSPropTween} pt The CSSPropTween that is the current head of the linked list (we'll prepend to it) 3710 * @param {TweenPlugin=} plugin If a plugin will be used to tween the parsed value, this is the plugin instance. 3711 * @param {Object=} vars Original vars object that contains the data for parsing. 3712 * @return {CSSPropTween} The first CSSPropTween in the linked list which includes the new one(s) added by the parse() call. 3713 */ 3714 p.parse = function(t, e, p, cssp, pt, plugin, vars) { 3715 return this.parseComplex(t.style, this.format(_getStyle(t, this.p, _cs, false, this.dflt)), this.format(e), pt, plugin); 3716 }; 3717 3718 /** 3719 * Registers a special property that should be intercepted from any "css" objects defined in tweens. This allows you to handle them however you want without CSSPlugin doing it for you. The 2nd parameter should be a function that accepts 3 parameters: 3720 * 1) Target object whose property should be tweened (typically a DOM element) 3721 * 2) The end/destination value (could be a string, number, object, or whatever you want) 3722 * 3) The tween instance (you probably don't need to worry about this, but it can be useful for looking up information like the duration) 3723 * 3724 * Then, your function should return a function which will be called each time the tween gets rendered, passing a numeric "ratio" parameter to your function that indicates the change factor (usually between 0 and 1). For example: 3725 * 3726 * CSSPlugin.registerSpecialProp("myCustomProp", function(target, value, tween) { 3727 * var start = target.style.width;
3728 * return function(ratio) { 3729 * target.style.width = (start + value * ratio) + "px"; 3730 * console.log("set width to " + target.style.width); 3731 * } 3732 * }, 0); 3733 * 3734 * Then, when I do this tween, it will trigger my special property: 3735 * 3736 * TweenLite.to(element, 1, {css:{myCustomProp:100}}); 3737 * 3738 * In the example, of course, we're just changing the width, but you can do anything you want. 3739 * 3740 * @param {!string} name Property name (or comma-delimited list of property names) that should be intercepted and handled by your function. For example, if I define "myCustomProp", then it would handle that portion of the following tween: TweenLite.to(element, 1, {css:{myCustomProp:100}}) 3741 * @param {!function(Object, Object, Object, string):function(number)} onInitTween The function that will be called when a tween of this special property is performed. The function will receive 4 parameters: 1) Target object that should be tweened, 2) Value that was passed to the tween, 3) The tween instance itself (rarely used), and 4) The property name that's being tweened. Your function should return a function that should be called on every update of the tween. That function will receive a single parameter that is a "change factor" value (typically between 0 and 1) indicating the amount of change as a ratio. You can use this to determine how to set the values appropriately in your function. 3742 * @param {number=} priority Priority that helps the engine determine the order in which to set the properties (default: 0). Higher priority properties will be updated before lower priority ones. 3743 */ 3744 CSSPlugin.registerSpecialProp = function(name, onInitTween, priority) { 3745 _registerComplexSpecialProp(name, {parser:function(t, e, p, cssp, pt, plugin, vars) { 3746 var rv = new CSSPropTween(t, p, 0, 0, pt, 2, p, false, priority); 3747 rv.plugin = plugin; 3748 rv.setRatio = onInitTween(t, e, cssp._tween, p); 3749 return rv; 3750 }, priority:priority}); 3751 }; 3752 3753 3754 3755 3756 3757 3758 //transform-related methods and properties 3759 CSSPlugin.useSVGTransformAttr = true; //Safari and Firefox both have some rendering bugs when applying CSS transforms to SVG elements, so default to using the "transform" attribute instead (users can override this). 3760 var _transformProps = ("scaleX,scaleY,scaleZ,x,y,z,skewX,skewY,rotation,rotationX,rotationY,perspective,xPercent,yPercent").split(","), 3761 _transformProp = _checkPropPrefix("transform"), //the Javascript (camelCase) transform property, like msTransform, WebkitTransform, MozTransform, or OTransform. 3762 _transformPropCSS = _prefixCSS + "transform", 3763 _transformOriginProp = _checkPropPrefix("transformOrigin"), 3764 _supports3D = (_checkPropPrefix("perspective") !== null), 3765 Transform = _internals.Transform = function() { 3766 this.perspective = parseFloat(CSSPlugin.defaultTransformPerspective) || 0; 3767 this.force3D = (CSSPlugin.defaultForce3D === false || !_supports3D) ? false : CSSPlugin.defaultForce3D || "auto"; 3768 }, 3769 _SVGElement = _gsScope.SVGElement, 3770 _useSVGTransformAttr, 3771 //Some browsers (like Firefox and IE) don't honor transform-origin properly in SVG elements, so we need to manually adjust the matrix accordingly. We feature detect here rather than always doing the conversion for certain browsers because they may fix the problem at some point in the future. 3772 3773 _createSVG = function(type, container, attributes) { 3774 var element = _doc.createElementNS("http://www.w3.org/2000/svg", type), 3775 reg = /([a-z])([A-Z])/g, 3776 p; 3777 for (p in attributes) { 3778 element.setAttributeNS(null, p.replace(reg, "$1-$2").toLowerCase(), attributes[p]); 3779 } 3780 container.appendChild(element); 3781 return element; 3782 }, 3783 _docElement = _doc.documentElement || {}, 3784 _forceSVGTransformAttr = (function() { 3785 //IE and Android stock don't support CSS transforms on SVG elements, so we must write them to the "transform" attribute. We populate this variable in the _parseTransform() method, and only if/when we come across an SVG element 3786 var force = _ieVers || (/Android/i.test(_agent) && !_gsScope.chrome), 3787 svg, rect, width; 3788 if (_doc.createElementNS && !force) { //IE8 and earlier doesn't support SVG anyway 3789 svg = _createSVG("svg", _docElement); 3790 rect = _createSVG("rect", svg, {width:100, height:50, x:100}); 3791 width = rect.getBoundingClientRect().width; 3792 rect.style[_transformOriginProp] = "50% 50%"; 3793 rect.style[_transformProp] = "scaleX(0.5)"; 3794 force = (width === rect.getBoundingClientRect().width && !(_is
3794Firefox && _supports3D)); //note: Firefox fails the test even though it does support CSS transforms in 3D. Since we can't push 3D stuff into the transform attribute, we force Firefox to pass the test here (as long as it does truly support 3D). 3795 _docElement.removeChild(svg); 3796 } 3797 return force; 3798 })(), 3799 _parseSVGOrigin = function(e, local, decoratee, absolute, smoothOrigin, skipRecord) { 3800 var tm = e._gsTransform, 3801 m = _getMatrix(e, true), 3802 v, x, y, xOrigin, yOrigin, a, b, c, d, tx, ty, determinant, xOriginOld, yOriginOld; 3803 if (tm) { 3804 xOriginOld = tm.xOrigin; //record the original values before we alter them. 3805 yOriginOld = tm.yOrigin; 3806 } 3807 if (!absolute || (v = absolute.split(" ")).length < 2) { 3808 b = e.getBBox(); 3809 if (b.x === 0 && b.y === 0 && b.width + b.height === 0) { //some browsers (like Firefox) misreport the bounds if the element has zero width and height (it just assumes it's at x:0, y:0), thus we need to manually grab the position in that case. 3810 b = {x: parseFloat(e.hasAttribute("x") ? e.getAttribute("x") : e.hasAttribute("cx") ? e.getAttribute("cx") : 0) || 0, y: parseFloat(e.hasAttribute("y") ? e.getAttribute("y") : e.hasAttribute("cy") ? e.getAttribute("cy") : 0) || 0, width:0, height:0}; 3811 } 3812 local = _parsePosition(local).split(" "); 3813 v = [(local[0].indexOf("%") !== -1 ? parseFloat(local[0]) / 100 * b.width : parseFloat(local[0])) + b.x, 3814 (local[1].indexOf("%") !== -1 ? parseFloat(local[1]) / 100 * b.height : parseFloat(local[1])) + b.y]; 3815 } 3816 decoratee.xOrigin = xOrigin = parseFloat(v[0]); 3817 decoratee.yOrigin = yOrigin = parseFloat(v[1]); 3818 if (absolute && m !== _identity2DMatrix) { //if svgOrigin is being set, we must invert the matrix and determine where the absolute point is, factoring in the current transforms. Otherwise, the svgOrigin would be based on the element's non-transformed position on the canvas. 3819 a = m[0]; 3820 b = m[1]; 3821 c = m[2]; 3822 d = m[3]; 3823 tx = m[4]; 3824 ty = m[5]; 3825 determinant = (a * d - b * c); 3826 if (determinant) { //if it's zero (like if scaleX and scaleY are zero), skip it to avoid errors with dividing by zero. 3827 x = xOrigin * (d / determinant) + yOrigin * (-c / determinant) + ((c * ty - d * tx) / determinant); 3828 y = xOrigin * (-b / determinant) + yOrigin * (a / determinant) - ((a * ty - b * tx) / determinant); 3829 xOrigin = decoratee.xOrigin = v[0] = x; 3830 yOrigin = decoratee.yOrigin = v[1] = y; 3831 } 3832 } 3833 if (tm) { //avoid jump when transformOrigin is changed - adjust the x/y values accordingly 3834 if (skipRecord) { 3835 decoratee.xOffset = tm.xOffset; 3836 decoratee.yOffset = tm.yOffset; 3837 tm = decoratee; 3838 } 3839 if (smoothOrigin || (smoothOrigin !== false && CSSPlugin.defaultSmoothOrigin !== false)) { 3840 x = xOrigin - xOriginOld; 3841 y = yOrigin - yOriginOld; 3842 //originally, we simply adjusted the x and y values, but that would cause problems if, for example, you created a rotational tween part-way through an x/y tween. Managing the offset in a separate variable gives us ultimate flexibility. 3843 //tm.x -= x - (x * m[0] + y * m[2]); 3844 //tm.y -= y - (x * m[1] + y * m[3]); 3845 tm.xOffset += (x * m[0] + y * m[2]) - x; 3846 tm.yOffset += (x * m[1] + y * m[3]) - y; 3847 } else { 3848 tm.xOffset = tm.yOffset = 0; 3849 } 3850 } 3851 if (!skipRecord) { 3852 e.setAttribute("data-svg-origin", v.join(" ")); 3853 } 3854 }, 3855 _getBBoxHack = function(swapIfPossible) { //works around issues in some browsers (like Firefox) that don't correctly report getBBox() on SVG elements inside a <defs> element and/or <mask>. We try creating an SVG, adding it to the documentElement and toss the element in there so that it's definitely part of the rendering tree, then grab the bbox and if it works, we actually swap out the original getBBox() method for our own that does these extra steps whenever getBBox is needed. This helps ensure that performance is optimal (only do all these extra steps when absolutely necessary...most elements don't need it). 3856 var svg = _createElement("svg", (this.ownerSVGElement && this.ownerSVGElement.getAttribute("xmlns")) || "http://www.w3.org/2000/svg"), 3857 oldParent = this.parentNode, 3858 oldSibling = this.nextSibling, 3859 oldCSS = this.style.cssText, 3860 bbox; 3861 _docElement.appendChild(svg); 3862 svg.appendChild(this); 3863 this.style.display = "block"; 3864 if (swapIfPossible) { 3865 try { 3866 bbox = this.getBBox(); 3867 this._originalGetBBox = this.getBBox; 3868 this.getBBox = _getBBoxHack; 3869 } catch (e) { } 3870 } else if (this._originalGetBBox) { 3871 bbox = this._originalGetBBox(); 3872 } 3873 if (oldSibling) { 3874 oldParent.insertBefore(this, oldSibling); 3875 } else { 3876 oldParent.appendChild(this); 3877 } 3878 _docElement.removeChild(svg); 3879 this.style.cssText = oldCSS; 3880 return bbox; 3881 }, 3882 _getBBox = function(e) { 3883 try { 3884 return e.getBBox(); //Firefox throws errors if you try calling getBBox() on an SVG element that's not rendered (like in a <symbol> or <defs>). https://bugzilla.mozilla.org/show_bug.cgi?id=612118 3885 } catch (error) { 3886 return _getBBoxHack.call(e, true); 3887 } 3888 }, 3889 _isSVG = function(e) { //reports if the element is an SVG on which getBBox() actually works 3890 return !!(_SVGElement && e.getCTM && (!e.parentNode || e.ownerSVGElement) && _getBBox(e)); 3891 }, 3892 _identity2DMatrix = [1,0,0,1,0,0], 3893 _getMatrix = function(e, force2D) { 3894 var tm = e._gsTransform || new Transform(), 3895 rnd = 100000, 3896 style = e.style, 3897 isDefault, s, m, n, dec, none; 3898 if (_transformProp) { 3899 s = _getStyle(e, _transformPropCSS, null, true); 3900 } else if (e.currentStyle) { 3901 //for older versions of IE, we need to interpret the filter portion that is in the format: progid:DXImageTransform.Microsoft.Matrix(M11=6.123233995736766e-17, M12=-1, M21=1, M22=6.123233995736766e-17, sizingMethod='auto expand') Notice that we need to swap b and c compared to a normal matrix. 3902 s = e.currentStyle.filter.match(_ieGetMatrixExp); 3903 s = (s && s.length === 4) ? [s[0].substr(4), Number(s[2].substr(4)), Number(s[1].substr(4)), s[3].substr(4), (tm.x || 0), (tm.y || 0)].join(",") : ""; 3904 }
3905 isDefault = (!s || s === "none" || s === "matrix(1, 0, 0, 1, 0, 0)"); 3906 if (_transformProp && ((none = (!_getComputedStyle(e) || _getComputedStyle(e).display === "none")) || !e.parentNode)) { //note: Firefox returns null for getComputedStyle() if the element is in an iframe that has display:none. https://bugzilla.mozilla.org/show_bug.cgi?id=548397 3907 if (none) { //browsers don't report transforms accurately unless the element is in the DOM and has a display value that's not "none". Firefox and Microsoft browsers have a partial bug where they'll report transforms even if display:none BUT not any percentage-based values like translate(-50%, 8px) will be reported as if it's translate(0, 8px). 3908 n = style.display; 3909 style.display = "block"; 3910 } 3911 if (!e.parentNode) { 3912 dec = 1; //flag 3913 _docElement.appendChild(e); 3914 } 3915 s = _getStyle(e, _transformPropCSS, null, true); 3916 isDefault = (!s || s === "none" || s === "matrix(1, 0, 0, 1, 0, 0)"); 3917 if (n) { 3918 style.display = n; 3919 } else if (none) { 3920 _removeProp(style, "display"); 3921 } 3922 if (dec) { 3923 _docElement.removeChild(e); 3924 } 3925 } 3926 if (tm.svg || (e.getCTM && _isSVG(e))) { 3927 if (isDefault && (style[_transformProp] + "").indexOf("matrix") !== -1) { //some browsers (like Chrome 40) don't correctly report transforms that are applied inline on an SVG element (they don't get included in the computed style), so we double-check here and accept matrix values 3928 s = style[_transformProp]; 3929 isDefault = 0; 3930 } 3931 m = e.getAttribute("transform"); 3932 if (isDefault && m) { 3933 m = e.transform.baseVal.consolidate().matrix; //ensures that even complex values like "translate(50,60) rotate(135,0,0)" are parsed because it mashes it into a matrix. 3934 s = "matrix(" + m.a + "," + m.b + "," + m.c + "," + m.d + "," + m.e + "," + m.f + ")"; 3935 isDefault = 0; 3936 } 3937 } 3938 if (isDefault) { 3939 return _identity2DMatrix; 3940 } 3941 //split the matrix values out into an array (m for matrix) 3942 m = (s || "").match(_numExp) || []; 3943 i = m.length; 3944 while (--i > -1) { 3945 n = Number(m[i]); 3946 m[i] = (dec = n - (n |= 0)) ? ((dec * rnd + (dec < 0 ? -0.5 : 0.5)) | 0) / rnd + n : n; //convert strings to Numbers and round to 5 decimal places to avoid issues with tiny numbers. Roughly 20x faster than Number.toFixed(). We also must make sure to round before dividing so that values like 0.9999999999 become 1 to avoid glitches in browser rendering and interpretation of flipped/rotated 3D matrices. And don't just multiply the number by rnd, floor it, and then divide by rnd because the bitwise operations max out at a 32-bit signed integer, thus it could get clipped at a relatively low value (like 22,000.00000 for example). 3947 } 3948 return (force2D && m.length > 6) ? [m[0], m[1], m[4], m[5], m[12], m[13]] : m; 3949 }, 3950 3951 /** 3952 * Parses the transform values for an element, returning an object with x, y, z, scaleX, scaleY, scaleZ, rotation, rotationX, rotationY, skewX, and skewY properties. Note: by default (for performance reasons), all skewing is combined into skewX and rotation but skewY still has a place in the transform object so that we can record how much of the skew is attributed to skewX vs skewY. Remember, a skewY of 10 looks the same as a rotation of 10 and skewX of -10. 3953 * @param {!Object} t target element 3954 * @param {Object=} cs computed style object (optional) 3955 * @param {boolean=} rec if true, the transform values will be recorded to the target element's _gsTransform object, like target._gsTransform = {x:0, y:0, z:0, scaleX:1...} 3956 * @param {boolean=} parse if true, we'll ignore any _gsTransform values that already exist on the element, and force a reparsing of the css (calculated style) 3957 * @return {object}
3957 object containing all of the transform properties/values like {x:0, y:0, z:0, scaleX:1...} 3958 */ 3959 _getTransform = _internals.getTransform = function(t, cs, rec, parse) { 3960 if (t._gsTransform && rec && !parse) { 3961 return t._gsTransform; //if the element already has a _gsTransform, use that. Note: some browsers don't accurately return the calculated style for the transform (particularly for SVG), so it's almost always safest to just use the values we've already applied rather than re-parsing things. 3962 } 3963 var tm = rec ? t._gsTransform || new Transform() : new Transform(), 3964 invX = (tm.scaleX < 0), //in order to interpret things properly, we need to know if the user applied a negative scaleX previously so that we can adjust the rotation and skewX accordingly. Otherwise, if we always interpret a flipped matrix as affecting scaleY and the user only wants to tween the scaleX on multiple sequential tweens, it would keep the negative scaleY without that being the user's intent. 3965 min = 0.00002, 3966 rnd = 100000, 3967 zOrigin = _supports3D ? parseFloat(_getStyle(t, _transformOriginProp, cs, false, "0 0 0").split(" ")[2]) || tm.zOrigin || 0 : 0, 3968 defaultTransformPerspective = parseFloat(CSSPlugin.defaultTransformPerspective) || 0, 3969 m, i, scaleX, scaleY, rotation, skewX; 3970 3971 tm.svg = !!(t.getCTM && _isSVG(t)); 3972 if (tm.svg) { 3973 _parseSVGOrigin(t, _getStyle(t, _transformOriginProp, cs, false, "50% 50%") + "", tm, t.getAttribute("data-svg-origin")); 3974 _useSVGTransformAttr = CSSPlugin.useSVGTransformAttr || _forceSVGTransformAttr; 3975 } 3976 m = _getMatrix(t); 3977 if (m !== _identity2DMatrix) { 3978 3979 if (m.length === 16) { 3980 //we'll only look at these position-related 6 variables first because if x/y/z all match, it's relatively safe to assume we don't need to re-parse everything which risks losing important rotational information (like rotationX:180 plus rotationY:180 would look the same as rotation:180 - there's no way to know for sure which direction was taken based solely on the matrix3d() values) 3981 var a11 = m[0], a21 = m[1], a31 = m[2], a41 = m[3], 3982 a12 = m[4], a22 = m[5], a32 = m[6], a42 = m[7], 3983 a13 = m[8], a23 = m[9], a33 = m[10], 3984 a14 = m[12], a24 = m[13], a34 = m[14], 3985 a43 = m[11], 3986 angle = Math.atan2(a32, a33), 3987 t1, t2, t3, t4, cos, sin; 3988 //we manually compensate for non-zero z component of transformOrigin to work around bugs in Safari 3989 if (tm.zOrigin) { 3990 a34 = -tm.zOrigin; 3991 a14 = a13*a34-m[12]; 3992 a24 = a23*a34-m[13]; 3993 a34 = a33*a34+tm.zOrigin-m[14]; 3994 } 3995 //note for possible future consolidation: rotationX: Math.atan2(a32, a33), rotationY: Math.atan2(-a31, Math.sqrt(a33 * a33 + a32 * a32)), rotation: Math.atan2(a21, a11), skew: Math.atan2(a12, a22). However, it doesn't seem to be quite as reliable as the full-on backwards rotation procedure. 3996 tm.rotationX = angle * _RAD2DEG; 3997 //rotationX 3998 if (angle) { 3999 cos = Math.cos(-angle); 4000 sin = Math.sin(-angle); 4001 t1 = a12*cos+a13*sin; 4002 t2 = a22*cos+a23*sin; 4003 t3 = a32*cos+a33*sin; 4004 a13 = a12*-sin+a13*cos; 4005 a23 = a22*-sin+a23*cos; 4006 a33 = a32*-sin+a33*cos; 4007 a43 = a42*-sin+a43*cos; 4008 a12 = t1; 4009 a22 = t2; 4010 a32 = t3; 4011 } 4012 //rotationY 4013 angle = Math.atan2(-a31, a33); 4014 tm.rotationY = angle * _RAD2DEG; 4015 if (angle) { 4016 cos = Math.cos(-angle); 4017 sin = Math.sin(-angle); 4018 t1 = a11*cos-a13*sin; 4019 t2 = a21*cos-a23*sin; 4020 t3 = a31*cos-a33*sin; 4021 a23 = a21*sin+a23*cos; 4022 a33 = a31*sin+a33*cos; 4023 a43 = a41*sin+a43*cos; 4024 a11 = t1; 4025 a21 = t2; 4026 a31 = t3; 4027 } 4028 //rotationZ 4029 angle = Math.atan2(a21, a11); 4030 tm.rotation = angle * _RAD2DEG; 4031 if (angle) { 4032 cos = Math.cos(angle); 4033 sin = Math.sin(angle); 4034 t1 = a11*cos+a21*sin; 4035 t2 = a12*cos+a22*sin; 4036 t3 = a13*cos+a23*sin; 4037 a21 = a21*cos-a11*sin; 4038 a22 = a22*cos-a12*sin; 4039 a23 = a23*cos-a13*sin; 4040 a11 = t1; 4041 a12 = t2; 4042 a13 = t3; 4043 } 4044 4045 if (tm.rotationX && Math.abs(tm.rotationX) + Math.abs(tm.rotation) > 359.9) { //when rotationY is set, it will often be parsed as 180 degrees different than it should be, and rotationX and rotation both being 180 (it looks the same), so we adjust for that here. 4046 tm.rotationX = tm.rotation = 0; 4047 tm.rotationY = 180 - tm.rotationY; 4048 } 4049 4050 //skewX 4051 angle = Math.atan2(a12, a22); 4052 4053 //scales 4054 tm.scaleX = ((Math.sqrt(a11 * a11 + a21 * a21 + a31 * a31) * rnd + 0.5) | 0) / rnd; 4055 tm.scaleY = ((Math.sqrt(a22 * a22 + a32 * a32) * rnd + 0.5) | 0) / rnd; 4056 tm.scaleZ = ((Math.sqrt(a13 * a13 + a23 * a23 + a33 * a33) * rnd + 0.5) | 0) / rnd; 4057 a11 /= tm.scaleX; 4058 a12 /= tm.scaleY; 4059 a21 /= tm.scaleX; 4060 a22 /= tm.scaleY; 4061 if (Math.abs(angle) > min) { 4062 tm.skewX = angle * _RAD2DEG; 4063 a12 = 0;
4063 //unskews 4064 if (tm.skewType !== "simple") { 4065 tm.scaleY *= 1 / Math.cos(angle); //by default, we compensate the scale based on the skew so that the element maintains a similar proportion when skewed, so we have to alter the scaleY here accordingly to match the default (non-adjusted) skewing that CSS does (stretching more and more as it skews). 4066 } 4067 4068 } else { 4069 tm.skewX = 0; 4070 } 4071 4072 /* //for testing purposes 4073 var transform = "matrix3d(", 4074 comma = ",", 4075 zero = "0"; 4076 a13 /= tm.scaleZ; 4077 a23 /= tm.scaleZ; 4078 a31 /= tm.scaleX; 4079 a32 /= tm.scaleY; 4080 a33 /= tm.scaleZ; 4081 transform += ((a11 < min && a11 > -min) ? zero : a11) + comma + ((a21 < min && a21 > -min) ? zero : a21) + comma + ((a31 < min && a31 > -min) ? zero : a31); 4082 transform += comma + ((a41 < min && a41 > -min) ? zero : a41) + comma + ((a12 < min && a12 > -min) ? zero : a12) + comma + ((a22 < min && a22 > -min) ? zero : a22); 4083 transform += comma + ((a32 < min && a32 > -min) ? zero : a32) + comma + ((a42 < min && a42 > -min) ? zero : a42) + comma + ((a13 < min && a13 > -min) ? zero : a13); 4084 transform += comma + ((a23 < min && a23 > -min) ? zero : a23) + comma + ((a33 < min && a33 > -min) ? zero : a33) + comma + ((a43 < min && a43 > -min) ? zero : a43) + comma; 4085 transform += a14 + comma + a24 + comma + a34 + comma + (tm.perspective ? (1 + (-a34 / tm.perspective)) : 1) + ")"; 4086 console.log(transform); 4087 document.querySelector(".test").style[_transformProp] = transform; 4088 */ 4089 4090 tm.perspective = a43 ? 1 / ((a43 < 0) ? -a43 : a43) : 0; 4091 tm.x = a14; 4092 tm.y = a24; 4093 tm.z = a34; 4094 if (tm.svg) { 4095 tm.x -= tm.xOrigin - (tm.xOrigin * a11 - tm.yOrigin * a12); 4096 tm.y -= tm.yOrigin - (tm.yOrigin * a21 - tm.xOrigin * a22); 4097 } 4098 4099 } else if ((!_supports3D || parse || !m.length || tm.x !== m[4] || tm.y !== m[5] || (!tm.rotationX && !tm.rotationY))) { //sometimes a 6-element matrix is returned even when we performed 3D transforms, like if rotationX and rotationY are 180. In cases like this, we still need to honor the 3D transforms. If we just rely on the 2D info, it could affect how the data is interpreted, like scaleY might get set to -1 or rotation could get offset by 180 degrees. For example, do a TweenLite.to(element, 1, {css:{rotationX:180, rotationY:180}}) and then later, TweenLite.to(element, 1, {css:{rotationX:0}}) and without this conditional logic in place, it'd jump to a state of being unrotated when the 2nd tween starts. Then again, we need to honor the fact that the user COULD alter the transforms outside of CSSPlugin, like by manually applying new css, so we try to sense that by looking at x and y because if those changed, we know the changes were made outside CSSPlugin and we force a reinterpretation of the matrix values. Also, in Webkit browsers, if the element's "display" is "none", its calculated style value will always return empty, so if we've already recorded the values in the _gsTransform object, we'll just rely on those. 4100 var k = (m.length >= 6), 4101 a = k ? m[0] : 1, 4102 b = m[1] || 0, 4103 c = m[2] || 0, 4104 d = k ? m[3] : 1; 4105 tm.x = m[4] || 0; 4106 tm.y = m[5] || 0; 4107 scaleX = Math.sqrt(a * a + b * b); 4108 scaleY = Math.sqrt(d * d + c * c); 4109 rotation = (a || b) ? Math.atan2(b, a) * _RAD2DEG : tm.rotation || 0; //note: if scaleX is 0, we cannot accurately measure rotation. Same for skewX with a scaleY of 0. Therefore, we default to the previously recorded value (or zero if that doesn't exist). 4110 skewX = (c || d) ? Math.atan2(c, d) * _RAD2DEG + rotation : tm.skewX || 0; 4111 tm.scaleX = scaleX; 4112 tm.scaleY = scaleY; 4113 tm.rotation = rotation; 4114 tm.skewX = skewX; 4115 if (_supports3D) { 4116 tm.rotationX = tm.rotationY = tm.z = 0; 4117 tm.perspective = defaultTransformPerspective; 4118 tm.scaleZ = 1; 4119 } 4120 if (tm.svg) { 4121 tm.x -= tm.xOrigin - (tm.xOrigin * a + tm.yOrigin * c); 4122 tm.y -= tm.yOrigin - (tm.xOrigin * b + tm.yOrigin * d); 4123 } 4124 } 4125 if (Math.abs(tm.skewX) > 90 && Math.abs(tm.skewX) < 270) { 4126 if (invX) { 4127 tm.scaleX *= -1; 4128 tm.skewX += (tm.rotation <= 0) ? 180 : -180; 4129 tm.rotation += (tm.rotation <= 0) ? 180 : -180; 4130 } else { 4131 tm.scaleY *= -1; 4132 tm.skewX += (tm.skewX <= 0) ? 180 : -180; 4133 } 4134 } 4135 tm.zOrigin = zOrigin;
4136 //some browsers have a hard time with very small values like 2.4492935982947064e-16 (notice the "e-" towards the end) and would render the object slightly off. So we round to 0 in these cases. The conditional logic here is faster than calling Math.abs(). Also, browsers tend to render a SLIGHTLY rotated object in a fuzzy way, so we need to snap to exactly 0 when appropriate. 4137 for (i in tm) { 4138 if (tm[i] < min) if (tm[i] > -min) { 4139 tm[i] = 0; 4140 } 4141 } 4142 } 4143 //DEBUG: _log("parsed rotation of " + t.getAttribute("id")+": "+(tm.rotationX)+", "+(tm.rotationY)+", "+(tm.rotation)+", scale: "+tm.scaleX+", "+tm.scaleY+", "+tm.scaleZ+", position: "+tm.x+", "+tm.y+", "+tm.z+", perspective: "+tm.perspective+ ", origin: "+ tm.xOrigin+ ","+ tm.yOrigin); 4144 if (rec) { 4145 t._gsTransform = tm; //record to the object's _gsTransform which we use so that tweens can control individual properties independently (we need all the properties to accurately recompose the matrix in the setRatio() method) 4146 if (tm.svg) { //if we're supposed to apply transforms to the SVG element's "transform" attribute, make sure there aren't any CSS transforms applied or they'll override the attribute ones. Also clear the transform attribute if we're using CSS, just to be clean. 4147 if (_useSVGTransformAttr && t.style[_transformProp]) { 4148 TweenLite.delayedCall(0.001, function(){ //if we apply this right away (before anything has rendered), we risk there being no transforms for a brief moment and it also interferes with adjusting the transformOrigin in a tween with immediateRender:true (it'd try reading the matrix and it wouldn't have the appropriate data in place because we just removed it). 4149 _removeProp(t.style, _transformProp); 4150 }); 4151 } else if (!_useSVGTransformAttr && t.getAttribute("transform")) { 4152 TweenLite.delayedCall(0.001, function(){ 4153 t.removeAttribute("transform"); 4154 }); 4155 } 4156 } 4157 } 4158 return tm; 4159 }, 4160 4161 //for setting 2D transforms in IE6, IE7, and IE8 (must use a "filter" to emulate the behavior of modern day browser transforms) 4162 _setIETransformRatio = function(v) { 4163 var t = this.data, //refers to the element's _gsTransform object 4164 ang = -t.rotation * _DEG2RAD, 4165 skew = ang + t.skewX * _DEG2RAD, 4166 rnd = 100000, 4167 a = ((Math.cos(ang) * t.scaleX * rnd) | 0) / rnd, 4168 b = ((Math.sin(ang) * t.scaleX * rnd) | 0) / rnd, 4169 c = ((Math.sin(skew) * -t.scaleY * rnd) | 0) / rnd, 4170 d = ((Math.cos(skew) * t.scaleY * rnd) | 0) / rnd, 4171 style = this.t.style, 4172 cs = this.t.currentStyle, 4173 filters, val; 4174 if (!cs) { 4175 return; 4176 } 4177 val = b; //just for swapping the variables an inverting them (reused "val" to avoid creating another variable in memory). IE's filter matrix uses a non-standard matrix configuration (angle goes the opposite way, and b and c are reversed and inverted) 4178 b = -c; 4179 c = -val; 4180 filters = cs.filter; 4181 style.filter = ""; //remove filters so that we can accurately measure offsetWidth/offsetHeight 4182 var w = this.t.offsetWidth, 4183 h = this.t.offsetHeight, 4184 clip = (cs.position !== "absolute"), 4185 m = "progid:DXImageTransform.Microsoft.Matrix(M11=" + a + ", M12=" + b + ", M21=" + c + ", M22=" + d, 4186 ox = t.x + (w * t.xPercent / 100), 4187 oy = t.y + (h * t.yPercent / 100), 4188 dx, dy; 4189 4190 //if transformOrigin is being used, adjust the offset x and y 4191 if (t.ox != null) { 4192 dx = ((t.oxp) ? w * t.ox * 0.01 : t.ox) - w / 2; 4193 dy = ((t.oyp) ? h * t.oy * 0.01 : t.oy) - h / 2; 4194 ox += dx - (dx * a + dy * b); 4195 oy += dy - (dx * c + dy * d); 4196 } 4197 4198 if (!clip) { 4199 m += ", sizingMethod='auto expand')"; 4200 } else { 4201 dx = (w / 2); 4202 dy = (h / 2); 4203 //translate to ensure that transformations occur around the correct origin (default is center). 4204 m += ", Dx=" + (dx - (dx * a + dy * b) + ox) + ", Dy=" + (dy - (dx * c + dy * d) + oy) + ")"; 4205 } 4206 if (filters.indexOf("DXImageTransform.Microsoft.Matrix(") !== -1) { 4207 style.filter = filters.replace(_ieSetMatrixExp, m); 4208 } else { 4209 style.filter = m + " " + filters;
4209 //we must always put the transform/matrix FIRST (before alpha(opacity=xx)) to avoid an IE bug that slices part of the object when rotation is applied with alpha. 4210 } 4211 4212 //at the end or beginning of the tween, if the matrix is normal (1, 0, 0, 1) and opacity is 100 (or doesn't exist), remove the filter to improve browser performance. 4213 if (v === 0 || v === 1) if (a === 1) if (b === 0) if (c === 0) if (d === 1) if (!clip || m.indexOf("Dx=0, Dy=0") !== -1) if (!_opacityExp.test(filters) || parseFloat(RegExp.$1) === 100) if (filters.indexOf("gradient(" && filters.indexOf("Alpha")) === -1) { 4214 style.removeAttribute("filter"); 4215 } 4216 4217 //we must set the margins AFTER applying the filter in order to avoid some bugs in IE8 that could (in rare scenarios) cause them to be ignored intermittently (vibration). 4218 if (!clip) { 4219 var mult = (_ieVers < 8) ? 1 : -1, //in Internet Explorer 7 and before, the box model is broken, causing the browser to treat the width/height of the actual rotated filtered image as the width/height of the box itself, but Microsoft corrected that in IE8. We must use a negative offset in IE8 on the right/bottom 4220 marg, prop, dif; 4221 dx = t.ieOffsetX || 0; 4222 dy = t.ieOffsetY || 0; 4223 t.ieOffsetX = Math.round((w - ((a < 0 ? -a : a) * w + (b < 0 ? -b : b) * h)) / 2 + ox); 4224 t.ieOffsetY = Math.round((h - ((d < 0 ? -d : d) * h + (c < 0 ? -c : c) * w)) / 2 + oy); 4225 for (i = 0; i < 4; i++) { 4226 prop = _margins[i]; 4227 marg = cs[prop]; 4228 //we need to get the current margin in case it is being tweened separately (we want to respect that tween's changes) 4229 val = (marg.indexOf("px") !== -1) ? parseFloat(marg) : _convertToPixels(this.t, prop, parseFloat(marg), marg.replace(_suffixExp, "")) || 0; 4230 if (val !== t[prop]) { 4231 dif = (i < 2) ? -t.ieOffsetX : -t.ieOffsetY; //if another tween is controlling a margin, we cannot only apply the difference in the ieOffsets, so we essentially zero-out the dx and dy here in that case. We record the margin(s) later so that we can keep comparing them, making this code very flexible. 4232 } else { 4233 dif = (i < 2) ? dx - t.ieOffsetX : dy - t.ieOffsetY; 4234 } 4235 style[prop] = (t[prop] = Math.round( val - dif * ((i === 0 || i === 2) ? 1 : mult) )) + "px"; 4236 } 4237 } 4238 }, 4239 4240 /* translates a super small decimal to a string WITHOUT scientific notation 4241 _safeDecimal = function(n) { 4242 var s = (n < 0 ? -n : n) + "", 4243 a = s.split("e-"); 4244 return (n < 0 ? "-0." : "0.") + new Array(parseInt(a[1], 10) || 0).join("0") + a[0].split(".").join(""); 4245 }, 4246 */ 4247 4248 _setTransformRatio = _internals.set3DTransformRatio = _internals.setTransformRatio = function(v) { 4249 var t = this.data, //refers to the element's _gsTransform object 4250 style = this.t.style, 4251 angle = t.rotation, 4252 rotationX = t.rotationX, 4253 rotationY = t.rotationY, 4254 sx = t.scaleX, 4255 sy = t.scaleY, 4256 sz = t.scaleZ, 4257 x = t.x, 4258 y = t.y, 4259 z = t.z, 4260 isSVG = t.svg, 4261 perspective = t.perspective, 4262 force3D = t.force3D, 4263 skewY = t.skewY, 4264 skewX = t.skewX, 4265 t1, a11, a12, a13, a21, a22, a23, a31, a32, a33, a41, a42, a43, 4266 zOrigin, min, cos, sin, t2, transform, comma, zero, skew, rnd; 4267 if (skewY) { //for performance reasons, we combine all skewing into the skewX and rotation values. Remember, a skewY of 10 degrees looks the same as a rotation of 10 degrees plus a skewX of 10 degrees. 4268 skewX += skewY; 4269 angle += skewY; 4270 } 4271 4272 //check to see if we should render as 2D (and SVGs must use 2D when _useSVGTransformAttr is true) 4273 if (((((v === 1 || v === 0) && force3D === "auto" && (this.tween._totalTime === this.tween._totalDuration || !this.tween._totalTime)) || !force3D) && !z && !perspective && !rotationY && !rotationX && sz === 1) || (_useSVGTransformAttr && isSVG) || !_supports3D) { //on the final render (which could be 0 for a from tween), if there are no 3D aspects, render in 2D to free up memory and improve performance especially on mobile devices. Check the tween's totalTime/totalDuration too in order to make sure it doesn't happen between repeats if it's a repeating tween. 4274 4275 //2D 4276 if (angle || skewX || isSVG) { 4277 angle *= _DEG2RAD; 4278 skew = skewX * _DEG2RAD; 4279 rnd = 100000; 4280 a11 = Math.cos(angle) * sx; 4281 a21 = Math.sin(angle) * sx; 4282 a12 = Math.sin(angle - skew) * -sy;
4283 a22 = Math.cos(angle - skew) * sy; 4284 if (skew && t.skewType === "simple") { //by default, we compensate skewing on the other axis to make it look more natural, but you can set the skewType to "simple" to use the uncompensated skewing that CSS does 4285 t1 = Math.tan(skew - skewY * _DEG2RAD); 4286 t1 = Math.sqrt(1 + t1 * t1); 4287 a12 *= t1; 4288 a22 *= t1; 4289 if (skewY) { 4290 t1 = Math.tan(skewY * _DEG2RAD); 4291 t1 = Math.sqrt(1 + t1 * t1); 4292 a11 *= t1; 4293 a21 *= t1; 4294 } 4295 } 4296 if (isSVG) { 4297 x += t.xOrigin - (t.xOrigin * a11 + t.yOrigin * a12) + t.xOffset; 4298 y += t.yOrigin - (t.xOrigin * a21 + t.yOrigin * a22) + t.yOffset; 4299 if (_useSVGTransformAttr && (t.xPercent || t.yPercent)) { //The SVG spec doesn't support percentage-based translation in the "transform" attribute, so we merge it into the matrix to simulate it. 4300 min = this.t.getBBox(); 4301 x += t.xPercent * 0.01 * min.width; 4302 y += t.yPercent * 0.01 * min.height; 4303 } 4304 min = 0.000001; 4305 if (x < min) if (x > -min) { 4306 x = 0; 4307 } 4308 if (y < min) if (y > -min) { 4309 y = 0; 4310 } 4311 } 4312 transform = (((a11 * rnd) | 0) / rnd) + "," + (((a21 * rnd) | 0) / rnd) + "," + (((a12 * rnd) | 0) / rnd) + "," + (((a22 * rnd) | 0) / rnd) + "," + x + "," + y + ")"; 4313 if (isSVG && _useSVGTransformAttr) {
4314 this.t.setAttribute("transform", "matrix(" + transform); 4315 } else { 4316 //some browsers have a hard time with very small values like 2.4492935982947064e-16 (notice the "e-" towards the end) and would render the object slightly off. So we round to 5 decimal places. 4317 style[_transformProp] = ((t.xPercent || t.yPercent) ? "translate(" + t.xPercent + "%," + t.yPercent + "%) matrix(" : "matrix(") + transform; 4318 } 4319 } else { 4320 style[_transformProp] = ((t.xPercent || t.yPercent) ? "translate(" + t.xPercent + "%," + t.yPercent + "%) matrix(" : "matrix(") + sx + ",0,0," + sy + "," + x + "," + y + ")"; 4321 } 4322 return; 4323 4324 } 4325 if (_isFirefox) { //Firefox has a bug (at least in v25) that causes it to render the transparent part of 32-bit PNG images as black when displayed inside an iframe and the 3D scale is very small and doesn't change sufficiently enough between renders (like if you use a Power4.easeInOut to scale from 0 to 1 where the beginning values only change a tiny amount to begin the tween before accelerating). In this case, we force the scale to be 0.00002 instead which is visually the same but works around the Firefox issue. 4326 min = 0.0001; 4327 if (sx < min && sx > -min) { 4328 sx = sz = 0.00002; 4329 } 4330 if (sy < min && sy > -min) { 4331 sy = sz = 0.00002; 4332 } 4333 if (perspective && !t.z && !t.rotationX && !t.rotationY) { //Firefox has a bug that causes elements to have an odd super-thin, broken/dotted black border on elements that have a perspective set but aren't utilizing 3D space (no rotationX, rotationY, or z). 4334 perspective = 0; 4335 } 4336 } 4337 if (angle || skewX) { 4338 angle *= _DEG2RAD; 4339 cos = a11 = Math.cos(angle); 4340 sin = a21 = Math.sin(angle); 4341 if (skewX) { 4342 angle -= skewX * _DEG2RAD; 4343 cos = Math.cos(angle); 4344 sin = Math.sin(angle); 4345 if (t.skewType === "simple") { //by default, we compensate skewing on the other axis to make it look more natural, but you can set the skewType to "simple" to use the uncompensated skewing that CSS does 4346 t1 = Math.tan((skewX - skewY) * _DEG2RAD); 4347 t1 = Math.sqrt(1 + t1 * t1); 4348 cos *= t1; 4349 sin *= t1; 4350 if (t.skewY) { 4351 t1 = Math.tan(skewY * _DEG2RAD); 4352 t1 = Math.sqrt(1 + t1 * t1); 4353 a11 *= t1; 4354 a21 *= t1; 4355 } 4356 } 4357 } 4358 a12 = -sin; 4359 a22 = cos; 4360 4361 } else if (!rotationY && !rotationX && sz === 1 && !perspective && !isSVG) { //if we're only translating and/or 2D scaling, this is faster... 4362 style[_transformProp] = ((t.xPercent || t.yPercent) ? "translate(" + t.xPercent + "%," + t.yPercent + "%) translate3d(" : "translate3d(") + x + "px," + y + "px," + z +"px)" + ((sx !== 1 || sy !== 1) ? " scale(" + sx + "," + sy + ")" : ""); 4363 return; 4364 } else { 4365 a11 = a22 = 1; 4366 a12 = a21 = 0; 4367 } 4368 // KEY INDEX AFFECTS a[row][column] 4369 // a11 0 rotation, rotationY, scaleX 4370 // a21 1 rotation, rotationY, scaleX 4371 // a31 2 rotationY, scaleX 4372 // a41 3 rotationY, scaleX 4373 // a12 4 rotation, skewX, rotationX, scaleY 4374 // a22 5 rotation, skewX, rotationX, scaleY 4375 // a32 6 rotationX, scaleY 4376 // a42 7 rotationX, scaleY 4377 // a13 8 rotationY, rotationX, scaleZ 4378 // a23 9 rotationY, rotationX, scaleZ 4379 // a33 10 rotationY, rotationX, scaleZ 4380 // a43 11 rotationY, rotationX, perspective, scaleZ 4381 // a14 12 x, zOrigin, svgOrigin 4382 // a24 13 y, zOrigin, svgOrigin 4383 // a34 14 z, zOrigin 4384 // a44 15 4385 // rotation: Math.atan2(a21, a11) 4386 // rotationY: Math.atan2(a13, a33) (or Math.atan2(a13, a11)) 4387 // rotationX: Math.atan2(a32, a33) 4388 a33 = 1; 4389 a13 = a23 = a31 = a32 = a41 = a42 = 0; 4390 a43 = (perspective) ? -1 / perspective : 0; 4391 zOrigin = t.zOrigin; 4392 min = 0.000001; //threshold below which browsers use scientific notation which won't work. 4393 comma = ","; 4394 zero = "0"; 4395 angle = rotationY * _DEG2RAD; 4396 if (angle) { 4397 cos = Math.cos(angle); 4398 sin = Math.sin(angle);
4399 a31 = -sin; 4400 a41 = a43*-sin; 4401 a13 = a11*sin; 4402 a23 = a21*sin; 4403 a33 = cos; 4404 a43 *= cos; 4405 a11 *= cos; 4406 a21 *= cos; 4407 } 4408 angle = rotationX * _DEG2RAD; 4409 if (angle) { 4410 cos = Math.cos(angle); 4411 sin = Math.sin(angle); 4412 t1 = a12*cos+a13*sin; 4413 t2 = a22*cos+a23*sin; 4414 a32 = a33*sin; 4415 a42 = a43*sin; 4416 a13 = a12*-sin+a13*cos; 4417 a23 = a22*-sin+a23*cos; 4418 a33 = a33*cos; 4419 a43 = a43*cos; 4420 a12 = t1; 4421 a22 = t2; 4422 } 4423 if (sz !== 1) { 4424 a13*=sz; 4425 a23*=sz; 4426 a33*=sz; 4427 a43*=sz; 4428 } 4429 if (sy !== 1) { 4430 a12*=sy; 4431 a22*=sy; 4432 a32*=sy; 4433 a42*=sy; 4434 } 4435 if (sx !== 1) { 4436 a11*=sx; 4437 a21*=sx; 4438 a31*=sx; 4439 a41*=sx; 4440 } 4441 4442 if (zOrigin || isSVG) { 4443 if (zOrigin) { 4444 x += a13*-zOrigin; 4445 y += a23*-zOrigin; 4446 z += a33*-zOrigin+zOrigin; 4447 } 4448 if (isSVG) { //due to bugs in some browsers, we need to manage the transform-origin of SVG manually 4449 x += t.xOrigin - (t.xOrigin * a11 + t.yOrigin * a12) + t.xOffset; 4450 y += t.yOrigin - (t.xOrigin * a21 + t.yOrigin * a22) + t.yOffset; 4451 } 4452 if (x < min && x > -min) { 4453 x = zero; 4454 } 4455 if (y < min && y > -min) { 4456 y = zero; 4457 } 4458 if (z < min && z > -min) { 4459 z = 0; //don't use string because we calculate perspective later and need the number. 4460 } 4461 } 4462 4463 //optimized way of concatenating all the values into a string. If we do it all in one shot, it's slower because of the way browsers have to create temp strings and the way it affects memory. If we do it piece-by-piece with +=, it's a bit slower too. We found that doing it in these sized chunks works best overall: 4464 transform = ((t.xPercent || t.yPercent) ? "translate(" + t.xPercent + "%," + t.yPercent + "%) matrix3d(" : "matrix3d("); 4465 transform += ((a11 < min && a11 > -min) ? zero : a11) + comma + ((a21 < min && a21 > -min) ? zero : a21) + comma + ((a31 < min && a31 > -min) ? zero : a31); 4466 transform += comma + ((a41 < min && a41 > -min) ? zero : a41) + comma + ((a12 < min && a12 > -min) ? zero : a12) + comma + ((a22 < min && a22 > -min) ? zero : a22); 4467 if (rotationX || rotationY || sz !== 1) { //performance optimization (often there's no rotationX or rotationY, so we can skip these calculations) 4468 transform += comma + ((a32 < min && a32 > -min) ? zero : a32) + comma + ((a42 < min && a42 > -min) ? zero : a42) + comma + ((a13 < min && a13 > -min) ? zero : a13); 4469 transform += comma + ((a23 < min && a23 > -min) ? zero : a23) + comma + ((a33 < min && a33 > -min) ? zero : a33) + comma + ((a43 < min && a43 > -min) ? zero : a43) + comma; 4470 } else { 4471 transform += ",0,0,0,0,1,0,"; 4472 } 4473 transform += x + comma + y + comma + z + comma + (perspective ? (1 + (-z / perspective)) : 1) + ")"; 4474 4475 style[_transformProp] = transform; 4476 }; 4477 4478 p = Transform.prototype; 4479 p.x = p.y = p.z = p.skewX = p.skewY = p.rotation = p.rotationX = p.rotationY = p.zOrigin = p.xPercent = p.yPercent = p.xOffset = p.yOffset = 0; 4480 p.scaleX = p.scaleY = p.scaleZ = 1; 4481 4482 _registerComplexSpecialProp("transform,scale,scaleX,scaleY,scaleZ,x,y,z,rotation,rotationX,rotationY,rotationZ,skewX,skewY,shortRotation,shortRotationX,shortRotationY,shortRotationZ,transformOrigin,svgOrigin,transformPerspective,directionalRotation,parseTransform,force3D,skewType,xPercent,yPercent,smoothOrigin", {parser:function(t, e, parsingProp, cssp, pt, plugin, vars) { 4483 if (cssp._lastParsedTransform === vars) { return pt; } //only need to parse the transform once, and only if the browser supports it. 4484 cssp._lastParsedTransform = vars; 4485 var scaleFunc = (vars.scale && typeof(vars.scale) === "function") ? vars.scale : 0, //if there's a function-based "scale" value, swap in the resulting numeric value temporarily. Otherwise, if it's called for both scaleX and scaleY independently, they may not match (like if the function uses Math.random()). 4486 swapFunc; 4487 if (typeof(vars[parsingProp]) === "function") { //whatever property triggers the initial parsing might be a function-based value in which case it already got called in parse(), thus we don't want to call it again in here. The most efficient way to avoid this is to temporarily swap the value directly into the vars object, and then after we do all our parsing in this function, we'll swap it back again. 4488 swapFunc = vars[parsingProp]; 4489 vars[parsingProp] = e; 4490 } 4491 if (scaleFunc) { 4492 vars.scale = scaleFunc(_index, t); 4493 } 4494 var originalGSTransform = t._gsTransform, 4495 style = t.style, 4496 min = 0.000001, 4497 i = _transformProps.length, 4498 v = vars, 4499 endRotations = {}, 4500 transformOriginString = "transformOrigin", 4501 m1 = _getTransform(t, _cs, true, v.parseTransform), 4502 orig = v.transform && ((typeof(v.transform) === "function") ? v.transform(_index, _target) : v.transform), 4503 m2, copy, has3D, hasChange, dr, x, y, matrix, p; 4504 m1.skewType = v.skewType || m1.skewType || CSSPlugin.defaultSkewType; 4505 cssp._transform = m1; 4506 if ("rotationZ" in v) { 4507 v.rotation = v.rotationZ; 4508 } 4509 if (orig && typeof(orig) === "string" && _transformProp) { //for values like transform:"rotate(60deg) scale(0.5, 0.8)" 4510 copy = _tempDiv.style; //don't use the original target because it might be SVG in which case some browsers don't report computed style correctly. 4511 copy[_transformProp] = orig; 4512 copy.display = "block"; //if display is "none", the browser often refuses to report the transform properties correctly. 4513 copy.position = "absolute"; 4514 if (orig.indexOf("%") !== -1) { //%-based translations will fail unless we set the width/height to match the original target... 4515 copy.width = _getStyle(t, "width");
4516 copy.height = _getStyle(t, "height"); 4517 } 4518 _doc.body.appendChild(_tempDiv); 4519 m2 = _getTransform(_tempDiv, null, false); 4520 if (m1.skewType === "simple") { //the default _getTransform() reports the skewX/scaleY as if skewType is "compensated", thus we need to adjust that here if skewType is "simple". 4521 m2.scaleY *= Math.cos(m2.skewX * _DEG2RAD); 4522 } 4523 if (m1.svg) { //if it's an SVG element, x/y part of the matrix will be affected by whatever we use as the origin and the offsets, so compensate here... 4524 x = m1.xOrigin; 4525 y = m1.yOrigin; 4526 m2.x -= m1.xOffset; 4527 m2.y -= m1.yOffset; 4528 if (v.transformOrigin || v.svgOrigin) { //if this tween is altering the origin, we must factor that in here. The actual work of recording the transformOrigin values and setting up the PropTween is done later (still inside this function) so we cannot leave the changes intact here - we only want to update the x/y accordingly. 4529 orig = {}; 4530 _parseSVGOrigin(t, _parsePosition(v.transformOrigin), orig, v.svgOrigin, v.smoothOrigin, true); 4531 x = orig.xOrigin; 4532 y = orig.yOrigin; 4533 m2.x -= orig.xOffset - m1.xOffset; 4534 m2.y -= orig.yOffset - m1.yOffset; 4535 } 4536 if (x || y) { 4537 matrix = _getMatrix(_tempDiv, true); 4538 m2.x -= x - (x * matrix[0] + y * matrix[2]); 4539 m2.y -= y - (x * matrix[1] + y * matrix[3]); 4540 } 4541 } 4542 _doc.body.removeChild(_tempDiv); 4543 if (!m2.perspective) { 4544 m2.perspective = m1.perspective; //tweening to no perspective gives very unintuitive results - just keep the same perspective in that case. 4545 } 4546 if (v.xPercent != null) { 4547 m2.xPercent = _parseVal(v.xPercent, m1.xPercent); 4548 } 4549 if (v.yPercent != null) { 4550 m2.yPercent = _parseVal(v.yPercent, m1.yPercent); 4551 } 4552 } else if (typeof(v) === "object") { //for values like scaleX, scaleY, rotation, x, y, skewX, and skewY or transform:{...} (object) 4553 m2 = {scaleX:_parseVal((v.scaleX != null) ? v.scaleX : v.scale, m1.scaleX), 4554 scaleY:_parseVal((v.scaleY != null) ? v.scaleY : v.scale, m1.scaleY), 4555 scaleZ:_parseVal(v.scaleZ, m1.scaleZ), 4556 x:_parseVal(v.x, m1.x), 4557 y:_parseVal(v.y, m1.y), 4558 z:_parseVal(v.z, m1.z), 4559 xPercent:_parseVal(v.xPercent, m1.xPercent), 4560 yPercent:_parseVal(v.yPercent, m1.yPercent), 4561 perspective:_parseVal(v.transformPerspective, m1.perspective)}; 4562 dr = v.directionalRotation; 4563 if (dr != null) { 4564 if (typeof(dr) === "object") { 4565 for (copy in dr) { 4566 v[copy] = dr[copy]; 4567 } 4568 } else { 4569 v.rotation = dr; 4570 } 4571 } 4572 if (typeof(v.x) === "string" && v.x.indexOf("%") !== -1) { 4573 m2.x = 0; 4574 m2.xPercent = _parseVal(v.x, m1.xPercent); 4575 } 4576 if (typeof(v.y) === "string" && v.y.indexOf("%") !== -1) { 4577 m2.y = 0; 4578 m2.yPercent = _parseVal(v.y, m1.yPercent); 4579 } 4580 4581 m2.rotation = _parseAngle(("rotation" in v) ? v.rotation : ("shortRotation" in v) ? v.shortRotation + "_short" : m1.rotation, m1.rotation, "rotation", endRotations); 4582 if (_supports3D) { 4583 m2.rotationX = _parseAngle(("rotationX" in v) ? v.rotationX : ("shortRotationX" in v) ? v.shortRotationX + "_short" : m1.rotationX || 0, m1.rotationX, "rotationX", endRotations); 4584 m2.rotationY = _parseAngle(("rotationY" in v) ? v.rotationY : ("shortRotationY" in v) ? v.shortRotationY + "_short" : m1.rotationY || 0, m1.rotationY, "rotationY", endRotations); 4585 } 4586 m2.skewX = _parseAngle(v.skewX, m1.skewX); 4587 m2.skewY = _parseAngle(v.skewY, m1.skewY); 4588 } 4589 if (_supports3D && v.force3D != null) { 4590 m1.force3D = v.force3D; 4591 hasChange = true; 4592 } 4593 4594 has3D = (m1.force3D || m1.z || m1.rotationX || m1.rotationY || m2.z || m2.rotationX || m2.rotationY || m2.perspective); 4595 if (!has3D && v.scale != null) { 4596 m2.scaleZ = 1; //no need to tween scaleZ. 4597 } 4598 4599 while (--i > -1) { 4600 p = _transformProps[i]; 4601 orig = m2[p] - m1[p]; 4602 if (orig > min || orig < -min || v[p] != null || _forcePT[p] != null) { 4603 hasChange = true; 4604 pt = new CSSPropTween(m1, p, m1[p], orig, pt); 4605 if (p in endRotations) { 4606 pt.e = endRotations[p]; //directional rotations typically have compensated values during the tween, but we need to make sure they end at exactly what the user requested 4607 } 4608 pt.xs0 = 0; //ensures the value stays numeric in setRatio() 4609 pt.plugin = plugin; 4610 cssp._overwriteProps.push(pt.n); 4611 } 4612 } 4613 4614 orig = v.transformOrigin; 4615 if (m1.svg && (orig || v.svgOrigin)) { 4616 x = m1.xOffset; //when we change the origin, in order to prevent things from jumping we adjust the x/y so we must record those here so that we can create PropTweens for them and flip them at the same time as the origin 4617 y = m1.yOffset; 4618 _parseSVGOrigin(t, _parsePosition(orig), m2, v.svgOrigin, v.smoothOrigin); 4619 pt = _addNonTweeningNumericPT(m1, "xOrigin", (originalGSTransform ? m1 : m2).xOrigin, m2.xOrigin, pt, transformOriginString); //note: if there wasn't a transformOrigin defined yet, just start with the destination one; it's wasteful otherwise, and it causes problems with fromTo() tweens. For example, TweenLite.to("#wheel", 3, {rotation:180, transformOrigin:"50% 50%", delay:1}); TweenLite.fromTo("#wheel", 3, {scale:0.5, transformOrigin:"50% 50%"}, {scale:1, delay:2}); would cause a jump when the from values revert at the beginning of the 2nd tween.
4620 pt = _addNonTweeningNumericPT(m1, "yOrigin", (originalGSTransform ? m1 : m2).yOrigin, m2.yOrigin, pt, transformOriginString); 4621 if (x !== m1.xOffset || y !== m1.yOffset) { 4622 pt = _addNonTweeningNumericPT(m1, "xOffset", (originalGSTransform ? x : m1.xOffset), m1.xOffset, pt, transformOriginString); 4623 pt = _addNonTweeningNumericPT(m1, "yOffset", (originalGSTransform ? y : m1.yOffset), m1.yOffset, pt, transformOriginString); 4624 } 4625 orig = "0px 0px"; //certain browsers (like firefox) completely botch transform-origin, so we must remove it to prevent it from contaminating transforms. We manage it ourselves with xOrigin and yOrigin 4626 } 4627 if (orig || (_supports3D && has3D && m1.zOrigin)) { //if anything 3D is happening and there's a transformOrigin with a z component that's non-zero, we must ensure that the transformOrigin's z-component is set to 0 so that we can manually do those calculations to get around Safari bugs. Even if the user didn't specifically define a "transformOrigin" in this particular tween (maybe they did it via css directly). 4628 if (_transformProp) { 4629 hasChange = true; 4630 p = _transformOriginProp; 4631 orig = (orig || _getStyle(t, p, _cs, false, "50% 50%")) + ""; //cast as string to avoid errors 4632 pt = new CSSPropTween(style, p, 0, 0, pt, -1, transformOriginString); 4633 pt.b = style[p]; 4634 pt.plugin = plugin; 4635 if (_supports3D) { 4636 copy = m1.zOrigin; 4637 orig = orig.split(" "); 4638 m1.zOrigin = ((orig.length > 2 && !(copy !== 0 && orig[2] === "0px")) ? parseFloat(orig[2]) : copy) || 0; //Safari doesn't handle the z part of transformOrigin correctly, so we'll manually handle it in the _set3DTransformRatio() method. 4639 pt.xs0 = pt.e = orig[0] + " " + (orig[1] || "50%") + " 0px"; //we must define a z value of 0px specifically otherwise iOS 5 Safari will stick with the old one (if one was defined)! 4640 pt = new CSSPropTween(m1, "zOrigin", 0, 0, pt, -1, pt.n); //we must create a CSSPropTween for the _gsTransform.zOrigin so that it gets reset properly at the beginning if the tween runs backward (as opposed to just setting m1.zOrigin here) 4641 pt.b = copy; 4642 pt.xs0 = pt.e = m1.zOrigin; 4643 } else { 4644 pt.xs0 = pt.e = orig; 4645 } 4646 4647 //for older versions of IE (6-8), we need to manually calculate things inside the setRatio() function. We record origin x and y (ox and oy) and whether or not the values are percentages (oxp and oyp). 4648 } else { 4649 _parsePosition(orig + "", m1); 4650 } 4651 } 4652 if (hasChange) { 4653 cssp._transformType = (!(m1.svg && _useSVGTransformAttr) && (has3D || this._transformType === 3)) ? 3 : 2; //quicker than calling cssp._enableTransforms(); 4654 } 4655 if (swapFunc) { 4656 vars[parsingProp] = swapFunc; 4657 } 4658 if (scaleFunc) { 4659 vars.scale = scaleFunc; 4660 } 4661 return pt; 4662 }, prefix:true}); 4663 4664 _registerComplexSpecialProp("boxShadow", {defaultValue:"0px 0px 0px 0px #999", prefix:true, color:true, multi:true, keyword:"inset"}); 4665 4666 _registerComplexSpecialProp("borderRadius", {defaultValue:"0px", parser:function(t, e, p, cssp, pt, plugin) { 4667 e = this.format(e); 4668 var props = ["borderTopLeftRadius","borderTopRightRadius","borderBottomRightRadius","borderBottomLeftRadius"], 4669 style = t.style, 4670 ea1, i, es2, bs2, bs, es, bn, en, w, h, esfx, bsfx, rel, hn, vn, em; 4671 w = parseFloat(t.offsetWidth); 4672 h = parseFloat(t.offsetHeight); 4673 ea1 = e.split(" "); 4674 for (i = 0; i < props.length; i++) { //if we're dealing with percentages, we must convert things separately for the horizontal and vertical axis! 4675 if (this.p.indexOf("border")) { //older browsers used a prefix 4676 props[i] = _checkPropPrefix(props[i]); 4677 } 4678 bs = bs2 = _getStyle(t, props[i], _cs, false, "0px"); 4679 if (bs.indexOf(" ") !== -1) { 4680 bs2 = bs.split(" "); 4681 bs = bs2[0]; 4682 bs2 = bs2[1]; 4683 } 4684 es = es2 = ea1[i]; 4685 bn = parseFloat(bs); 4686 bsfx = bs.substr((bn + "").length); 4687 rel = (es.charAt(1) === "="); 4688 if (rel) { 4689 en = parseInt(es.charAt(0)+"1", 10); 4690 es = es.substr(2); 4691 en *= parseFloat(es); 4692 esfx = es.substr((en + "").length - (en < 0 ? 1 : 0)) || ""; 4693 } else { 4694 en = parseFloat(es); 4695 esfx = es.substr((en + "").length); 4696 } 4697 if (esfx === "") { 4698 esfx = _suffixMap[p] || bsfx; 4699 } 4700 if (esfx !== bsfx) { 4701 hn = _convertToPixels(t, "borderLeft", bn, bsfx); //horizontal number (we use a bogus "borderLeft" property just because the _convertToPixels() method searches for the keywords "Left", "Right", "Top", and "Bottom" to determine of it's a horizontal or vertical property, and we need "border" in the name so that it knows it should measure relative to the element itself, not its parent. 4702 vn = _convertToPixels(t, "borderTop", bn, bsfx); //vertical number 4703 if (esfx === "%") { 4704 bs = (hn / w * 100) + "%"; 4705 bs2 = (vn / h * 100) + "%"; 4706 } else if (esfx === "em") { 4707 em = _convertToPixels(t, "borderLeft", 1, "em"); 4708 bs = (hn / em) + "em"; 4709 bs2 = (vn / em) + "em"; 4710 } else { 4711 bs = hn + "px"; 4712 bs2 = vn + "px"; 4713 } 4714 if (rel) { 4715 es = (parseFloat(bs) + en) + esfx; 4716 es2 = (parseFloat(bs2) + en) + esfx; 4717 } 4718 } 4719 pt = _parseComplex(style, props[i], bs + " " + bs2, es + " " + es2, false, "0px", pt); 4720 } 4721 return pt; 4722 }, prefix:true, formatter:_getFormatter("0px 0px 0px 0px", false, true)}); 4723 _registerComplexSpecialProp("borderBottomLeftRadius,borderBottomRightRadius,borderTopLeftRadius,borderTopRightRadius", {defaultValue:"0px", parser:function(t, e, p, cssp, pt, plugin) { 4724 return _parseComplex(t.style, p, this.format(_getStyle(t, p, _cs, false, "0px 0px")), this.format(e), false, "0px", pt); 4725 }, prefix:true, formatter:_getFormatter("0px 0px", false, true)}); 4726 _registerComplexSpecialProp("backgroundPosition", {defaultValue:"0 0", parser:function(t, e, p, cssp, pt, plugin) { 4727 var bp = "background-position", 4728 cs = (_cs || _getComputedStyle(t, null)), 4729 bs = this.format( ((cs) ? _ieVers ? cs.getPropertyValue(bp + "-x") + " " + cs.getPropertyValue(bp + "-y") : cs.getPropertyValue(bp) : t.currentStyle.backgroundPositionX + " " + t.currentStyle.backgroundPositionY) || "0 0"), //Internet Explorer doesn't report background-position correctly - we must query background-position-x and background-position-y and combine them (even in IE10). Before IE9, we must do the same with the currentStyle object and use camelCase 4730 es = this.format(e), 4731 ba, ea, i, pct, overlap, src; 4732 if ((bs.indexOf("%") !== -1) !== (es.indexOf("%") !== -1) && es.split(",").length < 2) { 4733 src = _getStyle(t, "backgroundImage").replace(_urlExp, ""); 4734 if (src && src !== "none") { 4735 ba = bs.split(" "); 4736 ea = es.split(" "); 4737 _tempImg.setAttribute("src", src); //set the temp IMG's src to the background-image so that we can measure its width/height 4738 i = 2; 4739 while (--i > -1) { 4740 bs = ba[i]; 4741 pct = (bs.indexOf("%") !== -1); 4742 if (pct !== (ea[i].indexOf("%") !== -1)) { 4743 overlap = (i === 0) ? t.offsetWidth - _tempImg.width : t.offsetHeight - _tempImg.height; 4744 ba[i] = pct ? (parseFloat(bs) / 100 * overlap) + "px" : (parseFloat(bs) / overlap * 100) + "%"; 4745 } 4746 } 4747 bs = ba.join(" "); 4748 } 4749 } 4750 return this.parseComplex(t.style, bs, es, pt, plugin); 4751 }, formatter:_parsePosition}); 4752 _registerComplexSpecialProp("backgroundSize", {defaultValue:"0 0", formatter:function(v) { 4753 v += ""; //ensure it's a string 4754 return (v.substr(0,2) === "co") ? v : _parsePosition(v.indexOf(" ") === -1 ? v + " " + v : v); //if set to something like "100% 100%", Safari typically reports the computed style as just "100%" (no 2nd value), but we should ensure that there are two values, so copy the first one. Otherwise, it'd be interpreted as "100% 0" (wrong). Also remember that it could be "cover" or "contain" which we can't tween but should be able to set. 4755 }}); 4756 _registerComplexSpecialProp("perspective", {defaultValue:"0px", prefix:true}); 4757 _registerComplexSpecialProp("perspectiveOrigin", {defaultValue:"50% 50%", prefix:true}); 4758 _registerComplexSpecialProp("transformStyle", {prefix:true}); 4759 _registerComplexSpecialProp("backfaceVisibility", {prefix:true}); 4760 _registerComplexSpecialProp("userSelect", {prefix:true}); 4761 _registerComplexSpecialProp("margin", {parser:_getEdgeParser("marginTop,marginRight,marginBottom,marginLeft")}); 4762 _registerComplexSpecialProp("padding", {parser:_getEdgeParser("paddingTop,paddingRight,paddingBottom,paddingLeft")}); 4763 _registerComplexSpecialProp("clip", {defaultValue:"rect(0px,0px,0px,0px)", parser:function(t, e, p, cssp, pt, plugin){ 4764 var b, cs, delim; 4765 if (_ieVers < 9) { //IE8 and earlier don't report a "clip" value in the currentStyle - instead, the values are split apart into clipTop, clipRight, clipBottom, and clipLeft. Also, in IE7 and earlier, the values inside rect() are space-delimited, not comma-delimited. 4766 cs = t.currentStyle; 4767 delim = _ieVers < 8 ? " " : ","; 4768 b = "rect(" + cs.clipTop + delim + cs.clipRight + delim + cs.clipBottom + delim + cs.clipLeft + ")"; 4769 e = this.format(e).split(",").join(delim); 4770 } else { 4771 b = this.format(_getStyle(t, this.p, _cs, false, this.dflt)); 4772 e = this.format(e); 4773 } 4774 return this.parseComplex(t.style, b, e, pt, plugin); 4775 }}); 4776 _registerComplexSpecialProp("textShadow", {defaultValue:"0px 0px 0px #999", color:true, multi:true}); 4777 _registerComplexSpecialProp("autoRound,strictUnits", {parser:function(t, e, p, cssp, pt) {return pt;}}); //just so that we can ignore these properties (not tween them) 4778 _registerComplexSpecialProp("border", {defaultValue:"0px solid #000", parser:function(t, e, p, cssp, pt, plugin) { 4779 var bw = _getStyle(t, "borderTopWidth", _cs, false, "0px"), 4780 end = this.format(e).split(" "), 4781 esfx = end[0].replace(_suffixExp, ""); 4782 if (esfx !== "px") { //if we're animating to a non-px value, we need to convert the beginning width to that unit. 4783 bw = (parseFloat(bw) / _convertToPixels(t, "borderTopWidth", 1, esfx)) + esfx; 4784 } 4785 return this.parseComplex(t.style, this.format(bw + " " + _getStyle(t, "borderTopStyle", _cs, false, "solid") + " " + _getStyle(t, "borderTopColor", _cs, false, "#000")), end.join(" "), pt, plugin); 4786 }, color:true, formatter:function(v) { 4787 var a = v.split(" "); 4788 return a[0] + " " + (a[1] || "solid") + " " + (v.match(_colorExp) || ["#000"])[0]; 4789 }}); 4790 _registerComplexSpecialProp("borderWidth", {parser:_getEdgeParser("borderTopWidth,borderRightWidth,borderBottomWidth,borderLeftWidth")}); //Firefox doesn't pick up on borderWidth set in style sheets (only inline). 4791 _registerComplexSpecialProp("float,cssFloat,styleFloat", {parser:function(t, e, p, cssp, pt, plugin) { 4792 var s = t.style, 4793 prop = ("cssFloat" in s) ? "cssFloat" : "styleFloat"; 4794 return new CSSPropTween(s, prop, 0, 0, pt, -1, p, false, 0, s[prop], e); 4795 }}); 4796 4797 //opacity-related 4798 var _setIEOpacityRatio = function(v) { 4799 var t = this.t, //refers to the element's style property 4800 filters = t.filter || _getStyle(this.data, "filter") || "", 4801 val = (this.s + this.c * v) | 0, 4802 skip; 4803 if (val === 100) { //for older versions of IE that need to use a filter to apply opacity, we should remove the filter if opacity hits 1 in order to improve performance, but make sure there isn't a transform (matrix) or gradient in the filters. 4804 if (filters.indexOf("atrix(") === -1 && filters.indexOf("radient(") === -1 && filters.indexOf("oader(") === -1) { 4805 t.removeAttribute("filter"); 4806 skip = (!_getStyle(this.data, "filter")); //if a class is applied that has an alpha filter, it will take effect (we don't want that), so re-apply our alpha filter in that case. We must first remove it and then check. 4807 } else {
4808 t.filter = filters.replace(_alphaFilterExp, ""); 4809 skip = true; 4810 } 4811 } 4812 if (!skip) { 4813 if (this.xn1) { 4814 t.filter = filters = filters || ("alpha(opacity=" + val + ")"); //works around bug in IE7/8 that prevents changes to "visibility" from being applied properly if the filter is changed to a different alpha on the same frame. 4815 } 4816 if (filters.indexOf("pacity") === -1) { //only used if browser doesn't support the standard opacity style property (IE 7 and 8). We omit the "O" to avoid case-sensitivity issues 4817 if (val !== 0 || !this.xn1) { //bugs in IE7/8 won't render the filter properly if opacity is ADDED on the same frame/render as "visibility" changes (this.xn1 is 1 if this tween is an "autoAlpha" tween) 4818 t.filter = filters + " alpha(opacity=" + val + ")"; //we round the value because otherwise, bugs in IE7/8 can prevent "visibility" changes from being applied properly. 4819 } 4820 } else { 4821 t.filter = filters.replace(_opacityExp, "opacity=" + val); 4822 } 4823 } 4824 }; 4825 _registerComplexSpecialProp("opacity,alpha,autoAlpha", {defaultValue:"1", parser:function(t, e, p, cssp, pt, plugin) { 4826 var b = parseFloat(_getStyle(t, "opacity", _cs, false, "1")), 4827 style = t.style, 4828 isAutoAlpha = (p === "autoAlpha"); 4829 if (typeof(e) === "string" && e.charAt(1) === "=") { 4830 e = ((e.charAt(0) === "-") ? -1 : 1) * parseFloat(e.substr(2)) + b; 4831 } 4832 if (isAutoAlpha && b === 1 && _getStyle(t, "visibility", _cs) === "hidden" && e !== 0) { //if visibility is initially set to "hidden", we should interpret that as intent to make opacity 0 (a convenience) 4833 b = 0; 4834 } 4835 if (_supportsOpacity) { 4836 pt = new CSSPropTween(style, "opacity", b, e - b, pt); 4837 } else { 4838 pt = new CSSPropTween(style, "opacity", b * 100, (e - b) * 100, pt); 4839 pt.xn1 = isAutoAlpha ? 1 : 0; //we need to record whether or not this is an autoAlpha so that in the setRatio(), we know to duplicate the setting of the alpha in order to work around a bug in IE7 and IE8 that prevents changes to "visibility" from taking effect if the filter is changed to a different alpha(opacity) at the same time. Setting it to the SAME value first, then the new value works around the IE7/8 bug. 4840 style.zoom = 1; //helps correct an IE issue. 4841 pt.type = 2; 4842 pt.b = "alpha(opacity=" + pt.s + ")"; 4843 pt.e = "alpha(opacity=" + (pt.s + pt.c) + ")"; 4844 pt.data = t; 4845 pt.plugin = plugin; 4846 pt.setRatio = _setIEOpacityRatio; 4847 } 4848 if (isAutoAlpha) { //we have to create the "visibility" PropTween after the opacity one in the linked list so that they run in the order that works properly in IE8 and earlier 4849 pt = new CSSPropTween(style, "visibility", 0, 0, pt, -1, null, false, 0, ((b !== 0) ? "inherit" : "hidden"), ((e === 0) ? "hidden" : "inherit")); 4850 pt.xs0 = "inherit"; 4851 cssp._overwriteProps.push(pt.n); 4852 cssp._overwriteProps.push(p); 4853 } 4854 return pt; 4855 }}); 4856 4857 4858 var _removeProp = function(s, p) { 4859 if (p) { 4860 if (s.removeProperty) { 4861 if (p.substr(0,2) === "ms" || p.substr(0,6) === "webkit") { //Microsoft and some Webkit browsers don't conform to the standard of capitalizing the first prefix character, so we adjust so that when we prefix the caps with a dash, it's correct (otherwise it'd be "ms-transform" instead of "-ms-transform" for IE9, for example) 4862 p = "-" + p; 4863 } 4864 s.removeProperty(p.replace(_capsExp, "-$1").toLowerCase()); 4865 } else { //note: old versions of IE use "removeAttribute()" instead of "removeProperty()" 4866 s.removeAttribute(p); 4867 } 4868 } 4869 }, 4870 _setClassNameRatio = function(v) { 4871 this.t._gsClassPT = this; 4872 if (v === 1 || v === 0) { 4873 this.t.setAttribute("class", (v === 0) ? this.b : this.e); 4874 var mpt = this.data, //first MiniPropTween 4875 s = this.t.style; 4876 while (mpt) { 4877 if (!mpt.v) { 4878 _removeProp(s, mpt.p); 4879 } else { 4880 s[mpt.p] = mpt.v; 4881 } 4882 mpt = mpt._next; 4883 } 4884 if (v === 1 && this.t._gsClassPT === this) { 4885 this.t._gsClassPT = null; 4886 } 4887 } else if (this.t.getAttribute("class") !== this.e) { 4888 this.t.setAttribute("class", this.e); 4889 } 4890 }; 4891 _registerComplexSpecialProp("className", {parser:function(t, e, p, cssp, pt, plugin, vars) { 4892 var b = t.getAttribute("class") || "", //don't use t.className because it doesn't work consistently on SVG elements; getAttribute("class") and setAttribute("class", value") is more reliable. 4893 cssText = t.style.cssText, 4894 difData, bs, cnpt, cnptLookup, mpt; 4895 pt = cssp._classNamePT = new CSSPropTween(t, p, 0, 0, pt, 2); 4896 pt.setRatio = _setClassNameRatio; 4897 pt.pr = -11; 4898 _hasPriority = true; 4899 pt.b = b; 4900 bs = _getAllStyles(t, _cs); 4901 //if there's a className tween already operating on the target, force it to its end so that the necessary inline styles are removed and the class name is applied before we determine the end state (we don't want inline styles interfer
4901ing that were there just for class-specific values) 4902 cnpt = t._gsClassPT; 4903 if (cnpt) { 4904 cnptLookup = {}; 4905 mpt = cnpt.data; //first MiniPropTween which stores the inline styles - we need to force these so that the inline styles don't contaminate things. Otherwise, there's a small chance that a tween could start and the inline values match the destination values and they never get cleaned. 4906 while (mpt) { 4907 cnptLookup[mpt.p] = 1; 4908 mpt = mpt._next; 4909 } 4910 cnpt.setRatio(1); 4911 } 4912 t._gsClassPT = pt; 4913 pt.e = (e.charAt(1) !== "=") ? e : b.replace(new RegExp("(?:\\s|^)" + e.substr(2) + "(?![\\w-])"), "") + ((e.charAt(0) === "+") ? " " + e.substr(2) : ""); 4914 t.setAttribute("class", pt.e); 4915 difData = _cssDif(t, bs, _getAllStyles(t), vars, cnptLookup); 4916 t.setAttribute("class", b); 4917 pt.data = difData.firstMPT; 4918 t.style.cssText = cssText; //we recorded cssText before we swapped classes and ran _getAllStyles() because in cases when a className tween is overwritten, we remove all the related tweening properties from that class change (otherwise class-specific stuff can't override properties we've directly set on the target's style object due to specificity). 4919 pt = pt.xfirst = cssp.parse(t, difData.difs, pt, plugin); //we record the CSSPropTween as the xfirst so that we can handle overwriting propertly (if "className" gets overwritten, we must kill all the properties associated with the className part of the tween, so we can loop through from xfirst to the pt itself) 4920 return pt; 4921 }}); 4922 4923 4924 var _setClearPropsRatio = function(v) { 4925 if (v === 1 || v === 0) if (this.data._totalTime === this.data._totalDuration && this.data.data !== "isFromStart") { //this.data refers to the tween. Only clear at the END of the tween (remember, from() tweens make the ratio go from 1 to 0, so we can't just check that and if the tween is the zero-duration one that's created internally to render the starting values in a from() tween, ignore that because otherwise, for example, from(...{height:100, clearProps:"height", delay:1}) would wipe the height at the beginning of the tween and after 1 second, it'd kick back in). 4926 var s = this.t.style, 4927 transformParse = _specialProps.transform.parse, 4928 a, p, i, clearTransform, transform; 4929 if (this.e === "all") { 4930 s.cssText = ""; 4931 clearTransform = true; 4932 } else { 4933 a = this.e.split(" ").join("").split(","); 4934 i = a.length; 4935 while (--i > -1) { 4936 p = a[i]; 4937 if (_specialProps[p]) { 4938 if (_specialProps[p].parse === transformParse) { 4939 clearTransform = true; 4940 } else { 4941 p = (p === "transformOrigin") ? _transformOriginProp : _specialProps[p].p; //ensures that special properties use the proper browser-specific property name, like "scaleX" might be "-webkit-transform" or "boxShadow" might be "-moz-box-shadow" 4942 } 4943 } 4944 _removeProp(s, p); 4945 } 4946 } 4947 if (clearTransform) { 4948 _removeProp(s, _transformProp); 4949 transform = this.t._gsTransform; 4950 if (transform) { 4951 if (transform.svg) { 4952 this.t.removeAttribute("data-svg-origin"); 4953 this.t.removeAttribute("transform"); 4954 } 4955 delete this.t._gsTransform; 4956 } 4957 } 4958 4959 } 4960 }; 4961 _registerComplexSpecialProp("clearProps", {parser:function(t, e, p, cssp, pt) { 4962 pt = new CSSPropTween(t, p, 0, 0, pt, 2); 4963 pt.setRatio = _setClearPropsRatio; 4964 pt.e = e; 4965 pt.pr = -10; 4966 pt.data = cssp._tween; 4967 _hasPriority = true; 4968 return pt; 4969 }}); 4970 4971 p = "bezier,throwProps,physicsProps,physics2D".split(","); 4972 i = p.length; 4973 while (i--) { 4974 _registerPluginProp(p[i]); 4975 } 4976 4977 4978 4979 4980 4981 4982 4983 4984 p = CSSPlugin.prototype; 4985 p._firstPT = p._lastParsedTransform = p._transform = null; 4986 4987 //gets called when the tween renders for the first time. This kicks everything off, recording start/end values, etc. 4988 p._onInitTween = function(target, vars, tween, index) { 4989 if (!target.nodeType) { //css is only for dom elements 4990 return false; 4991 } 4992 this._target = _target = target; 4993 this._tween = tween; 4994 this._vars = vars; 4995 _index = index; 4996 _autoRound = vars.autoRound; 4997 _hasPriority = false;
4998 _suffixMap = vars.suffixMap || CSSPlugin.suffixMap; 4999 _cs = _getComputedStyle(target, ""); 5000 _overwriteProps = this._overwriteProps; 5001 var style = target.style, 5002 v, pt, pt2, first, last, next, zIndex, tpt, threeD; 5003 if (_reqSafariFix) if (style.zIndex === "") { 5004 v = _getStyle(target, "zIndex", _cs); 5005 if (v === "auto" || v === "") { 5006 //corrects a bug in [non-Android] Safari that prevents it from repainting elements in their new positions if they don't have a zIndex set. We also can't just apply this inside _parseTransform() because anything that's moved in any way (like using "left" or "top" instead of transforms like "x" and "y") can be affected, so it is best to ensure that anything that's tweening has a z-index. Setting "WebkitPerspective" to a non-zero value worked too except that on iOS Safari things would flicker randomly. Plus zIndex is less memory-intensive. 5007 this._addLazySet(style, "zIndex", 0); 5008 } 5009 } 5010 5011 if (typeof(vars) === "string") { 5012 first = style.cssText; 5013 v = _getAllStyles(target, _cs); 5014 style.cssText = first + ";" + vars; 5015 v = _cssDif(target, v, _getAllStyles(target)).difs; 5016 if (!_supportsOpacity && _opacityValExp.test(vars)) { 5017 v.opacity = parseFloat( RegExp.$1 ); 5018 } 5019 vars = v; 5020 style.cssText = first; 5021 } 5022 5023 if (vars.className) { //className tweens will combine any differences they find in the css with the vars that are passed in, so {className:"myClass", scale:0.5, left:20} would work. 5024 this._firstPT = pt = _specialProps.className.parse(target, vars.className, "className", this, null, null, vars); 5025 } else { 5026 this._firstPT = pt = this.parse(target, vars, null); 5027 } 5028 5029 if (this._transformType) { 5030 threeD = (this._transformType === 3); 5031 if (!_transformProp) { 5032 style.zoom = 1; //helps correct an IE issue. 5033 } else if (_isSafari) { 5034 _reqSafariFix = true; 5035 //if zIndex isn't set, iOS Safari doesn't repaint things correctly sometimes (seemingly at random). 5036 if (style.zIndex === "") { 5037 zIndex = _getStyle(target, "zIndex", _cs); 5038 if (zIndex === "auto" || zIndex === "") { 5039 this._addLazySet(style, "zIndex", 0); 5040 } 5041 } 5042 //Setting WebkitBackfaceVisibility corrects 3 bugs: 5043 // 1) [non-Android] Safari skips rendering changes to "top" and "left" that are made on the same frame/render as a transform update. 5044 // 2) iOS Safari sometimes neglects to repaint elements in their new positions. Setting "WebkitPerspective" to a non-zero value worked too except that on iOS Safari things would flicker randomly. 5045 // 3) Safari sometimes displayed odd artifacts when tweening the transform (or WebkitTransform) property, like ghosts of the edges of the element remained. Definitely a browser bug. 5046 //Note: we allow the user to override the auto-setting by defining WebkitBackfaceVisibility in the vars of the tween. 5047 if (_isSafariLT6) { 5048 this._addLazySet(style, "WebkitBackfaceVisibility", this._vars.WebkitBackfaceVisibility || (threeD ? "visible" : "hidden")); 5049 } 5050 } 5051 pt2 = pt; 5052 while (pt2 && pt2._next) { 5053 pt2 = pt2._next; 5054 } 5055 tpt = new CSSPropTween(target, "transform", 0, 0, null, 2); 5056 this._linkCSSP(tpt, null, pt2); 5057 tpt.setRatio = _transformProp ? _setTransformRatio : _setIETransformRatio; 5058 tpt.data = this._transform || _getTransform(target, _cs, true); 5059 tpt.tween = tween; 5060 tpt.pr = -1; //ensures that the transforms get applied after the components are updated. 5061 _overwriteProps.pop(); //we don't want to force the overwrite of all "transform" tweens of the target - we only care about individual transform properties like scaleX, rotation, etc. The CSSPropTween constructor automatically adds the property to _overwriteProps which is why we need to pop() here. 5062 } 5063 5064 if (_hasPriority) { 5065 //reorders the linked list in order of pr (priority) 5066 while (pt) { 5067 next = pt._next; 5068 pt2 = first; 5069 while (pt2 && pt2.pr > pt.pr) { 5070 pt2 = pt2._next; 5071 } 5072 if ((pt._prev = pt2 ? pt2._prev : last)) { 5073 pt._prev._next = pt; 5074 } else { 5075 first = pt; 5076 } 5077 if ((pt._next = pt2)) { 5078 pt2._prev = pt; 5079 } else { 5080 last = pt; 5081 } 5082 pt = next; 5083 } 5084 this._firstPT = first; 5085 } 5086 return true; 5087 }; 5088 5089 5090 p.parse = function(target, vars, pt, plugin) { 5091 var style = target.style, 5092 p, sp, bn, en, bs, es, bsfx, esfx, isStr, rel; 5093 for (p in vars) { 5094 es = vars[p]; //ending value string 5095 if (typeof(es) === "function") { 5096 es = es(_index, _target); 5097 } 5098 sp = _specialProps[p]; //SpecialProp lookup. 5099 if (sp) { 5100 pt = sp.parse(target, es, p, this, pt, plugin, vars); 5101 } else if (p.substr(0,2) === "--") { //for tweening CSS variables (which always start with "--"). To maximize performance and simplicity, we bypass CSSPlugin altogether and just add a normal property tween to the tween instance itself. 5102 this._tween._propLookup[p] = this._addTween.call(this._tween, target.style, "setProperty", _getComputedStyle(target).getPropertyValue(p) + "", es + "", p, false, p); 5103 continue; 5104 } else { 5105 bs = _getStyle(target, p, _cs) + ""; 5106 isStr = (typeof(es) === "string"); 5107 if (p === "color" || p === "fill" || p === "stroke" || p.indexOf("Color") !== -1 || (isStr && _rgbhslExp.test(es))) { //Opera uses background: to define color sometimes in addition to backgroundColor: 5108 if (!isStr) { 5109 es = _parseColor(es); 5110 es = ((es.length > 3) ? "rgba(" : "rgb(") + es.join(",") + ")"; 5111 } 5112 pt = _parseComplex(style, p, bs, es, true, "transparent", pt, 0, plugin); 5113 5114 } else if (isStr && _complexExp.test(es)) { 5115 pt = _parseComplex(style, p, bs, es, true, null, pt, 0, plugin); 5116 5117 } else { 5118 bn = parseFloat(bs); 5119 bsfx = (bn || bn === 0) ? bs.substr((bn + "").length) : ""; //remember, bs could be non-numeric like "normal" for fontWeight, so we should default to a blank suffix in that case. 5120 5121 if (bs === "" || bs === "auto") { 5122 if (p === "width" || p === "height") { 5123 bn = _getDimension(target, p, _cs); 5124 bsfx = "px"; 5125 } else if (p === "left" || p === "top") {
5126 bn = _calculateOffset(target, p, _cs); 5127 bsfx = "px"; 5128 } else { 5129 bn = (p !== "opacity") ? 0 : 1; 5130 bsfx = ""; 5131 } 5132 } 5133 5134 rel = (isStr && es.charAt(1) === "="); 5135 if (rel) { 5136 en = parseInt(es.charAt(0) + "1", 10); 5137 es = es.substr(2); 5138 en *= parseFloat(es); 5139 esfx = es.replace(_suffixExp, ""); 5140 } else { 5141 en = parseFloat(es); 5142 esfx = isStr ? es.replace(_suffixExp, "") : ""; 5143 } 5144 5145 if (esfx === "") { 5146 esfx = (p in _suffixMap) ? _suffixMap[p] : bsfx; //populate the end suffix, prioritizing the map, then if none is found, use the beginning suffix. 5147 } 5148 5149 es = (en || en === 0) ? (rel ? en + bn : en) + esfx : vars[p]; //ensures that any += or -= prefixes are taken care of. Record the end value before normalizing the suffix because we always want to end the tween on exactly what they intended even if it doesn't match the beginning value's suffix. 5150 //if the beginning/ending suffixes don't match, normalize them... 5151 if (bsfx !== esfx) if (esfx !== "" || p === "lineHeight") if (en || en === 0) if (bn) { //note: if the beginning value (bn) is 0, we don't need to convert units! 5152 bn = _convertToPixels(target, p, bn, bsfx); 5153 if (esfx === "%") { 5154 bn /= _convertToPixels(target, p, 100, "%") / 100; 5155 if (vars.strictUnits !== true) { //some browsers report only "px" values instead of allowing "%" with getComputedStyle(), so we assume that if we're tweening to a %, we should start there too unless strictUnits:true is defined. This approach is particularly useful for responsive designs that use from() tweens. 5156 bs = bn + "%"; 5157 } 5158 5159 } else if (esfx === "em" || esfx === "rem" || esfx === "vw" || esfx === "vh") { 5160 bn /= _convertToPixels(target, p, 1, esfx); 5161 5162 //otherwise convert to pixels. 5163 } else if (esfx !== "px") { 5164 en = _convertToPixels(target, p, en, esfx); 5165 esfx = "px"; //we don't use bsfx after this, so we don't need to set it to px too. 5166 } 5167 if (rel) if (en || en === 0) { 5168 es = (en + bn) + esfx; //the changes we made affect relative calculations, so adjust the end value here. 5169 } 5170 } 5171 5172 if (rel) { 5173 en += bn; 5174 } 5175 5176 if ((bn || bn === 0) && (en || en === 0)) { //faster than isNaN(). Also, previously we required en !== bn but that doesn't really gain much performance and it prevents _parseToProxy() from working properly if beginning and ending values match but need to get tweened by an external plugin anyway. For example, a bezier tween where the target starts at left:0 and has these points: [{left:50},{left:0}] wouldn't work properly because when parsing the last point, it'd match the first (current) one and a non-tweening CSSPropTween would be recorded when we actually need a normal tween (type:0) so that things get updated during the tween properly. 5177 pt = new CSSPropTween(style, p, bn, en - bn, pt, 0, p, (_autoRound !== false && (esfx === "px" || p === "zIndex")), 0, bs, es); 5178 pt.xs0 = esfx; 5179 //DEBUG: _log("tween "+p+" from "+pt.b+" ("+bn+esfx+") to "+pt.e+" with suffix: "+pt.xs0); 5180 } else if (style[p] === undefined || !es && (es + "" === "NaN" || es == null)) { 5181 _log("invalid " + p + " tween value: " + vars[p]); 5182 } else { 5183 pt = new CSSPropTween(style, p, en || bn || 0, 0, pt, -1, p, false, 0, bs, es); 5184 pt.xs0 = (es === "none" && (p === "display" || p.indexOf("Style") !== -1)) ? bs : es; //intermediate value should typically be set immediately (end value) except for "display" or things like borderTopStyle, borderBottomStyle, etc. which should use the beginning value during the tween. 5185 //DEBUG: _log("non-tweening value "+p+": "+pt.xs0); 5186 } 5187 } 5188 } 5189 if (plugin) if (pt && !pt.plugin) { 5190 pt.plugin = plugin; 5191 } 5192 } 5193 return pt; 5194 }; 5195 5196 5197 //gets called every time the tween updates, passing the new ratio (typically a value between 0 and 1, but not always (for example, if an Elastic.easeOut is used, the value can jump above 1 mid-tween). It will always start and 0 and end at 1. 5198 p.setRatio = function(v) { 5199 var pt = this._firstPT, 5200 min = 0.000001, 5201 val, str, i; 5202 //at the end of the tween, we set the values to exactly what we received in order to make sure non-tweening values (like "position" or "float" or whatever) are set and so that if the beginning/ending suffixes (units) didn't match and we normalized to px, the value that the user passed in is used here. We check to see if the tween is at its beginning in case it's a from() tween in which case the ratio will actually go from 1 to 0 over the course of the tween (backwards). 5203 if (v === 1 && (this._tween._time === this._tween._duration || this._tween._time === 0)) { 5204 while (pt) { 5205 if (pt.type !== 2) { 5206 if (pt.r && pt.type !== -1) { 5207 val = pt.r(pt.s + pt.c); 5208 if (!pt.type) { 5209 pt.t[pt.p] = val + pt.xs0; 5210 } else if (pt.type === 1) { //complex value (one that typically has multiple numbers inside a string, like "rect(5px,10px,20px,25px)" 5211 i = pt.l; 5212 str = pt.xs0 + val + pt.xs1; 5213 for (i = 1; i < pt.l; i++) {
5214 str += pt["xn"+i] + pt["xs"+(i+1)]; 5215 } 5216 pt.t[pt.p] = str; 5217 } 5218 } else { 5219 pt.t[pt.p] = pt.e; 5220 } 5221 } else { 5222 pt.setRatio(v); 5223 } 5224 pt = pt._next; 5225 } 5226 5227 } else if (v || !(this._tween._time === this._tween._duration || this._tween._time === 0) || this._tween._rawPrevTime === -0.000001) { 5228 while (pt) { 5229 val = pt.c * v + pt.s; 5230 if (pt.r) { 5231 val = pt.r(val); 5232 } else if (val < min) if (val > -min) { 5233 val = 0; 5234 } 5235 if (!pt.type) { 5236 pt.t[pt.p] = val + pt.xs0; 5237 } else if (pt.type === 1) { //complex value (one that typically has multiple numbers inside a string, like "rect(5px,10px,20px,25px)" 5238 i = pt.l; 5239 if (i === 2) { 5240 pt.t[pt.p] = pt.xs0 + val + pt.xs1 + pt.xn1 + pt.xs2; 5241 } else if (i === 3) { 5242 pt.t[pt.p] = pt.xs0 + val + pt.xs1 + pt.xn1 + pt.xs2 + pt.xn2 + pt.xs3; 5243 } else if (i === 4) { 5244 pt.t[pt.p] = pt.xs0 + val + pt.xs1 + pt.xn1 + pt.xs2 + pt.xn2 + pt.xs3 + pt.xn3 + pt.xs4; 5245 } else if (i === 5) { 5246 pt.t[pt.p] = pt.xs0 + val + pt.xs1 + pt.xn1 + pt.xs2 + pt.xn2 + pt.xs3 + pt.xn3 + pt.xs4 + pt.xn4 + pt.xs5; 5247 } else { 5248 str = pt.xs0 + val + pt.xs1; 5249 for (i = 1; i < pt.l; i++) { 5250 str += pt["xn"+i] + pt["xs"+(i+1)]; 5251 } 5252 pt.t[pt.p] = str; 5253 } 5254 5255 } else if (pt.type === -1) { //non-tweening value 5256 pt.t[pt.p] = pt.xs0; 5257 5258 } else if (pt.setRatio) { //custom setRatio() for things like SpecialProps, external plugins, etc. 5259 pt.setRatio(v); 5260 } 5261 pt = pt._next; 5262 } 5263 5264 //if the tween is reversed all the way back to the beginning, we need to restore the original values which may have different units (like % instead of px or em or whatever). 5265 } else { 5266 while (pt) { 5267 if (pt.type !== 2) { 5268 pt.t[pt.p] = pt.b; 5269 } else { 5270 pt.setRatio(v); 5271 } 5272 pt = pt._next; 5273 } 5274 } 5275 }; 5276 5277 /** 5278 * @private 5279 * Forces rendering of the target's transforms (rotation, scale, etc.) whenever the CSSPlugin's setRatio() is called. 5280 * Basically, this tells the CSSPlugin to create a CSSPropTween (type 2) after instantiation that runs last in the linked 5281 * list and calls the appropriate (3D or 2D) rendering function. We separate this into its own method so that we can call 5282 * it from other plugins like BezierPlugin if, for example, it needs to apply an autoRotation and this CSSPlugin 5283 * doesn't have any transform-related properties of its own. You can call this method as many times as you 5284 * want and it won't create duplicate CSSPropTweens. 5285 * 5286 * @param {boolean} threeD if true, it should apply 3D tweens (otherwise, just 2D ones are fine and typically faster) 5287 */ 5288 p._enableTransforms = function(threeD) { 5289 this._transform = this._transform || _getTransform(this._target, _cs, true); //ensures that the element has a _gsTransform property with the appropriate values. 5290 this._transformType = (!(this._transform.svg && _useSVGTransformAttr) && (threeD || this._transformType === 3)) ? 3 : 2; 5291 }; 5292 5293 var lazySet = function(v) { 5294 this.t[this.p] = this.e; 5295 this.data._linkCSSP(this, this._next, null, true); //we purposefully keep this._next even though it'd make sense to null it, but this is a performance optimization, as this happens during the while (pt) {} loop in setRatio() at the bottom of which it sets pt = pt._next, so if we null it, the linked list will be broken in that loop. 5296 }; 5297 /** @private Gives us a way to set a value on the first render (and only the first render). **/ 5298 p._addLazySet = function(t, p, v) { 5299 var pt = this._firstPT = new CSSPropTween(t, p, 0, 0, this._firstPT, 2); 5300 pt.e = v; 5301 pt.setRatio = lazySet; 5302 pt.data = this; 5303 }; 5304 5305 /** @private **/ 5306 p._linkCSSP = function(pt, next, prev, remove) { 5307 if (pt) { 5308 if (next) { 5309 next._prev = pt; 5310 } 5311 if (pt._next) { 5312 pt._next._prev = pt._prev; 5313 } 5314 if (pt._prev) { 5315 pt._prev._next = pt._next; 5316 } else if (this._firstPT === pt) { 5317 this._firstPT = pt._next; 5318 remove = true; //just to prevent resetting this._firstPT 5 lines down in case pt._next is null. (optimized for speed) 5319 } 5320 if (prev) { 5321 prev._next = pt; 5322 } else if (!remove && this._firstPT === null) { 5323 this._firstPT = pt; 5324 } 5325 pt._next = next; 5326 pt._prev = prev; 5327 } 5328 return pt; 5329 }; 5330 5331 p._mod = function(lookup) { 5332 var pt = this._firstPT; 5333 while (pt) { 5334 if (typeof(lookup[pt.p]) === "function") { //only gets called by RoundPropsPlugin (ModifyPlugin manages all the rendering internally for CSSPlugin properties that need modification). Remember, we handle rounding a bit differently in this plugin for performance reasons, leveraging "r" as an indicator that the value should be roun
5334ded internally. 5335 pt.r = lookup[pt.p]; 5336 } 5337 pt = pt._next; 5338 } 5339 }; 5340 5341 //we need to make sure that if alpha or autoAlpha is killed, opacity is too. And autoAlpha affects the "visibility" property. 5342 p._kill = function(lookup) { 5343 var copy = lookup, 5344 pt, p, xfirst; 5345 if (lookup.autoAlpha || lookup.alpha) { 5346 copy = {}; 5347 for (p in lookup) { //copy the lookup so that we're not changing the original which may be passed elsewhere. 5348 copy[p] = lookup[p]; 5349 } 5350 copy.opacity = 1; 5351 if (copy.autoAlpha) { 5352 copy.visibility = 1; 5353 } 5354 } 5355 if (lookup.className && (pt = this._classNamePT)) { //for className tweens, we need to kill any associated CSSPropTweens too; a linked list starts at the className's "xfirst". 5356 xfirst = pt.xfirst; 5357 if (xfirst && xfirst._prev) { 5358 this._linkCSSP(xfirst._prev, pt._next, xfirst._prev._prev); //break off the prev 5359 } else if (xfirst === this._firstPT) { 5360 this._firstPT = pt._next; 5361 } 5362 if (pt._next) { 5363 this._linkCSSP(pt._next, pt._next._next, xfirst._prev); 5364 } 5365 this._classNamePT = null; 5366 } 5367 pt = this._firstPT; 5368 while (pt) { 5369 if (pt.plugin && pt.plugin !== p && pt.plugin._kill) { //for plugins that are registered with CSSPlugin, we should notify them of the kill. 5370 pt.plugin._kill(lookup); 5371 p = pt.plugin; 5372 } 5373 pt = pt._next; 5374 } 5375 return TweenPlugin.prototype._kill.call(this, copy); 5376 }; 5377 5378 5379 5380 //used by cascadeTo() for gathering all the style properties of each child element into an array for comparison. 5381 var _getChildStyles = function(e, props, targets) { 5382 var children, i, child, type; 5383 if (e.slice) { 5384 i = e.length; 5385 while (--i > -1) { 5386 _getChildStyles(e[i], props, targets); 5387 } 5388 return; 5389 } 5390 children = e.childNodes; 5391 i = children.length; 5392 while (--i > -1) { 5393 child = children[i]; 5394 type = child.type; 5395 if (child.style) { 5396 props.push(_getAllStyles(child)); 5397 if (targets) { 5398 targets.push(child); 5399 } 5400 } 5401 if ((type === 1 || type === 9 || type === 11) && child.childNodes.length) { 5402 _getChildStyles(child, props, targets); 5403 } 5404 } 5405 }; 5406 5407 /** 5408 * Typically only useful for className tweens that may affect child elements, this method creates a TweenLite 5409 * and then compares the style properties of all the target's child elements at the tween's start and end, and 5410 * if any are different, it also creates tweens for those and returns an array containing ALL of the resulting 5411 * tweens (so that you can easily add() them to a TimelineLite, for example). The reason this functionality is 5412 * wrapped into a separate static method of CSSPlugin instead of being integrated into all regular className tweens 5413 * is because it creates entirely new tweens that may have completely different targets than the original tween, 5414 * so if they were all lumped into the original tween instance, it would be inconsistent with the rest of the API 5415 * and it would create other problems. For example: 5416 * - If I create a tween of elementA, that tween instance may suddenly change its target to include 50 other elements (unintuitive if I specifically defined the target I wanted) 5417 * - We can't just create new independent tweens because otherwise, what happens if the original/parent tween is reversed or pause or dropped into a TimelineLite for tight control? You'd expect that tween's behavior to affect all the others. 5418 * - Analyzing every style property of every child before and after the tween is an expensive operation when there are many children, so this behavior shouldn't be imposed on all className tweens by default, especially since it's probably rare that this extra functionality is needed. 5419 * 5420 * @param {Object} target object to be tweened 5421 * @param {number} Duration in seconds (or frames for frames-based tweens) 5422 * @param {Object} Object containing the end values, like {className:"newClass", ease:Linear.easeNone} 5423 * @return {Array} An array of TweenLite instances 5424 */ 5425 CSSPlugin.cascadeTo = function(target, duration, vars) { 5426 var tween = TweenLite.to(target, duration, vars), 5427 results = [tween], 5428 b = [], 5429 e = [], 5430 targets = [], 5431 _reservedProps = TweenLite._internals.reservedProps, 5432 i, difs, p, from; 5433 target = tween._targets || tween.target; 5434 _getChildStyles(target, b, targets); 5435 tween.render(duration, true, true); 5436 _getChildStyles(target, e); 5437 tween.render(0, true, true); 5438 tween._enabled(true); 5439 i = targets.length; 5440 while (--i > -1) { 5441 difs = _cssDif(targets[i], b[i], e[i]); 5442 if (difs.firstMPT) { 5443 difs = difs.difs; 5444 for (p in vars) { 5445 if (_reservedProps[p]) { 5446 difs[p] = vars[p]; 5447 } 5448 } 5449 from = {}; 5450 for (p in difs) { 5451 from[p] = b[i][p]; 5452 } 5453 results.push(TweenLite.fromTo(targets[i], duration, from, difs)); 5454 } 5455 } 5456 return results; 5457 }; 5458 5459 TweenPlugin.activate([CSSPlugin]); 5460 return CSSPlugin; 5461 5462 }, true); 5463 5464 5465 5466 5467 5468 5469 5470 5471 5472 5473 5474/* 5475 * ---------------------------------------------------------------- 5476 * RoundPropsPlugin 5477 * ---------------------------------------------------------------- 5478 */ 5479 (function() { 5480 5481 var RoundPropsPlugin = _gsScope._gsDefine.plugin({ 5482 propName: "roundProps", 5483 version: "1.7.0", 5484 priority: -1, 5485 API: 2, 5486 5487 //called when the tween renders for the first time. This is where initial values should be recorded and any setup routines should run. 5488 init: function(target, value, tween) { 5489 this._tween = tween; 5490 return true; 5491 } 5492 5493 }), 5494 _getRoundFunc = function(v) { //pass in 0.1 get a function that'll round to the nearest tenth, or 5 to round to the closest 5, or 0.001 to the closest 1000th, etc. 5495 var p = v < 1 ? Math.pow(10, (v + "").length - 2) : 1; //to avoid floating point math errors (like 24 * 0.1 == 2.4000000000000004), we chop off at a specific number of decimal places (much faster than toFixed() 5496 return function(n) { 5497 return ((Math.round(n / v) * v * p) | 0) / p; 5498 }; 5499 }, 5500 _roundLinkedList = function(node, mod) { 5501 while (node) { 5502 if (!node.f && !node.blob) { 5503 node.m = mod || Math.round; 5504 } 5505 node = node._next; 5506 } 5507 }, 5508 p = RoundPropsPlugin.prototype; 5509 5510 p._onInitAllProps = function() { 5511 var tween = this._tween,
5512 rp = tween.vars.roundProps, 5513 lookup = {}, 5514 rpt = tween._propLookup.roundProps, 5515 pt, next, i, p; 5516 if (typeof(rp) === "object" && !rp.push) { 5517 for (p in rp) { 5518 lookup[p] = _getRoundFunc(rp[p]); 5519 } 5520 } else { 5521 if (typeof(rp) === "string") { 5522 rp = rp.split(","); 5523 } 5524 i = rp.length; 5525 while (--i > -1) { 5526 lookup[rp[i]] = Math.round; 5527 } 5528 } 5529 5530 for (p in lookup) { 5531 pt = tween._firstPT; 5532 while (pt) { 5533 next = pt._next; //record here, because it may get removed 5534 if (pt.pg) { 5535 pt.t._mod(lookup); 5536 } else if (pt.n === p) { 5537 if (pt.f === 2 && pt.t) { //a blob (text containing multiple numeric values) 5538 _roundLinkedList(pt.t._firstPT, lookup[p]); 5539 } else { 5540 this._add(pt.t, p, pt.s, pt.c, lookup[p]); 5541 //remove from linked list 5542 if (next) { 5543 next._prev = pt._prev; 5544 } 5545 if (pt._prev) { 5546 pt._prev._next = next; 5547 } else if (tween._firstPT === pt) { 5548 tween._firstPT = next; 5549 } 5550 pt._next = pt._prev = null; 5551 tween._propLookup[p] = rpt; 5552 } 5553 } 5554 pt = next; 5555 } 5556 } 5557 return false; 5558 }; 5559 5560 p._add = function(target, p, s, c, mod) { 5561 this._addTween(target, p, s, s + c, p, mod || Math.round); 5562 this._overwriteProps.push(p); 5563 }; 5564 5565 }()); 5566 5567 5568 5569 5570 5571 5572 5573 5574 5575 5576/* 5577 * ---------------------------------------------------------------- 5578 * AttrPlugin 5579 * ---------------------------------------------------------------- 5580 */ 5581 5582 (function() { 5583 5584 _gsScope._gsDefine.plugin({ 5585 propName: "attr", 5586 API: 2, 5587 version: "0.6.1", 5588 5589 //called when the tween renders for the first time. This is where initial values should be recorded and any setup routines should run. 5590 init: function(target, value, tween, index) { 5591 var p, end; 5592 if (typeof(target.setAttribute) !== "function") { 5593 return false; 5594 } 5595 for (p in value) { 5596 end = value[p]; 5597 if (typeof(end) === "function") { 5598 end = end(index, target); 5599 } 5600 this._addTween(target, "setAttribute", target.getAttribute(p) + "", end + "", p, false, p); 5601 this._overwriteProps.push(p); 5602 } 5603 return true; 5604 } 5605 5606 }); 5607 5608 }()); 5609 5610 5611 5612 5613 5614 5615 5616 5617 5618 5619/* 5620 * ---------------------------------------------------------------- 5621 * DirectionalRotationPlugin 5622 * ---------------------------------------------------------------- 5623 */ 5624 _gsScope._gsDefine.plugin({ 5625 propName: "directionalRotation", 5626 version: "0.3.1", 5627 API: 2, 5628 5629 //called when the tween renders for the first time. This is where initial values should be recorded and any setup routines should run. 5630 init: function(target, value, tween, index) { 5631 if (typeof(value) !== "object") { 5632 value = {rotation:value}; 5633 } 5634 this.finals = {}; 5635 var cap = (value.useRadians === true) ? Math.PI * 2 : 360, 5636 min = 0.000001, 5637 p, v, start, end, dif, split; 5638 for (p in value) { 5639 if (p !== "useRadians") { 5640 end = value[p]; 5641 if (typeof(end) === "function") { 5642 end = end(index, target); 5643 } 5644 split = (end + "").split("_"); 5645 v = split[0]; 5646 start = parseFloat( (typeof(target[p]) !== "function") ? target[p] : target[ ((p.indexOf("set") || typeof(target["get" + p.substr(3)]) !== "function") ? p : "get" + p.substr(3)) ]() ); 5647 end = this.finals[p] = (typeof(v) === "string" && v.charAt(1) === "=") ? start + parseInt(v.charAt(0) + "1", 10) * Number(v.substr(2)) : Number(v) || 0; 5648 dif = end - start; 5649 if (split.length) { 5650 v = split.join("_"); 5651 if (v.indexOf("short") !== -1) { 5652 dif = dif % cap; 5653 if (dif !== dif % (cap / 2)) { 5654 dif = (dif < 0) ? dif + cap : dif - cap; 5655 } 5656 } 5657 if (v.indexOf("_cw") !== -1 && dif < 0) { 5658 dif = ((dif + cap * 9999999999) % cap) - ((dif / cap) | 0) * cap; 5659 } else if (v.indexOf("ccw") !== -1 && dif > 0) { 5660 dif = ((dif - cap * 9999999999) % cap) - ((dif / cap) | 0) * cap; 5661 } 5662 } 5663 if (dif > min || dif < -min) { 5664 this._addTween(target, p, start, start + dif, p); 5665 this._overwriteProps.push(p); 5666 } 5667 } 5668 } 5669 return true; 5670 }, 5671 5672 //called each time the values should be updated, and the ratio gets passed as the only parameter (typically it's a value between 0 and 1, but it can exceed those when using an ease like Elastic.easeOut or Back.easeOut, etc.)
5673 set: function(ratio) { 5674 var pt; 5675 if (ratio !== 1) { 5676 this._super.setRatio.call(this, ratio); 5677 } else { 5678 pt = this._firstPT; 5679 while (pt) { 5680 if (pt.f) { 5681 pt.t[pt.p](this.finals[pt.p]); 5682 } else { 5683 pt.t[pt.p] = this.finals[pt.p]; 5684 } 5685 pt = pt._next; 5686 } 5687 } 5688 } 5689 5690 })._autoCSS = true; 5691 5692 5693 5694 5695 5696 5697 5698 5699 5700 5701 5702/* 5703 * ---------------------------------------------------------------- 5704 * EasePack 5705 * ---------------------------------------------------------------- 5706 */ 5707 _gsScope._gsDefine("easing.Back", ["easing.Ease"], function(Ease) { 5708 5709 var w = (_gsScope.GreenSockGlobals || _gsScope), 5710 gs = w.com.greensock, 5711 _2PI = Math.PI * 2, 5712 _HALF_PI = Math.PI / 2, 5713 _class = gs._class, 5714 _create = function(n, f) { 5715 var C = _class("easing." + n, function(){}, true), 5716 p = C.prototype = new Ease(); 5717 p.constructor = C; 5718 p.getRatio = f; 5719 return C; 5720 }, 5721 _easeReg = Ease.register || function(){}, //put an empty function in place just as a safety measure in case someone loads an OLD version of TweenLite.js where Ease.register doesn't exist. 5722 _wrap = function(name, EaseOut, EaseIn, EaseInOut, aliases) { 5723 var C = _class("easing."+name, { 5724 easeOut:new EaseOut(), 5725 easeIn:new EaseIn(), 5726 easeInOut:new EaseInOut() 5727 }, true); 5728 _easeReg(C, name); 5729 return C; 5730 }, 5731 EasePoint = function(time, value, next) { 5732 this.t = time; 5733 this.v = value; 5734 if (next) { 5735 this.next = next; 5736 next.prev = this; 5737 this.c = next.v - value; 5738 this.gap = next.t - time; 5739 } 5740 }, 5741 5742 //Back 5743 _createBack = function(n, f) { 5744 var C = _class("easing." + n, function(overshoot) { 5745 this._p1 = (overshoot || overshoot === 0) ? overshoot : 1.70158; 5746 this._p2 = this._p1 * 1.525; 5747 }, true), 5748 p = C.prototype = new Ease(); 5749 p.constructor = C; 5750 p.getRatio = f; 5751 p.config = function(overshoot) { 5752 return new C(overshoot); 5753 }; 5754 return C; 5755 }, 5756 5757 Back = _wrap("Back", 5758 _createBack("BackOut", function(p) { 5759 return ((p = p - 1) * p * ((this._p1 + 1) * p + this._p1) + 1); 5760 }), 5761 _createBack("BackIn", function(p) { 5762 return p * p * ((this._p1 + 1) * p - this._p1); 5763 }), 5764 _createBack("BackInOut", function(p) { 5765 return ((p *= 2) < 1) ? 0.5 * p * p * ((this._p2 + 1) * p - this._p2) : 0.5 * ((p -= 2) * p * ((this._p2 + 1) * p + this._p2) + 2); 5766 }) 5767 ), 5768 5769 5770 //SlowMo 5771 SlowMo = _class("easing.SlowMo", function(linearRatio, power, yoyoMode) { 5772 power = (power || power === 0) ? power : 0.7; 5773 if (linearRatio == null) { 5774 linearRatio = 0.7; 5775 } else if (linearRatio > 1) { 5776 linearRatio = 1; 5777 } 5778 this._p = (linearRatio !== 1) ? power : 0; 5779 this._p1 = (1 - linearRatio) / 2; 5780 this._p2 = linearRatio; 5781 this._p3 = this._p1 + this._p2; 5782 this._calcEnd = (yoyoMode === true); 5783 }, true), 5784 p = SlowMo.prototype = new Ease(), 5785 SteppedEase, ExpoScaleEase, RoughEase, _createElastic; 5786 5787 p.constructor = SlowMo; 5788 p.getRatio = function(p) { 5789 var r = p + (0.5 - p) * this._p; 5790 if (p < this._p1) { 5791 return this._calcEnd ? 1 - ((p = 1 - (p / this._p1)) * p) : r - ((p = 1 - (p / this._p1)) * p * p * p * r); 5792 } else if (p > this._p3) { 5793 return this._calcEnd ? (p === 1 ? 0 : 1 - (p = (p - this._p3) / this._p1) * p) : r + ((p - r) * (p = (p - this._p3) / this._p1) * p * p * p); //added p === 1 ? 0 to avoid floating point rounding errors from affecting the final value, like 1 - 0.7 = 0.30000000000000004 instead of 0.3 5794 } 5795 return this._calcEnd ? 1 : r; 5796 }; 5797 SlowMo.ease = new SlowMo(0.7, 0.7); 5798 5799 p.config = SlowMo.config = function(linearRatio, power, yoyoMode) { 5800 return new SlowMo(linearRatio, power, yoyoMode); 5801 }; 5802 5803 5804 //SteppedEase 5805 SteppedEase = _class("easing.SteppedEase", function(steps, immediateStart) { 5806 steps = steps || 1; 5807 this._p1 = 1 / steps; 5808 this._p2 = steps + (immediateStart ? 0 : 1); 5809 this._p3 = immediateStart ? 1 : 0; 5810 }, true); 5811 p = SteppedEase.prototype = new Ease(); 5812 p.constructor = SteppedEase; 5813 p.getRatio = function(p) { 5814 if (p < 0) { 5815 p = 0; 5816 } else if (p >= 1) { 5817 p = 0.999999999; 5818 } 5819 return (((this._p2 * p) | 0) + this._p3) * this._p1; 5820 }; 5821 p.config = SteppedEase.config = function(steps, immediateStart) { 5822 return new SteppedEase(steps, immediateStart); 5823 }; 5824 5825 //ExpoScaleEase 5826 ExpoScaleEase = _class("easing.ExpoScaleEase", function(start, end, ease) { 5827 this._p1 = Math.log(end / start); 5828 this._p2 = end - start; 5829 this._p3 = start; 5830 this._ease = ease; 5831 }, true); 5832 p = ExpoScaleEase.prototype = new Ease(); 5833 p.constructor = ExpoScaleEase; 5834 p.getRatio = function(p) { 5835 if (this._ease) { 5836 p = this._ease.getRatio(p); 5837 } 5838 return (this._p3 * Math.exp(this._p1 * p) - this._p3) / this._p2; 5839 }; 5840 p.config = ExpoScaleEase.config = function(start, end, ease) { 5841 return new ExpoScaleEase(start, end, ease); 5842 }; 5843 5844 5845 //RoughEase 5846 RoughEase = _class("easing.RoughEase", function(vars) { 5847 vars = vars || {}; 5848 var taper = vars.taper || "none", 5849 a = [], 5850 cnt = 0, 5851 points = (vars.points || 20) | 0, 5852 i = points, 5853 randomize = (vars.randomize !== false), 5854 clamp = (vars.clamp === true), 5855 template = (vars.template instanceof Ease) ? vars.template : null,
5856 strength = (typeof(vars.strength) === "number") ? vars.strength * 0.4 : 0.4, 5857 x, y, bump, invX, obj, pnt; 5858 while (--i > -1) { 5859 x = randomize ? Math.random() : (1 / points) * i; 5860 y = template ? template.getRatio(x) : x; 5861 if (taper === "none") { 5862 bump = strength; 5863 } else if (taper === "out") { 5864 invX = 1 - x; 5865 bump = invX * invX * strength; 5866 } else if (taper === "in") { 5867 bump = x * x * strength; 5868 } else if (x < 0.5) { //"both" (start) 5869 invX = x * 2; 5870 bump = invX * invX * 0.5 * strength; 5871 } else { //"both" (end) 5872 invX = (1 - x) * 2; 5873 bump = invX * invX * 0.5 * strength; 5874 } 5875 if (randomize) { 5876 y += (Math.random() * bump) - (bump * 0.5); 5877 } else if (i % 2) { 5878 y += bump * 0.5; 5879 } else { 5880 y -= bump * 0.5; 5881 } 5882 if (clamp) { 5883 if (y > 1) { 5884 y = 1; 5885 } else if (y < 0) { 5886 y = 0; 5887 } 5888 } 5889 a[cnt++] = {x:x, y:y}; 5890 } 5891 a.sort(function(a, b) { 5892 return a.x - b.x; 5893 }); 5894 5895 pnt = new EasePoint(1, 1, null); 5896 i = points; 5897 while (--i > -1) { 5898 obj = a[i]; 5899 pnt = new EasePoint(obj.x, obj.y, pnt); 5900 } 5901 5902 this._prev = new EasePoint(0, 0, (pnt.t !== 0) ? pnt : pnt.next); 5903 }, true); 5904 p = RoughEase.prototype = new Ease(); 5905 p.constructor = RoughEase; 5906 p.getRatio = function(p) { 5907 var pnt = this._prev; 5908 if (p > pnt.t) { 5909 while (pnt.next && p >= pnt.t) { 5910 pnt = pnt.next; 5911 } 5912 pnt = pnt.prev; 5913 } else { 5914 while (pnt.prev && p <= pnt.t) { 5915 pnt = pnt.prev; 5916 } 5917 } 5918 this._prev = pnt; 5919 return (pnt.v + ((p - pnt.t) / pnt.gap) * pnt.c); 5920 }; 5921 p.config = function(vars) { 5922 return new RoughEase(vars); 5923 }; 5924 RoughEase.ease = new RoughEase(); 5925 5926 5927 //Bounce 5928 _wrap("Bounce", 5929 _create("BounceOut", function(p) { 5930 if (p < 1 / 2.75) { 5931 return 7.5625 * p * p; 5932 } else if (p < 2 / 2.75) { 5933 return 7.5625 * (p -= 1.5 / 2.75) * p + 0.75; 5934 } else if (p < 2.5 / 2.75) { 5935 return 7.5625 * (p -= 2.25 / 2.75) * p + 0.9375; 5936 } 5937 return 7.5625 * (p -= 2.625 / 2.75) * p + 0.984375; 5938 }), 5939 _create("BounceIn", function(p) { 5940 if ((p = 1 - p) < 1 / 2.75) { 5941 return 1 - (7.5625 * p * p); 5942 } else if (p < 2 / 2.75) { 5943 return 1 - (7.5625 * (p -= 1.5 / 2.75) * p + 0.75); 5944 } else if (p < 2.5 / 2.75) { 5945 return 1 - (7.5625 * (p -= 2.25 / 2.75) * p + 0.9375); 5946 } 5947 return 1 - (7.5625 * (p -= 2.625 / 2.75) * p + 0.984375); 5948 }), 5949 _create("BounceInOut", function(p) { 5950 var invert = (p < 0.5); 5951 if (invert) { 5952 p = 1 - (p * 2); 5953 } else { 5954 p = (p * 2) - 1; 5955 } 5956 if (p < 1 / 2.75) { 5957 p = 7.5625 * p * p; 5958 } else if (p < 2 / 2.75) { 5959 p = 7.5625 * (p -= 1.5 / 2.75) * p + 0.75; 5960 } else if (p < 2.5 / 2.75) { 5961 p = 7.5625 * (p -= 2.25 / 2.75) * p + 0.9375; 5962 } else { 5963 p = 7.5625 * (p -= 2.625 / 2.75) * p + 0.984375; 5964 } 5965 return invert ? (1 - p) * 0.5 : p * 0.5 + 0.5; 5966 }) 5967 ); 5968 5969 5970 //CIRC 5971 _wrap("Circ", 5972 _create("CircOut", function(p) { 5973 return Math.sqrt(1 - (p = p - 1) * p); 5974 }), 5975 _create("CircIn", function(p) { 5976 return -(Math.sqrt(1 - (p * p)) - 1); 5977 }), 5978 _create("CircInOut", function(p) { 5979 return ((p*=2) < 1) ? -0.5 * (Math.sqrt(1 - p * p) - 1) : 0.5 * (Math.sqrt(1 - (p -= 2) * p) + 1); 5980 }) 5981 ); 5982 5983 5984 //Elastic 5985 _createElastic = function(n, f, def) { 5986 var C = _class("easing." + n, function(amplitude, period) { 5987 this._p1 = (amplitude >= 1) ? amplitude : 1; //note: if amplitude is < 1, we simply adjust the period for a more natural feel. Otherwise the math doesn't work right and the curve starts at 1. 5988 this._p2 = (period || def) / (amplitude < 1 ? amplitude : 1); 5989 this._p3 = this._p2 / _2PI * (Math.asin(1 / this._p1) || 0); 5990 this._p2 = _2PI / this._p2; //precalculate to optimize 5991 }, true), 5992 p = C.prototype = new Ease(); 5993 p.constructor = C; 5994 p.getRatio = f; 5995 p.config = function(amplitude, period) { 5996 return new C(amplitude, period); 5997 }; 5998 return C; 5999 }; 6000 _wrap("Elastic", 6001 _createElastic("ElasticOut", function(p) { 6002 return this._p1 * Math.pow(2, -10 * p) * Math.sin( (p - this._p3) * this._p2 ) + 1; 6003 }, 0.3), 6004 _createElastic("ElasticIn", function(p) { 6005 return -(this._p1 * Math.pow(2, 10 * (p -= 1)) * Math.sin( (p - this._p3) * this._p2 )); 6006 }, 0.3), 6007 _createElastic("ElasticInOut", function(p) { 6008 return ((p *= 2) < 1) ? -0.5 * (this._p1 * Math.pow(2, 10 * (p -= 1)) * Math.sin( (p - this._p3) * this._p2)) : this._p1 * Math.pow(2, -10 *(p -= 1)) * Math.sin( (p - this._p3) * this._p2 ) * 0.5 + 1; 6009 }, 0.45) 6010 ); 6011 6012 6013 //Expo 6014 _wrap("Expo", 6015 _create("ExpoOut", function(p) { 6016 return 1 - Math.pow(2, -10 * p); 6017 }), 6018 _create("ExpoIn", function(p) { 6019 return Math.pow(2, 10 * (p - 1)) - 0.001; 6020 }), 6021 _create("ExpoInOut", function(p) { 6022 return ((p *= 2) < 1) ? 0.5 * Math.pow(2, 10 * (p - 1)) : 0.5 * (2 - Math.pow(2, -10 * (p - 1))); 6023 }) 6024 ); 6025 6026 6027 //Sine 6028 _wrap("Sine", 6029 _create("SineOut", function(p) { 6030 return Math.sin(p * _HALF_PI); 6031 }), 6032 _create("SineIn", function(p) { 6033 return -Math.cos(p * _HALF_PI) + 1; 6034 }), 6035 _create("SineInOut", function(p) { 6036 return -0.5 * (Math.cos(Math.PI * p) - 1); 6037 }) 6038 ); 6039 6040 _class("easing.EaseLookup", { 6041 find:function(s) { 6042 return Ease.map[s]; 6043 } 6044 }, true); 6045 6046 //register the non-standard eases 6047 _easeReg(w.SlowMo, "SlowMo", "ease,"); 6048 _easeReg(RoughEase, "RoughEase", "ease,");
6049 _easeReg(SteppedEase, "SteppedEase", "ease,"); 6050 6051 return Back; 6052 6053 }, true); 6054 6055 6056}); 6057 6058if (_gsScope._gsDefine) { _gsScope._gsQueue.pop()(); } //necessary in case TweenLite was already loaded separately. 6059 6060 6061 6062 6063 6064 6065 6066 6067 6068 6069 6070/* 6071 * ---------------------------------------------------------------- 6072 * Base classes like TweenLite, SimpleTimeline, Ease, Ticker, etc. 6073 * ---------------------------------------------------------------- 6074 */ 6075(function(window, moduleName) { 6076 6077 "use strict"; 6078 var _exports = {}, 6079 _doc = window.document, 6080 _globals = window.GreenSockGlobals = window.GreenSockGlobals || window, 6081 existingModule = _globals[moduleName]; 6082 if (existingModule) { 6083 if (typeof(module) !== "undefined" && module.exports) { //node 6084 module.exports = existingModule; 6085 } 6086 return existingModule; //in case the core set of classes is already loaded, don't instantiate twice. 6087 } 6088 var _namespace = function(ns) { 6089 var a = ns.split("."), 6090 p = _globals, i; 6091 for (i = 0; i < a.length; i++) { 6092 p[a[i]] = p = p[a[i]] || {}; 6093 } 6094 return p; 6095 }, 6096 gs = _namespace("com.greensock"), 6097 _tinyNum = 0.0000000001, 6098 _slice = function(a) { //don't use Array.prototype.slice.call(target, 0) because that doesn't work in IE8 with a NodeList that's returned by querySelectorAll() 6099 var b = [], 6100 l = a.length, 6101 i; 6102 for (i = 0; i !== l; b.push(a[i++])) {} 6103 return b; 6104 }, 6105 _emptyFunc = function() {}, 6106 _isArray = (function() { //works around issues in iframe environments where the Array global isn't shared, thus if the object originates in a different window/iframe, "(obj instanceof Array)" will evaluate false. We added some speed optimizations to avoid Object.prototype.toString.call() unless it's absolutely necessary because it's VERY slow (like 20x slower) 6107 var toString = Object.prototype.toString, 6108 array = toString.call([]); 6109 return function(obj) { 6110 return obj != null && (obj instanceof Array || (typeof(obj) === "object" && !!obj.push && toString.call(obj) === array)); 6111 }; 6112 }()), 6113 a, i, p, _ticker, _tickerActive, 6114 _defLookup = {}, 6115 6116 /** 6117 * @constructor 6118 * Defines a GreenSock class, optionally with an array of dependencies that must be instantiated first and passed into the definition. 6119 * This allows users to load GreenSock JS files in any order even if they have interdependencies (like CSSPlugin extends TweenPlugin which is 6120 * inside TweenLite.js, but if CSSPlugin is loaded first, it should wait to run its code until TweenLite.js loads and instantiates TweenPlugin 6121 * and then pass TweenPlugin to CSSPlugin's definition). This is all done automatically and internally. 6122 * 6123 * Every definition will be added to a "com.greensock" global object (typically window, but if a window.GreenSockGlobals object is found, 6124 * it will go there as of v1.7). For example, TweenLite will be found at window.com.greensock.TweenLite and since it's a global class that should be available anywhere, 6125 * it is ALSO referenced at window.TweenLite. However some classes aren't considered global, like the base com.greensock.core.Animation class, so 6126 * those will only be at the package like window.com.greensock.core.Animation. Again, if you define a GreenSockGlobals object on the window, everything 6127 * gets tucked neatly inside there instead of on the window directly. This allows you to do advanced things like load multiple versions of GreenSock 6128 * files and put them into distinct objects (imagine a banner ad uses a newer version but the main site uses an older one). In that case, you could 6129 * sandbox the banner one like: 6130 * 6131 * <script> 6132 * var gs = window.GreenSockGlobals = {}; //the newer version we're about to load could now be referenced in a "gs" object, like gs.TweenLite.to(...). Use whatever alias you want as long as it's unique, "gs" or "banner" or whatever. 6133 * </script> 6134 * <script src="js/greensock/v1.7/TweenMax.js"></script> 6135 * <script> 6136 * window.GreenSockGlobals = window._gsQueue = window._gsDefine = null; //reset it back to null (along with the special _gsQueue variable) so that the next load of TweenMax affects the window and we can reference things directly like TweenLite.to(...) 6137 * </script> 6138 * <script src="js/greensock/v1.6/TweenMax.js"></script> 6139 * <script> 6140 * gs.TweenLite.to(...); //would use v1.7 6141 * TweenLite.to(...); //would use v1.6 6142 * </script> 6143 * 6144 * @param {!string} ns The namespace of the class definition, leaving off "com.greensock." as that's assumed. For example, "TweenLite" or "plugins.CSSPlugin" or "easing.Back". 6145 * @param {!Array.<string>}
6145 dependencies An array of dependencies (described as their namespaces minus "com.greensock." prefix). For example ["TweenLite","plugins.TweenPlugin","core.Animation"] 6146 * @param {!function():Object} func The function that should be called and passed the resolved dependencies which will return the actual class for this definition. 6147 * @param {boolean=} global If true, the class will be added to the global scope (typically window unless you define a window.GreenSockGlobals object) 6148 */ 6149 Definition = function(ns, dependencies, func, global) { 6150 this.sc = (_defLookup[ns]) ? _defLookup[ns].sc : []; //subclasses 6151 _defLookup[ns] = this; 6152 this.gsClass = null; 6153 this.func = func; 6154 var _classes = []; 6155 this.check = function(init) { 6156 var i = dependencies.length, 6157 missing = i, 6158 cur, a, n, cl; 6159 while (--i > -1) { 6160 if ((cur = _defLookup[dependencies[i]] || new Definition(dependencies[i], [])).gsClass) { 6161 _classes[i] = cur.gsClass; 6162 missing--; 6163 } else if (init) { 6164 cur.sc.push(this); 6165 } 6166 } 6167 if (missing === 0 && func) { 6168 a = ("com.greensock." + ns).split("."); 6169 n = a.pop(); 6170 cl = _namespace(a.join("."))[n] = this.gsClass = func.apply(func, _classes); 6171 6172 //exports to multiple environments 6173 if (global) { 6174 _globals[n] = _exports[n] = cl; //provides a way to avoid global namespace pollution. By default, the main classes like TweenLite, Power1, Strong, etc. are added to window unless a GreenSockGlobals is defined. So if you want to have things added to a custom object instead, just do something like window.GreenSockGlobals = {} before loading any GreenSock files. You can even set up an alias like window.GreenSockGlobals = windows.gs = {} so that you can access everything like gs.TweenLite. Also remember that ALL classes are added to the window.com.greensock object (in their respective packages, like com.greensock.easing.Power1, com.greensock.TweenLite, etc.) 6175 if (typeof(module) !== "undefined" && module.exports) { //node 6176 if (ns === moduleName) { 6177 module.exports = _exports[moduleName] = cl; 6178 for (i in _exports) { 6179 cl[i] = _exports[i]; 6180 } 6181 } else if (_exports[moduleName]) { 6182 _exports[moduleName][n] = cl; 6183 } 6184 } else if (typeof(define) === "function" && define.amd){ //AMD 6185 define((window.GreenSockAMDPath ? window.GreenSockAMDPath + "/" : "") + ns.split(".").pop(), [], function() { return cl; }); 6186 } 6187 } 6188 for (i = 0; i < this.sc.length; i++) { 6189 this.sc[i].check(); 6190 } 6191 } 6192 }; 6193 this.check(true); 6194 }, 6195 6196 //used to create Definition instances (which basically registers a class that has dependencies). 6197 _gsDefine = window._gsDefine = function(ns, dependencies, func, global) { 6198 return new Definition(ns, dependencies, func, global); 6199 }, 6200 6201 //a quick way to create a class that doesn't have any dependencies. Returns the class, but first registers it in the GreenSock namespace so that other classes can grab it (other classes might be dependent on the class). 6202 _class = gs._class = function(ns, func, global) { 6203 func = func || function() {}; 6204 _gsDefine(ns, [], function(){ return func; }, global); 6205 return func; 6206 }; 6207 6208 _gsDefine.globals = _globals; 6209 6210 6211 6212/* 6213 * ---------------------------------------------------------------- 6214 * Ease 6215 * ---------------------------------------------------------------- 6216 */ 6217 var _baseParams = [0, 0, 1, 1], 6218 Ease = _class("easing.Ease", function(func, extraParams, type, power) { 6219 this._func = func; 6220 this._type = type || 0; 6221 this._power = power || 0; 6222 this._params = extraParams ? _baseParams.concat(extraParams) : _baseParams; 6223 }, true), 6224 _easeMap = Ease.map = {}, 6225 _easeReg = Ease.register = function(ease, names, types, create) { 6226 var na = names.split(","), 6227 i = na.length, 6228 ta = (types || "easeIn,easeOut,easeInOut").split(","), 6229 e, name, j, type; 6230 while (--i > -1) { 6231 name = na[i]; 6232 e = create ? _class("easing."+name, null, true) : gs.easing[name] || {}; 6233 j = ta.length; 6234 while (--j > -1) { 6235 type = ta[j]; 6236 _easeMap[name + "." + type] = _easeMap[type + name] = e[type] = ease.getRatio ? ease : ease[type] || new ease(); 6237 } 6238 } 6239 }; 6240 6241 p = Ease.prototype; 6242 p._calcEnd = false;
6243 p.getRatio = function(p) { 6244 if (this._func) { 6245 this._params[0] = p; 6246 return this._func.apply(null, this._params); 6247 } 6248 var t = this._type, 6249 pw = this._power, 6250 r = (t === 1) ? 1 - p : (t === 2) ? p : (p < 0.5) ? p * 2 : (1 - p) * 2; 6251 if (pw === 1) { 6252 r *= r; 6253 } else if (pw === 2) { 6254 r *= r * r; 6255 } else if (pw === 3) { 6256 r *= r * r * r; 6257 } else if (pw === 4) { 6258 r *= r * r * r * r; 6259 } 6260 return (t === 1) ? 1 - r : (t === 2) ? r : (p < 0.5) ? r / 2 : 1 - (r / 2); 6261 }; 6262 6263 //create all the standard eases like Linear, Quad, Cubic, Quart, Quint, Strong, Power0, Power1, Power2, Power3, and Power4 (each with easeIn, easeOut, and easeInOut) 6264 a = ["Linear","Quad","Cubic","Quart","Quint,Strong"]; 6265 i = a.length; 6266 while (--i > -1) { 6267 p = a[i]+",Power"+i; 6268 _easeReg(new Ease(null,null,1,i), p, "easeOut", true); 6269 _easeReg(new Ease(null,null,2,i), p, "easeIn" + ((i === 0) ? ",easeNone" : "")); 6270 _easeReg(new Ease(null,null,3,i), p, "easeInOut"); 6271 } 6272 _easeMap.linear = gs.easing.Linear.easeIn; 6273 _easeMap.swing = gs.easing.Quad.easeInOut; //for jQuery folks 6274 6275 6276/* 6277 * ---------------------------------------------------------------- 6278 * EventDispatcher 6279 * ---------------------------------------------------------------- 6280 */ 6281 var EventDispatcher = _class("events.EventDispatcher", function(target) { 6282 this._listeners = {}; 6283 this._eventTarget = target || this; 6284 }); 6285 p = EventDispatcher.prototype; 6286 6287 p.addEventListener = function(type, callback, scope, useParam, priority) { 6288 priority = priority || 0; 6289 var list = this._listeners[type], 6290 index = 0, 6291 listener, i; 6292 if (this === _ticker && !_tickerActive) { 6293 _ticker.wake(); 6294 } 6295 if (list == null) { 6296 this._listeners[type] = list = []; 6297 } 6298 i = list.length; 6299 while (--i > -1) { 6300 listener = list[i]; 6301 if (listener.c === callback && listener.s === scope) { 6302 list.splice(i, 1); 6303 } else if (index === 0 && listener.pr < priority) { 6304 index = i + 1; 6305 } 6306 } 6307 list.splice(index, 0, {c:callback, s:scope, up:useParam, pr:priority}); 6308 }; 6309 6310 p.removeEventListener = function(type, callback) { 6311 var list = this._listeners[type], i; 6312 if (list) { 6313 i = list.length; 6314 while (--i > -1) { 6315 if (list[i].c === callback) { 6316 list.splice(i, 1); 6317 return; 6318 } 6319 } 6320 } 6321 }; 6322 6323 p.dispatchEvent = function(type) { 6324 var list = this._listeners[type], 6325 i, t, listener; 6326 if (list) { 6327 i = list.length; 6328 if (i > 1) { 6329 list = list.slice(0); //in case addEventListener() is called from within a listener/callback (otherwise the index could change, resulting in a skip) 6330 } 6331 t = this._eventTarget; 6332 while (--i > -1) { 6333 listener = list[i]; 6334 if (listener) { 6335 if (listener.up) { 6336 listener.c.call(listener.s || t, {type:type, target:t}); 6337 } else { 6338 listener.c.call(listener.s || t); 6339 } 6340 } 6341 } 6342 } 6343 }; 6344 6345 6346/* 6347 * ---------------------------------------------------------------- 6348 * Ticker 6349 * ---------------------------------------------------------------- 6350 */ 6351 var _reqAnimFrame = window.requestAnimationFrame, 6352 _cancelAnimFrame = window.cancelAnimationFrame, 6353 _getTime = Date.now || function() {return new Date().getTime();}, 6354 _lastUpdate = _getTime(); 6355 6356 //now try to determine the requestAnimationFrame and cancelAnimationFrame functions and if none are found, we'll use a setTimeout()/clearTimeout() polyfill. 6357 a = ["ms","moz","webkit","o"]; 6358 i = a.length; 6359 while (--i > -1 && !_reqAnimFrame) { 6360 _reqAnimFrame = window[a[i] + "RequestAnimationFrame"]; 6361 _cancelAnimFrame = window[a[i] + "CancelAnimationFrame"] || window[a[i] + "CancelRequestAnimationFrame"]; 6362 } 6363 6364 _class("Ticker", function(fps, useRAF) { 6365 var _self = this, 6366 _startTime = _getTime(), 6367 _useRAF = (useRAF !== false && _reqAnimFrame) ? "auto" : false, 6368 _lagThreshold = 500, 6369 _adjustedLag = 33, 6370 _tickWord = "tick", //helps reduce gc burden 6371 _fps, _req, _id, _gap, _nextTime, 6372 _tick = function(manual) { 6373 var elapsed = _getTime() - _lastUpdate, 6374 overlap, dispatch; 6375 if (elapsed > _lagThreshold) { 6376 _startTime += elapsed - _adjustedLag; 6377 } 6378 _lastUpdate += elapsed; 6379 _self.time = (_lastUpdate - _startTime) / 1000; 6380 overlap = _self.time - _nextTime; 6381 if (!_fps || overlap > 0 || manual === true) { 6382 _self.frame++; 6383 _nextTime += overlap + (overlap >= _gap ? 0.004 : _gap - overlap); 6384 dispatch = true; 6385 } 6386 if (manual !== true) { //make sure the request is made before we dispatch the "tick" event so that timing is maintained. Otherwise, if processing the "tick" requires a bunch of time (like 15ms) and we're using a setTimeout() that's based on 16.7ms, it'd technically take 31.7ms between frames otherwise. 6387 _id = _req(_tick); 6388 }
6389 if (dispatch) { 6390 _self.dispatchEvent(_tickWord); 6391 } 6392 }; 6393 6394 EventDispatcher.call(_self); 6395 _self.time = _self.frame = 0; 6396 _self.tick = function() { 6397 _tick(true); 6398 }; 6399 6400 _self.lagSmoothing = function(threshold, adjustedLag) { 6401 if (!arguments.length) { //if lagSmoothing() is called with no arguments, treat it like a getter that returns a boolean indicating if it's enabled or not. This is purposely undocumented and is for internal use. 6402 return (_lagThreshold < 1 / _tinyNum); 6403 } 6404 _lagThreshold = threshold || (1 / _tinyNum); //zero should be interpreted as basically unlimited 6405 _adjustedLag = Math.min(adjustedLag, _lagThreshold, 0); 6406 }; 6407 6408 _self.sleep = function() { 6409 if (_id == null) { 6410 return; 6411 } 6412 if (!_useRAF || !_cancelAnimFrame) { 6413 clearTimeout(_id); 6414 } else { 6415 _cancelAnimFrame(_id); 6416 } 6417 _req = _emptyFunc; 6418 _id = null; 6419 if (_self === _ticker) { 6420 _tickerActive = false; 6421 } 6422 }; 6423 6424 _self.wake = function(seamless) { 6425 if (_id !== null) { 6426 _self.sleep(); 6427 } else if (seamless) { 6428 _startTime += -_lastUpdate + (_lastUpdate = _getTime()); 6429 } else if (_self.frame > 10) { //don't trigger lagSmoothing if we're just waking up, and make sure that at least 10 frames have elapsed because of the iOS bug that we work around below with the 1.5-second setTimout(). 6430 _lastUpdate = _getTime() - _lagThreshold + 5; 6431 } 6432 _req = (_fps === 0) ? _emptyFunc : (!_useRAF || !_reqAnimFrame) ? function(f) { return setTimeout(f, ((_nextTime - _self.time) * 1000 + 1) | 0); } : _reqAnimFrame; 6433 if (_self === _ticker) { 6434 _tickerActive = true; 6435 } 6436 _tick(2); 6437 }; 6438 6439 _self.fps = function(value) { 6440 if (!arguments.length) { 6441 return _fps; 6442 } 6443 _fps = value; 6444 _gap = 1 / (_fps || 60); 6445 _nextTime = this.time + _gap; 6446 _self.wake(); 6447 }; 6448 6449 _self.useRAF = function(value) { 6450 if (!arguments.length) { 6451 return _useRAF; 6452 } 6453 _self.sleep(); 6454 _useRAF = value; 6455 _self.fps(_fps); 6456 }; 6457 _self.fps(fps); 6458 6459 //a bug in iOS 6 Safari occasionally prevents the requestAnimationFrame from working initially, so we use a 1.5-second timeout that automatically falls back to setTimeout() if it senses this condition. 6460 setTimeout(function() { 6461 if (_useRAF === "auto" && _self.frame < 5 && (_doc || {}).visibilityState !== "hidden") { 6462 _self.useRAF(false); 6463 } 6464 }, 1500); 6465 }); 6466 6467 p = gs.Ticker.prototype = new gs.events.EventDispatcher(); 6468 p.constructor = gs.Ticker; 6469 6470 6471/* 6472 * ---------------------------------------------------------------- 6473 * Animation 6474 * ---------------------------------------------------------------- 6475 */ 6476 var Animation = _class("core.Animation", function(duration, vars) { 6477 this.vars = vars = vars || {}; 6478 this._duration = this._totalDuration = duration || 0; 6479 this._delay = Number(vars.delay) || 0; 6480 this._timeScale = 1; 6481 this._active = (vars.immediateRender === true); 6482 this.data = vars.data; 6483 this._reversed = (vars.reversed === true); 6484 6485 if (!_rootTimeline) { 6486 return; 6487 } 6488 if (!_tickerActive) { //some browsers (like iOS 6 Safari) shut down JavaScript execution when the tab is disabled and they [occasionally] neglect to start up requestAnimationFrame again when returning - this code ensures that the engine starts up again properly. 6489 _ticker.wake(); 6490 } 6491 6492 var tl = this.vars.useFrames ? _rootFramesTimeline : _rootTimeline; 6493 tl.add(this, tl._time); 6494 6495 if (this.vars.paused) { 6496 this.paused(true); 6497 } 6498 }); 6499 6500 _ticker = Animation.ticker = new gs.Ticker(); 6501 p = Animation.prototype; 6502 p._dirty = p._gc = p._initted = p._paused = false; 6503 p._totalTime = p._time = 0; 6504 p._rawPrevTime = -1; 6505 p._next = p._last = p._onUpdate = p._timeline = p.timeline = null; 6506 p._paused = false; 6507 6508 6509 //some browsers (like iOS) occasionally drop the requestAnimationFrame event when the user switches to a different tab and then comes back again, so we use a 2-second setTimeout() to sense if/when that condition occurs and then wake() the ticker. 6510 var _checkTimeout = function() { 6511 if (_tickerActive && _getTime() - _lastUpdate > 2000 && ((_doc || {}).visibilityState !== "hidden" || !_ticker.lagSmoothing())) { //note: if the tab is hidden, we should still wake if lagSmoothing has been disabled. 6512 _ticker.wake(); 6513 } 6514 var t = setTimeout(_checkTimeout, 2000); 6515 if (t.unref) { 6516 // allows a node process to exit even if the timeout’s callback hasn't been invoked. Without it, the node process could hang as this function is called every two seconds. 6517 t.unref(); 6518 } 6519 }; 6520 _checkTimeout(); 6521 6522 6523 p.play = function(from, suppressEvents) { 6524 if (from != null) { 6525 this.seek(from, suppressEvents); 6526 } 6527 return this.reversed(false).paused(false); 6528 }; 6529 6530 p.pause = function(atTime, suppressEvents) { 6531 if (atTime != null) { 6532 this.seek(atTime, suppressEvents); 6533 } 6534 return this.paused(true); 6535 }; 6536 6537 p.resume = function(from, suppressEvents) { 6538 if (from != null) { 6539 this.seek(from, suppressEvents); 6540 } 6541 return this.paused(false); 6542 }; 6543 6544 p.seek = function(time, suppressEvents) { 6545 return this.totalTime(Number(time), suppressEvents !== false); 6546 }; 6547 6548 p.restart = function(includeDelay, suppressEvents) { 6549 return this.reversed(false).paused(false).totalTime(includeDelay ? -this._delay : 0, (suppressEvents !== false), true); 6550 }; 6551 6552 p.reverse = function(from, suppressEvents) { 6553 if (from != null) { 6554 this.seek((from || this.totalDuration()), suppressEvents); 6555 } 6556 return this.reversed(true).paused(false); 6557 }; 6558 6559 p.render = function(time, suppressEvents, force) { 6560 //stub - we override this method in subclasses. 6561 }; 6562 6563 p.invalidate = function() { 6564 this._time = this._totalTime = 0; 6565 this._initted = this._gc = false;
6566 this._rawPrevTime = -1; 6567 if (this._gc || !this.timeline) { 6568 this._enabled(true); 6569 } 6570 return this; 6571 }; 6572 6573 p.isActive = function() { 6574 var tl = this._timeline, //the 2 root timelines won't have a _timeline; they're always active. 6575 startTime = this._startTime, 6576 rawTime; 6577 return (!tl || (!this._gc && !this._paused && tl.isActive() && (rawTime = tl.rawTime(true)) >= startTime && rawTime < startTime + this.totalDuration() / this._timeScale - 0.0000001)); 6578 }; 6579 6580 p._enabled = function (enabled, ignoreTimeline) { 6581 if (!_tickerActive) { 6582 _ticker.wake(); 6583 } 6584 this._gc = !enabled; 6585 this._active = this.isActive(); 6586 if (ignoreTimeline !== true) { 6587 if (enabled && !this.timeline) { 6588 this._timeline.add(this, this._startTime - this._delay); 6589 } else if (!enabled && this.timeline) { 6590 this._timeline._remove(this, true); 6591 } 6592 } 6593 return false; 6594 }; 6595 6596 6597 p._kill = function(vars, target) { 6598 return this._enabled(false, false); 6599 }; 6600 6601 p.kill = function(vars, target) { 6602 this._kill(vars, target); 6603 return this; 6604 }; 6605 6606 p._uncache = function(includeSelf) { 6607 var tween = includeSelf ? this : this.timeline; 6608 while (tween) { 6609 tween._dirty = true; 6610 tween = tween.timeline; 6611 } 6612 return this; 6613 }; 6614 6615 p._swapSelfInParams = function(params) { 6616 var i = params.length, 6617 copy = params.concat(); 6618 while (--i > -1) { 6619 if (params[i] === "{self}") { 6620 copy[i] = this; 6621 } 6622 } 6623 return copy; 6624 }; 6625 6626 p._callback = function(type) { 6627 var v = this.vars, 6628 callback = v[type], 6629 params = v[type + "Params"], 6630 scope = v[type + "Scope"] || v.callbackScope || this, 6631 l = params ? params.length : 0; 6632 switch (l) { //speed optimization; call() is faster than apply() so use it when there are only a few parameters (which is by far most common). Previously we simply did var v = this.vars; v[type].apply(v[type + "Scope"] || v.callbackScope || this, v[type + "Params"] || _blankArray); 6633 case 0: callback.call(scope); break; 6634 case 1: callback.call(scope, params[0]); break; 6635 case 2: callback.call(scope, params[0], params[1]); break; 6636 default: callback.apply(scope, params); 6637 } 6638 }; 6639 6640//----Animation getters/setters -------------------------------------------------------- 6641 6642 p.eventCallback = function(type, callback, params, scope) { 6643 if ((type || "").substr(0,2) === "on") { 6644 var v = this.vars; 6645 if (arguments.length === 1) { 6646 return v[type]; 6647 } 6648 if (callback == null) { 6649 delete v[type]; 6650 } else { 6651 v[type] = callback; 6652 v[type + "Params"] = (_isArray(params) && params.join("").indexOf("{self}") !== -1) ? this._swapSelfInParams(params) : params; 6653 v[type + "Scope"] = scope; 6654 } 6655 if (type === "onUpdate") { 6656 this._onUpdate = callback; 6657 } 6658 } 6659 return this; 6660 }; 6661 6662 p.delay = function(value) { 6663 if (!arguments.length) { 6664 return this._delay; 6665 } 6666 if (this._timeline.smoothChildTiming) { 6667 this.startTime( this._startTime + value - this._delay ); 6668 } 6669 this._delay = value; 6670 return this; 6671 }; 6672 6673 p.duration = function(value) { 6674 if (!arguments.length) { 6675 this._dirty = false; 6676 return this._duration; 6677 } 6678 this._duration = this._totalDuration = value; 6679 this._uncache(true); //true in case it's a TweenMax or TimelineMax that has a repeat - we'll need to refresh the totalDuration. 6680 if (this._timeline.smoothChildTiming) if (this._time > 0) if (this._time < this._duration) if (value !== 0) { 6681 this.totalTime(this._totalTime * (value / this._duration), true); 6682 } 6683 return this; 6684 }; 6685 6686 p.totalDuration = function(value) { 6687 this._dirty = false; 6688 return (!arguments.length) ? this._totalDuration : this.duration(value); 6689 }; 6690 6691 p.time = function(value, suppressEvents) { 6692 if (!arguments.length) { 6693 return this._time; 6694 } 6695 if (this._dirty) { 6696 this.totalDuration(); 6697 } 6698 return this.totalTime((value > this._duration) ? this._duration : value, suppressEvents); 6699 }; 6700 6701 p.totalTime = function(time, suppressEvents, uncapped) { 6702 if (!_tickerActive) { 6703 _ticker.wake(); 6704 } 6705 if (!arguments.length) { 6706 return this._totalTime; 6707 } 6708 if (this._timeline) { 6709 if (time < 0 && !uncapped) { 6710 time += this.totalDuration(); 6711 } 6712 if (this._timeline.smoothChildTiming) { 6713 if (this._dirty) { 6714 this.totalDuration(); 6715 } 6716 var totalDuration = this._totalDuration, 6717 tl = this._timeline; 6718 if (time > totalDuration && !uncapped) { 6719 time = totalDuration; 6720 } 6721 this._startTime = (this._paused ? this._pauseTime : tl._time) - ((!this._reversed ? time : totalDuration - time) / this._timeScale); 6722 if (!tl._dirty) { //for performance improvement. If the parent's cache is already dirty, it already took care of marking the ancestors as dirty too, so skip the function call here. 6723 this._uncache(false); 6724 } 6725 //in case any of the ancestor timelines had completed but should now be enabled, we should reset the
6725ir totalTime() which will also ensure that they're lined up properly and enabled. Skip for animations that are on the root (wasteful). Example: a TimelineLite.exportRoot() is performed when there's a paused tween on the root, the export will not complete until that tween is unpaused, but imagine a child gets restarted later, after all [unpaused] tweens have completed. The startTime of that child would get pushed out, but one of the ancestors may have completed. 6726 if (tl._timeline) { 6727 while (tl._timeline) { 6728 if (tl._timeline._time !== (tl._startTime + tl._totalTime) / tl._timeScale) { 6729 tl.totalTime(tl._totalTime, true); 6730 } 6731 tl = tl._timeline; 6732 } 6733 } 6734 } 6735 if (this._gc) { 6736 this._enabled(true, false); 6737 } 6738 if (this._totalTime !== time || this._duration === 0) { 6739 if (_lazyTweens.length) { 6740 _lazyRender(); 6741 } 6742 this.render(time, suppressEvents, false); 6743 if (_lazyTweens.length) { //in case rendering caused any tweens to lazy-init, we should render them because typically when someone calls seek() or time() or progress(), they expect an immediate render. 6744 _lazyRender(); 6745 } 6746 } 6747 } 6748 return this; 6749 }; 6750 6751 p.progress = p.totalProgress = function(value, suppressEvents) { 6752 var duration = this.duration(); 6753 return (!arguments.length) ? (duration ? this._time / duration : this.ratio) : this.totalTime(duration * value, suppressEvents); 6754 }; 6755 6756 p.startTime = function(value) { 6757 if (!arguments.length) { 6758 return this._startTime; 6759 } 6760 if (value !== this._startTime) { 6761 this._startTime = value; 6762 if (this.timeline) if (this.timeline._sortChildren) { 6763 this.timeline.add(this, value - this._delay); //ensures that any necessary re-sequencing of Animations in the timeline occurs to make sure the rendering order is correct. 6764 } 6765 } 6766 return this; 6767 }; 6768 6769 p.endTime = function(includeRepeats) { 6770 return this._startTime + ((includeRepeats != false) ? this.totalDuration() : this.duration()) / this._timeScale; 6771 }; 6772 6773 p.timeScale = function(value) { 6774 if (!arguments.length) { 6775 return this._timeScale; 6776 } 6777 var pauseTime, t; 6778 value = value || _tinyNum; //can't allow zero because it'll throw the math off 6779 if (this._timeline && this._timeline.smoothChildTiming) { 6780 pauseTime = this._pauseTime; 6781 t = (pauseTime || pauseTime === 0) ? pauseTime : this._timeline.totalTime(); 6782 this._startTime = t - ((t - this._startTime) * this._timeScale / value); 6783 } 6784 this._timeScale = value; 6785 t = this.timeline; 6786 while (t && t.timeline) { //must update the duration/totalDuration of all ancestor timelines immediately in case in the middle of a render loop, one tween alters another tween's timeScale which shoves its startTime before 0, forcing the parent timeline to shift around and shiftChildren() which could affect that next tween's render (startTime). Doesn't matter for the root timeline though. 6787 t._dirty = true; 6788 t.totalDuration(); 6789 t = t.timeline; 6790 } 6791 return this; 6792 }; 6793 6794 p.reversed = function(value) { 6795 if (!arguments.length) { 6796 return this._reversed; 6797 } 6798 if (value != this._reversed) { 6799 this._reversed = value; 6800 this.totalTime(((this._timeline && !this._timeline.smoothChildTiming) ? this.totalDuration() - this._totalTime : this._totalTime), true); 6801 } 6802 return this; 6803 }; 6804 6805 p.paused = function(value) { 6806 if (!arguments.length) { 6807 return this._paused; 6808 } 6809 var tl = this._timeline, 6810 raw, elapsed; 6811 if (value != this._paused) if (tl) { 6812 if (!_tickerActive && !value) { 6813 _ticker.wake(); 6814 } 6815 raw = tl.rawTime(); 6816 elapsed = raw - this._pauseTime; 6817 if (!value && tl.smoothChildTiming) { 6818 this._startTime += elapsed; 6819 this._uncache(false); 6820 } 6821 this._pauseTime = value ? raw : null; 6822 this._paused = value; 6823 this._active = this.isActive(); 6824 if (!value && elapsed !== 0 && this._initted && this.duration()) { 6825 raw = tl.smoothChildTiming ? this._totalTime : (raw - this._startTime) / this._timeScale; 6826 this.render(raw, (raw === this._totalTime), true); //in case the target's properties changed via some other tween or manual update by the user, we should force a render. 6827 } 6828 } 6829 if (this._gc && !value) { 6830 this._enabled(true, false); 6831 } 6832 return this; 6833 }; 6834 6835
6836/* 6837 * ---------------------------------------------------------------- 6838 * SimpleTimeline 6839 * ---------------------------------------------------------------- 6840 */ 6841 var SimpleTimeline = _class("core.SimpleTimeline", function(vars) { 6842 Animation.call(this, 0, vars); 6843 this.autoRemoveChildren = this.smoothChildTiming = true; 6844 }); 6845 6846 p = SimpleTimeline.prototype = new Animation(); 6847 p.constructor = SimpleTimeline; 6848 p.kill()._gc = false; 6849 p._first = p._last = p._recent = null; 6850 p._sortChildren = false; 6851 6852 p.add = p.insert = function(child, position, align, stagger) { 6853 var prevTween, st; 6854 child._startTime = Number(position || 0) + child._delay; 6855 if (child._paused) if (this !== child._timeline) { //we only adjust the _pauseTime if it wasn't in this timeline already. Remember, sometimes a tween will be inserted again into the same timeline when its startTime is changed so that the tweens in the TimelineLite/Max are re-ordered properly in the linked list (so everything renders in the proper order). 6856 child._pauseTime = this.rawTime() - (child._timeline.rawTime() - child._pauseTime); 6857 } 6858 if (child.timeline) { 6859 child.timeline._remove(child, true); //removes from existing timeline so that it can be properly added to this one. 6860 } 6861 child.timeline = child._timeline = this; 6862 if (child._gc) { 6863 child._enabled(true, true); 6864 } 6865 prevTween = this._last; 6866 if (this._sortChildren) { 6867 st = child._startTime; 6868 while (prevTween && prevTween._startTime > st) { 6869 prevTween = prevTween._prev; 6870 } 6871 } 6872 if (prevTween) { 6873 child._next = prevTween._next; 6874 prevTween._next = child; 6875 } else { 6876 child._next = this._first; 6877 this._first = child; 6878 } 6879 if (child._next) { 6880 child._next._prev = child; 6881 } else { 6882 this._last = child; 6883 } 6884 child._prev = prevTween; 6885 this._recent = child; 6886 if (this._timeline) { 6887 this._uncache(true); 6888 } 6889 return this; 6890 }; 6891 6892 p._remove = function(tween, skipDisable) { 6893 if (tween.timeline === this) { 6894 if (!skipDisable) { 6895 tween._enabled(false, true); 6896 } 6897 6898 if (tween._prev) { 6899 tween._prev._next = tween._next; 6900 } else if (this._first === tween) { 6901 this._first = tween._next; 6902 } 6903 if (tween._next) { 6904 tween._next._prev = tween._prev; 6905 } else if (this._last === tween) { 6906 this._last = tween._prev; 6907 } 6908 tween._next = tween._prev = tween.timeline = null; 6909 if (tween === this._recent) { 6910 this._recent = this._last; 6911 } 6912 6913 if (this._timeline) { 6914 this._uncache(true); 6915 } 6916 } 6917 return this; 6918 }; 6919 6920 p.render = function(time, suppressEvents, force) { 6921 var tween = this._first, 6922 next; 6923 this._totalTime = this._time = this._rawPrevTime = time; 6924 while (tween) { 6925 next = tween._next; //record it here because the value could change after rendering... 6926 if (tween._active || (time >= tween._startTime && !tween._paused && !tween._gc)) { 6927 if (!tween._reversed) { 6928 tween.render((time - tween._startTime) * tween._timeScale, suppressEvents, force); 6929 } else { 6930 tween.render(((!tween._dirty) ? tween._totalDuration : tween.totalDuration()) - ((time - tween._startTime) * tween._timeScale), suppressEvents, force); 6931 } 6932 } 6933 tween = next; 6934 } 6935 }; 6936 6937 p.rawTime = function() { 6938 if (!_tickerActive) { 6939 _ticker.wake(); 6940 } 6941 return this._totalTime; 6942 }; 6943 6944/* 6945 * ---------------------------------------------------------------- 6946 * TweenLite 6947 * ---------------------------------------------------------------- 6948 */ 6949 var TweenLite = _class("TweenLite", function(target, duration, vars) { 6950 Animation.call(this, duration, vars); 6951 this.render = TweenLite.prototype.render; //speed optimization (avoid prototype lookup on this "hot" method) 6952 6953 if (target == null) { 6954 throw "Cannot tween a null target."; 6955 } 6956 6957 this.target = target = (typeof(target) !== "string") ? target : TweenLite.selector(target) || target; 6958 6959 var isSelector = (target.jquery || (target.length && target !== window && target[0] && (target[0] === window || (target[0].nodeType && target[0].style && !target.nodeType)))),
6960 overwrite = this.vars.overwrite, 6961 i, targ, targets; 6962 6963 this._overwrite = overwrite = (overwrite == null) ? _overwriteLookup[TweenLite.defaultOverwrite] : (typeof(overwrite) === "number") ? overwrite >> 0 : _overwriteLookup[overwrite]; 6964 6965 if ((isSelector || target instanceof Array || (target.push && _isArray(target))) && typeof(target[0]) !== "number") { 6966 this._targets = targets = _slice(target); //don't use Array.prototype.slice.call(target, 0) because that doesn't work in IE8 with a NodeList that's returned by querySelectorAll() 6967 this._propLookup = []; 6968 this._siblings = []; 6969 for (i = 0; i < targets.length; i++) { 6970 targ = targets[i]; 6971 if (!targ) { 6972 targets.splice(i--, 1); 6973 continue; 6974 } else if (typeof(targ) === "string") { 6975 targ = targets[i--] = TweenLite.selector(targ); //in case it's an array of strings 6976 if (typeof(targ) === "string") { 6977 targets.splice(i+1, 1); //to avoid an endless loop (can't imagine why the selector would return a string, but just in case) 6978 } 6979 continue; 6980 } else if (targ.length && targ !== window && targ[0] && (targ[0] === window || (targ[0].nodeType && targ[0].style && !targ.nodeType))) { //in case the user is passing in an array of selector objects (like jQuery objects), we need to check one more level and pull things out if necessary. Also note that <select> elements pass all the criteria regarding length and the first child having style, so we must also check to ensure the target isn't an HTML node itself. 6981 targets.splice(i--, 1); 6982 this._targets = targets = targets.concat(_slice(targ)); 6983 continue; 6984 } 6985 this._siblings[i] = _register(targ, this, false); 6986 if (overwrite === 1) if (this._siblings[i].length > 1) { 6987 _applyOverwrite(targ, this, null, 1, this._siblings[i]); 6988 } 6989 } 6990 6991 } else { 6992 this._propLookup = {}; 6993 this._siblings = _register(target, this, false); 6994 if (overwrite === 1) if (this._siblings.length > 1) { 6995 _applyOverwrite(target, this, null, 1, this._siblings); 6996 } 6997 } 6998 if (this.vars.immediateRender || (duration === 0 && this._delay === 0 && this.vars.immediateRender !== false)) { 6999 this._time = -_tinyNum; //forces a render without having to set the render() "force" parameter to true because we want to allow lazying by default (using the "force" parameter always forces an immediate full render) 7000 this.render(Math.min(0, -this._delay)); //in case delay is negative 7001 } 7002 }, true), 7003 _isSelector = function(v) { 7004 return (v && v.length && v !== window && v[0] && (v[0] === window || (v[0].nodeType && v[0].style && !v.nodeType))); //we cannot check "nodeType" if the target is window from within an iframe, otherwise it will trigger a security error in some browsers like Firefox. 7005 }, 7006 _autoCSS = function(vars, target) { 7007 var css = {}, 7008 p; 7009 for (p in vars) { 7010 if (!_reservedProps[p] && (!(p in target) || p === "transform" || p === "x" || p === "y" || p === "width" || p === "height" || p === "className" || p === "border") && (!_plugins[p] || (_plugins[p] && _plugins[p]._autoCSS))) { //note: <img> elements contain read-only "x" and "y" properties. We should also prioritize editing css width/height rather than the element's properties. 7011 css[p] = vars[p]; 7012 delete vars[p]; 7013 } 7014 } 7015 vars.css = css; 7016 }; 7017 7018 p = TweenLite.prototype = new Animation(); 7019 p.constructor = TweenLite; 7020 p.kill()._gc = false; 7021 7022//----TweenLite defaults, overwrite management, and root updates ---------------------------------------------------- 7023 7024 p.ratio = 0; 7025 p._firstPT = p._targets = p._overwrittenProps = p._startAt = null; 7026 p._notifyPluginsOfEnabled = p._lazy = false; 7027 7028 TweenLite.version = "2.0.2"; 7029 TweenLite.defaultEase = p._ease = new Ease(null, null, 1, 1); 7030 TweenLite.defaultOverwrite = "auto"; 7031 TweenLite.ticker = _ticker; 7032 TweenLite.autoSleep = 120; 7033 TweenLite.lagSmoothing = function(threshold, adjustedLag) { 7034 _ticker.lagSmoothing(threshold, adjustedLag); 7035 }; 7036 7037 TweenLite.selector = window.$ || window.jQuery || function(e) { 7038 var selector = window.$ || window.jQuery; 7039 if (selector) { 7040 TweenLite.selector = selector; 7041 return selector(e); 7042 } 7043 if (!_doc) { //in some dev environments (like Angular 6), GSAP gets loaded before the document is defined! So re-query it here if/when necessary. 7044 _doc = window.document; 7045 } 7046 return (!_doc) ? e : (_doc.querySelectorAll ? _doc.querySelectorAll(e) : _doc.getElementById((e.charAt(0) === "#") ? e.substr(1) : e)); 7047 }; 7048 7049 var _lazyTweens = [], 7050 _lazyLookup = {}, 7051 _numbersExp = /(?:(-|-=|\+=)?\d*\.?\d*(?:e[\-+]?\d+)?)[0-9]/ig, 7052 _relExp = /[\+-]=-?[\.\d]/, 7053 //_nonNumbersExp = /(?:([\-+](?!(\d|=)))|[^\d\-+=e]|(e(?![\-+][\d])))+/ig, 7054 _setRatio = function(v) { 7055 var pt = this._firstPT, 7056 min = 0.000001, 7057 val; 7058 while (pt) { 7059 val = !pt.blob ? pt.c * v + pt.s : (v === 1 && this.end != null) ? this.end : v ? this.join("") : this.start; 7060 if (pt.m) { 7061 val = pt.m.call(this._tween, val, this._target || pt.t, this._tween); 7062 } else if (val < min) if (val >
7062 -min && !pt.blob) { //prevents issues with converting very small numbers to strings in the browser 7063 val = 0; 7064 } 7065 if (!pt.f) { 7066 pt.t[pt.p] = val; 7067 } else if (pt.fp) { 7068 pt.t[pt.p](pt.fp, val); 7069 } else { 7070 pt.t[pt.p](val); 7071 } 7072 pt = pt._next; 7073 } 7074 }, 7075 //compares two strings (start/end), finds the numbers that are different and spits back an array representing the whole value but with the changing values isolated as elements. For example, "rgb(0,0,0)" and "rgb(100,50,0)" would become ["rgb(", 0, ",", 50, ",0)"]. Notice it merges the parts that are identical (performance optimization). The array also has a linked list of PropTweens attached starting with _firstPT that contain the tweening data (t, p, s, c, f, etc.). It also stores the starting value as a "start" property so that we can revert to it if/when necessary, like when a tween rewinds fully. If the quantity of numbers differs between the start and end, it will always prioritize the end value(s). The pt parameter is optional - it's for a PropTween that will be appended to the end of the linked list and is typically for actually setting the value after all of the elements have been updated (with array.join("")). 7076 _blobDif = function(start, end, filter, pt) { 7077 var a = [], 7078 charIndex = 0, 7079 s = "", 7080 color = 0, 7081 startNums, endNums, num, i, l, nonNumbers, currentNum; 7082 a.start = start; 7083 a.end = end; 7084 start = a[0] = start + ""; //ensure values are strings 7085 end = a[1] = end + ""; 7086 if (filter) { 7087 filter(a); //pass an array with the starting and ending values and let the filter do whatever it needs to the values. 7088 start = a[0]; 7089 end = a[1]; 7090 } 7091 a.length = 0; 7092 startNums = start.match(_numbersExp) || []; 7093 endNums = end.match(_numbersExp) || []; 7094 if (pt) { 7095 pt._next = null; 7096 pt.blob = 1; 7097 a._firstPT = a._applyPT = pt; //apply last in the linked list (which means inserting it first) 7098 } 7099 l = endNums.length; 7100 for (i = 0; i < l; i++) { 7101 currentNum = endNums[i]; 7102 nonNumbers = end.substr(charIndex, end.indexOf(currentNum, charIndex)-charIndex); 7103 s += (nonNumbers || !i) ? nonNumbers : ","; //note: SVG spec allows omission of comma/space when a negative sign is wedged between two numbers, like 2.5-5.3 instead of 2.5,-5.3 but when tweening, the negative value may switch to positive, so we insert the comma just in case. 7104 charIndex += nonNumbers.length; 7105 if (color) { //sense rgba() values and round them. 7106 color = (color + 1) % 5; 7107 } else if (nonNumbers.substr(-5) === "rgba(") { 7108 color = 1; 7109 } 7110 if (currentNum === startNums[i] || startNums.length <= i) { 7111 s += currentNum; 7112 } else { 7113 if (s) { 7114 a.push(s); 7115 s = ""; 7116 } 7117 num = parseFloat(startNums[i]); 7118 a.push(num); 7119 a._firstPT = {_next: a._firstPT, t:a, p: a.length-1, s:num, c:((currentNum.charAt(1) === "=") ? parseInt(currentNum.charAt(0) + "1", 10) * parseFloat(currentNum.substr(2)) : (parseFloat(currentNum) - num)) || 0, f:0, m:(color && color < 4) ? Math.round : 0}; 7120 //note: we don't set _prev because we'll never need to remove individual PropTweens from this list. 7121 } 7122 charIndex += currentNum.length; 7123 } 7124 s += end.substr(charIndex); 7125 if (s) { 7126 a.push(s); 7127 } 7128 a.setRatio = _setRatio; 7129 if (_relExp.test(end)) { //if the end string contains relative values, delete it so that on the final render (in _setRatio()), we don't actually set it to the string with += or -= characters (forces it to use the calculated value). 7130 a.end = null; 7131 } 7132 return a; 7133 }, 7134 //note: "funcParam" is only necessary for function-based getters/setters that require an extra parameter like getAttribute("width") and setAttribute("width", value). In this example, funcParam would be "width". Used by AttrPlugin for example. 7135 _addPropTween = function(target, prop, start, end, overwriteProp, mod, funcParam, stringFilter, index) { 7136 if (typeof(end) === "function") { 7137 end = end(index || 0, target); 7138 } 7139 var type = typeof(target[prop]), 7140 getterName = (type !== "function") ? "" : ((prop.indexOf("set") || typeof(target["get" + prop.substr(3)]) !== "function") ? prop : "get" + prop.substr(3)), 7141 s = (start !== "get") ? start : !getterName ? target[prop] : funcParam ? target[getterName](funcParam) : target[getterName](), 7142 isRelative = (typeof(end) === "string" && end.charAt(1) === "="), 7143 pt = {t:target, p:prop, s:s, f:(type === "function"), pg:0, n:overwriteProp || prop, m:(!mod ? 0 : (typeof(mod) === "function") ? mod : Math.round), pr:0, c:isRelative ? parseInt(end.charAt(0) + "1", 10) * parseFloat(end.substr(2)) : (parseFloat(end) - s) || 0}, 7144 blob; 7145 7146 if (typeof(s) !== "number" || (typeof(end) !== "number" && !isRelative)) { 7147 if (funcParam || isNaN(s) || (!isRelative && isNaN(end)) || typeof(s) === "boolean" || typeof(end) === "boolean") { 7148 //a blob (string that has multiple numbers in it) 7149 pt.fp = funcParam; 7150 blob = _blobDif(s, (isRelative ? (parseFloat(pt.s) + pt.c) + (pt.s + "").replace(/[0-9\-\.]/g, "") : end), stringFilter || TweenLite.defaultStringFilter, pt); 7151 pt = {t: blob, p: "setRatio", s: 0, c: 1, f: 2, pg: 0, n: overwriteProp || prop, pr: 0, m: 0}; //"2" indicates it's a Blob property tween. Needed for RoundPropsPlugin for example. 7152 } else { 7153 pt.s = parseFloat(s); 7154 if (!isRelative) { 7155 pt.c = (parseFloat(end) - pt.s) || 0; 7156 } 7157 } 7158 } 7159 if (pt.c) { //only add it to the linked list if there's a change. 7160 if ((pt._next = this._firstPT)) { 7161 pt._next._prev = pt; 7162 } 7163 this._firstPT = pt; 7164 return pt; 7165 } 7166 }, 7167 _internals = TweenLite._internals = {isArray:_isArray, isSelector:_isSelector, lazyTweens:_lazyTweens, blobDif:_blobDif}, //gives us a way to expose certain private values to other GreenSock classes without contaminating tha main TweenLite object. 7168 _plugins = TweenLite._plugins = {}, 7169 _tweenLookup = _internals.tweenLookup = {}, 7170 _tweenLookupNum = 0, 7171 _reservedProps = _internals.reservedProps = {ease:1, delay:1, overwrite:1, onComplete:1, onCompleteParams:1, onCompleteScope:1, useFrames:1, runBackwards:1, startAt:1, onUp
7171date:1, onUpdateParams:1, onUpdateScope:1, onStart:1, onStartParams:1, onStartScope:1, onReverseComplete:1, onReverseCompleteParams:1, onReverseCompleteScope:1, onRepeat:1, onRepeatParams:1, onRepeatScope:1, easeParams:1, yoyo:1, immediateRender:1, repeat:1, repeatDelay:1, data:1, paused:1, reversed:1, autoCSS:1, lazy:1, onOverwrite:1, callbackScope:1, stringFilter:1, id:1, yoyoEase:1}, 7172 _overwriteLookup = {none:0, all:1, auto:2, concurrent:3, allOnStart:4, preexisting:5, "true":1, "false":0}, 7173 _rootFramesTimeline = Animation._rootFramesTimeline = new SimpleTimeline(), 7174 _rootTimeline = Animation._rootTimeline = new SimpleTimeline(), 7175 _nextGCFrame = 30, 7176 _lazyRender = _internals.lazyRender = function() { 7177 var i = _lazyTweens.length, 7178 tween; 7179 _lazyLookup = {}; 7180 while (--i > -1) { 7181 tween = _lazyTweens[i]; 7182 if (tween && tween._lazy !== false) { 7183 tween.render(tween._lazy[0], tween._lazy[1], true); 7184 tween._lazy = false; 7185 } 7186 } 7187 _lazyTweens.length = 0; 7188 }; 7189 7190 _rootTimeline._startTime = _ticker.time; 7191 _rootFramesTimeline._startTime = _ticker.frame; 7192 _rootTimeline._active = _rootFramesTimeline._active = true; 7193 setTimeout(_lazyRender, 1); //on some mobile devices, there isn't a "tick" before code runs which means any lazy renders wouldn't run before the next official "tick". 7194 7195 Animation._updateRoot = TweenLite.render = function() { 7196 var i, a, p; 7197 if (_lazyTweens.length) { //if code is run outside of the requestAnimationFrame loop, there may be tweens queued AFTER the engine refreshed, so we need to ensure any pending renders occur before we refresh again. 7198 _lazyRender(); 7199 } 7200 _rootTimeline.render((_ticker.time - _rootTimeline._startTime) * _rootTimeline._timeScale, false, false); 7201 _rootFramesTimeline.render((_ticker.frame - _rootFramesTimeline._startTime) * _rootFramesTimeline._timeScale, false, false); 7202 if (_lazyTweens.length) { 7203 _lazyRender(); 7204 } 7205 if (_ticker.frame >= _nextGCFrame) { //dump garbage every 120 frames or whatever the user sets TweenLite.autoSleep to 7206 _nextGCFrame = _ticker.frame + (parseInt(TweenLite.autoSleep, 10) || 120); 7207 for (p in _tweenLookup) { 7208 a = _tweenLookup[p].tweens; 7209 i = a.length; 7210 while (--i > -1) { 7211 if (a[i]._gc) { 7212 a.splice(i, 1); 7213 } 7214 } 7215 if (a.length === 0) { 7216 delete _tweenLookup[p]; 7217 } 7218 } 7219 //if there are no more tweens in the root timelines, or if they're all paused, make the _timer sleep to reduce load on the CPU slightly 7220 p = _rootTimeline._first; 7221 if (!p || p._paused) if (TweenLite.autoSleep && !_rootFramesTimeline._first && _ticker._listeners.tick.length === 1) { 7222 while (p && p._paused) { 7223 p = p._next; 7224 } 7225 if (!p) { 7226 _ticker.sleep(); 7227 } 7228 } 7229 } 7230 }; 7231 7232 _ticker.addEventListener("tick", Animation._updateRoot); 7233 7234 var _register = function(target, tween, scrub) { 7235 var id = target._gsTweenID, a, i; 7236 if (!_tweenLookup[id || (target._gsTweenID = id = "t" + (_tweenLookupNum++))]) { 7237 _tweenLookup[id] = {target:target, tweens:[]}; 7238 } 7239 if (tween) { 7240 a = _tweenLookup[id].tweens; 7241 a[(i = a.length)] = tween; 7242 if (scrub) { 7243 while (--i > -1) { 7244 if (a[i] === tween) { 7245 a.splice(i, 1); 7246 } 7247 } 7248 } 7249 } 7250 return _tweenLookup[id].tweens; 7251 }, 7252 _onOverwrite = function(overwrittenTween, overwritingTween, target, killedProps) { 7253 var func = overwrittenTween.vars.onOverwrite, r1, r2; 7254 if (func) { 7255 r1 = func(overwrittenTween, overwritingTween, target, killedProps); 7256 } 7257 func = TweenLite.onOverwrite; 7258 if (func) { 7259 r2 = func(overwrittenTween, overwritingTween, target, killedProps); 7260 } 7261 return (r1 !== false && r2 !== false); 7262 }, 7263 _applyOverwrite = function(target, tween, props, mode, siblings) { 7264 var i, changed, curTween, l; 7265 if (mode === 1 || mode >= 4) { 7266 l = siblings.length; 7267 for (i = 0; i < l; i++) { 7268 if ((curTween = siblings[i]) !== tween) { 7269 if (!curTween._gc) { 7270 if (curTween._kill(null, target, tween)) { 7271 changed = true; 7272 } 7273 } 7274 } else if (mode === 5) { 7275 break; 7276 } 7277 } 7278 return changed; 7279 } 7280 //NOTE: Add 0.0000000001 to overcome floating point errors that can cause the startTime to be VERY slightly off (when a tween's time() is set for example) 7281 var startTime = tween._startTime + _tinyNum, 7282 overlaps = [], 7283 oCount = 0, 7284 zeroDur = (tween._duration === 0), 7285 globalStart; 7286 i = siblings.length; 7287 while (--i > -1) { 7288 if ((curTween = siblings[i]) === tween || curTween._gc || curTween._paused) { 7289 //ignore 7290 } else if (curTween._timeline !== tween._timeline) { 7291 globalStart = globalStart || _checkOverlap(tween, 0, zeroDur); 7292 if (_checkOverlap(curTween, globalStart, zeroDur) === 0) { 7293 overlaps[oCount++] = curTween; 7294 } 7295 } else if (curTween._startTime <= startTime) if (curTween._startTime + curTween.totalDuration() / curTween._timeScale > startTime) if (!((zeroDur || !curTween._initted) && startTime - curTween._startTime <= 0.0000000002)) { 7296 overlaps[oCount++] = curTween; 7297 } 7298 } 7299 7300 i = oCount; 7301 while (--i > -1) { 7302 curTween = overlaps[i]; 7303 l = curTween._firstPT; //we need to discern if there were property tweens originally; if they all get removed in the next line's _kill() call, the tween should be killed. See https://github.com/greensock/GreenSock-JS/issues/278 7304 if (mode === 2) if (curTween._kill(props, target, tween)) { 7305 changed = true; 7306 } 7307 if (mode !== 2 || (!curTween._firstPT && curTween._initted && l)) { 7308 if (mode !== 2 && !_onOverwrite(curTween, tween)) { 7309 continue; 7310 } 7311 if (curTween._enabled(false, false)) { //if all property tweens have been overwritten, kill the tween. 7312 changed = true; 7313 } 7314 } 7315 } 7316 return changed; 7317 }, 7318 _checkOverlap = function(tween, reference, zeroDur) { 7319 var tl = tween._timeline, 7320 ts = tl._timeScale, 7321 t = tween._startTime; 7322 while (tl._timeline) { 7323 t += tl._startTime; 7324 ts *= tl._timeScale; 7325 if (tl._paused) { 7326 return -100; 7327 } 7328 tl = tl._timeline; 7329 } 7330 t /= ts; 7331 return (t > reference) ? t - reference : ((zeroDur && t === reference) || (!tween._initted && t - reference < 2 * _tinyNum)) ? _tinyNum : ((t += tween.totalDuration() / tween._timeScale / ts) > reference + _tinyNum) ? 0 : t - reference - _tinyNum; 7332 }; 7333 7334 7335//---- TweenLite instance methods ----------------------------------------------------------------------------- 7336 7337 p._init = function() { 7338 var v = this.vars, 7339 op = this._overwrittenProps, 7340 dur = this._duration, 7341 immediate = !!v.immediateRender, 7342 ease = v.ease, 7343 i, initPlugins, pt, p, startVars, l; 7344 if (v.startAt) { 7345 if (this._startAt) { 7346 this._startAt.render(-1, true); //if we've run a startAt previously (when the tween instantiated), we should revert it so that the values re-instantiate correctly particularly for relative tweens. Without this, a TweenLite.fromTo(obj, 1, {x:"+=100"}, {x:"-=100"}), for example, would actually jump to +=200 because the startAt would run twice, doubling the relative change. 7347 this._startAt.kill(); 7348 } 7349 startVars = {}; 7350 for (p in v.startAt) { //copy the properties/values into a new object to avoid collisions, like var to = {x:0}, from = {x:500}; timeline.fromTo(e, 1, from, to).fromTo(e, 1, to, from); 7351 startVars[p] = v.startAt[p]; 7352 } 7353 startVars.data = "isStart"; 7354 startVars.overwrite = false;
7355 startVars.immediateRender = true; 7356 startVars.lazy = (immediate && v.lazy !== false); 7357 startVars.startAt = startVars.delay = null; //no nesting of startAt objects allowed (otherwise it could cause an infinite loop). 7358 startVars.onUpdate = v.onUpdate; 7359 startVars.onUpdateParams = v.onUpdateParams; 7360 startVars.onUpdateScope = v.onUpdateScope || v.callbackScope || this; 7361 this._startAt = TweenLite.to(this.target || {}, 0, startVars); 7362 if (immediate) { 7363 if (this._time > 0) { 7364 this._startAt = null; //tweens that render immediately (like most from() and fromTo() tweens) shouldn't revert when their parent timeline's playhead goes backward past the startTime because the initial render could have happened anytime and it shouldn't be directly correlated to this tween's startTime. Imagine setting up a complex animation where the beginning states of various objects are rendered immediately but the tween doesn't happen for quite some time - if we revert to the starting values as soon as the playhead goes backward past the tween's startTime, it will throw things off visually. Reversion should only happen in TimelineLite/Max instances where immediateRender was false (which is the default in the convenience methods like from()). 7365 } else if (dur !== 0) { 7366 return; //we skip initialization here so that overwriting doesn't occur until the tween actually begins. Otherwise, if you create several immediateRender:true tweens of the same target/properties to drop into a TimelineLite or TimelineMax, the last one created would overwrite the first ones because they didn't get placed into the timeline yet before the first render occurs and kicks in overwriting. 7367 } 7368 } 7369 } else if (v.runBackwards && dur !== 0) { 7370 //from() tweens must be handled uniquely: their beginning values must be rendered but we don't want overwriting to occur yet (when time is still 0). Wait until the tween actually begins before doing all the routines like overwriting. At that time, we should render at the END of the tween to ensure that things initialize correctly (remember, from() tweens go backwards) 7371 if (this._startAt) { 7372 this._startAt.render(-1, true); 7373 this._startAt.kill(); 7374 this._startAt = null; 7375 } else { 7376 if (this._time !== 0) { //in rare cases (like if a from() tween runs and then is invalidate()-ed), immediateRender could be true but the initial forced-render gets skipped, so there's no need to force the render in this context when the _time is greater than 0 7377 immediate = false; 7378 } 7379 pt = {}; 7380 for (p in v) { //copy props into a new object and skip any reserved props, otherwise onComplete or onUpdate or onStart could fire. We should, however, permit autoCSS to go through. 7381 if (!_reservedProps[p] || p === "autoCSS") { 7382 pt[p] = v[p]; 7383 } 7384 } 7385 pt.overwrite = 0; 7386 pt.data = "isFromStart"; //we tag the tween with as "isFromStart" so that if [inside a plugin] we need to only do something at the very END of a tween, we have a way of identifying this tween as merely the one that's setting the beginning values for a "from()" tween. For example, clearProps in CSSPlugin should only get applied at the very END of a tween and without this tag, from(...{height:100, clearProps:"height", delay:1}) would wipe the height at the beginning of the tween and after 1 second, it'd kick back in. 7387 pt.lazy = (immediate && v.lazy !== false); 7388 pt.immediateRender = immediate; //zero-duration tweens render immediately by default, but if we're not specifically instructed to render this tween immediately, we should skip this and merely _init() to record the starting values (rendering them immediately would push them to completion which is wasteful in that case - we'd have to render(-1) immediately after) 7389 this._startAt = TweenLite.to(this.target, 0, pt); 7390 if (!immediate) { 7391 this._startAt._init(); //ensures that the initial values are recorded 7392 this._startAt._enabled(false); //no need to have the tween render on the next cycle. Disable it because we'll always manually control the renders of the _startAt tween. 7393 if (this.vars.immediateRender) { 7394 this._startAt = null; 7395 } 7396 } else if (this._time === 0) { 7397 return; 7398 } 7399 } 7400 } 7401 this._ease = ease = (!ease) ? TweenLite.defaultEase : (ease instanceof Ease) ? ease : (typeof(ease) === "function") ? new Ease(ease, v.easeParams) : _easeMap[ease] || TweenLite.defaultEase; 7402 if (v.easeParams instanceof Array && ease.config) { 7403 this._ease = ease.config.apply(ease, v.easeParams); 7404 } 7405 this._easeType = this._ease._type; 7406 this._easePower = this._ease._power; 7407 this._firstPT = null; 7408 7409 if (this._targets) { 7410 l = this._targets.length; 7411 for (i = 0; i < l; i++) { 7412 if ( this._initProps( this._targets[i], (this._propLookup[i] = {}), this._siblings[i], (op ? op[i] : null), i) ) { 7413 initPlugins = true; 7414 } 7415 } 7416 } else { 7417 initPlugins = this._initProps(this.target, this._propLookup, this._siblings, op, 0); 7418 } 7419 7420 if (initPlugins) { 7421 TweenLite._onPluginEvent("_onInitAllProps", this); //reorders the array in order of priority. Uses a static TweenPlugin method in order to minimize file size in TweenLite 7422 } 7423 if (op) if (!this._firstPT) if (typeof(this.target) !== "function") { //if all tweening properties have been overwritten, kill the tween. If the target is a function, it's probably a delayedCall so let it live. 7424 this._enabled(false, false); 7425 } 7426 if (v.runBackwards) { 7427 pt = this._firstPT; 7428 while (pt) { 7429 pt.s += pt.c; 7430 pt.c = -pt.c; 7431 pt = pt._next; 7432 } 7433 } 7434 this._onUpdate = v.onUpdate; 7435 this._initted = true; 7436 }; 7437 7438 p._initProps = function(target, propLookup, siblings, overwrittenProps, index) { 7439 var p, i, initPlugins, plugin, pt, v; 7440 if (target == null) { 7441 return false;
7442 } 7443 7444 if (_lazyLookup[target._gsTweenID]) { 7445 _lazyRender(); //if other tweens of the same target have recently initted but haven't rendered yet, we've got to force the render so that the starting values are correct (imagine populating a timeline with a bunch of sequential tweens and then jumping to the end) 7446 } 7447 7448 if (!this.vars.css) if (target.style) if (target !== window && target.nodeType) if (_plugins.css) if (this.vars.autoCSS !== false) { //it's so common to use TweenLite/Max to animate the css of DOM elements, we assume that if the target is a DOM element, that's what is intended (a convenience so that users don't have to wrap things in css:{}, although we still recommend it for a slight performance boost and better specificity). Note: we cannot check "nodeType" on the window inside an iframe. 7449 _autoCSS(this.vars, target); 7450 } 7451 for (p in this.vars) { 7452 v = this.vars[p]; 7453 if (_reservedProps[p]) { 7454 if (v) if ((v instanceof Array) || (v.push && _isArray(v))) if (v.join("").indexOf("{self}") !== -1) { 7455 this.vars[p] = v = this._swapSelfInParams(v, this); 7456 } 7457 7458 } else if (_plugins[p] && (plugin = new _plugins[p]())._onInitTween(target, this.vars[p], this, index)) { 7459 7460 //t - target [object] 7461 //p - property [string] 7462 //s - start [number] 7463 //c - change [number] 7464 //f - isFunction [boolean] 7465 //n - name [string] 7466 //pg - isPlugin [boolean] 7467 //pr - priority [number] 7468 //m - mod [function | 0] 7469 this._firstPT = pt = {_next:this._firstPT, t:plugin, p:"setRatio", s:0, c:1, f:1, n:p, pg:1, pr:plugin._priority, m:0}; 7470 i = plugin._overwriteProps.length; 7471 while (--i > -1) { 7472 propLookup[plugin._overwriteProps[i]] = this._firstPT; 7473 } 7474 if (plugin._priority || plugin._onInitAllProps) { 7475 initPlugins = true; 7476 } 7477 if (plugin._onDisable || plugin._onEnable) { 7478 this._notifyPluginsOfEnabled = true; 7479 } 7480 if (pt._next) { 7481 pt._next._prev = pt; 7482 } 7483 7484 } else { 7485 propLookup[p] = _addPropTween.call(this, target, p, "get", v, p, 0, null, this.vars.stringFilter, index); 7486 } 7487 } 7488 7489 if (overwrittenProps) if (this._kill(overwrittenProps, target)) { //another tween may have tried to overwrite properties of this tween before init() was called (like if two tweens start at the same time, the one created second will run first) 7490 return this._initProps(target, propLookup, siblings, overwrittenProps, index); 7491 } 7492 if (this._overwrite > 1) if (this._firstPT) if (siblings.length > 1) if (_applyOverwrite(target, this, propLookup, this._overwrite, siblings)) { 7493 this._kill(propLookup, target); 7494 return this._initProps(target, propLookup, siblings, overwrittenProps, index); 7495 } 7496 if (this._firstPT) if ((this.vars.lazy !== false && this._duration) || (this.vars.lazy && !this._duration)) { //zero duration tweens don't lazy render by default; everything else does. 7497 _lazyLookup[target._gsTweenID] = true; 7498 } 7499 return initPlugins; 7500 }; 7501 7502 p.render = function(time, suppressEvents, force) { 7503 var prevTime = this._time, 7504 duration = this._duration, 7505 prevRawPrevTime = this._rawPrevTime, 7506 isComplete, callback, pt, rawPrevTime; 7507 if (time >= duration - 0.0000001 && time >= 0) { //to work around occasional floating point math artifacts. 7508 this._totalTime = this._time = duration; 7509 this.ratio = this._ease._calcEnd ? this._ease.getRatio(1) : 1; 7510 if (!this._reversed ) { 7511 isComplete = true; 7512 callback = "onComplete"; 7513 force = (force || this._timeline.autoRemoveChildren); //otherwise, if the animation is unpaused/activated after it's already finished, it doesn't get removed from the parent timeline. 7514 } 7515 if (duration === 0) if (this._initted || !this.vars.lazy || force) { //zero-duration tweens are tricky because we must discern the momentum/direction of time in order to determine whether the starting values should be rendered or the ending values. If the "playhead" of its timeline goes past the zero-duration tween in the forward direction or lands directly on it, the end values should be rendered, but if the timeline's "playhead" moves past it in the backward direction (from a postitive time to a negative time), the starting values must be rendered. 7516 if (this._startTime === this._timeline._duration) { //if a zero-duration tween is at the VERY end of a timeline and that timeline renders at its end, it will typically add a tiny bit of cushion to the render time to prevent roun
7516ding errors from getting in the way of tweens rendering their VERY end. If we then reverse() that timeline, the zero-duration tween will trigger its onReverseComplete even though technically the playhead didn't pass over it again. It's a very specific edge case we must accommodate. 7517 time = 0; 7518 } 7519 if (prevRawPrevTime < 0 || (time <= 0 && time >= -0.0000001) || (prevRawPrevTime === _tinyNum && this.data !== "isPause")) if (prevRawPrevTime !== time) { //note: when this.data is "isPause", it's a callback added by addPause() on a timeline that we should not be triggered when LEAVING its exact start time. In other words, tl.addPause(1).play(1) shouldn't pause. 7520 force = true; 7521 if (prevRawPrevTime > _tinyNum) { 7522 callback = "onReverseComplete"; 7523 } 7524 } 7525 this._rawPrevTime = rawPrevTime = (!suppressEvents || time || prevRawPrevTime === time) ? time : _tinyNum; //when the playhead arrives at EXACTLY time 0 (right on top) of a zero-duration tween, we need to discern if events are suppressed so that when the playhead moves again (next time), it'll trigger the callback. If events are NOT suppressed, obviously the callback would be triggered in this render. Basically, the callback should fire either when the playhead ARRIVES or LEAVES this exact spot, not both. Imagine doing a timeline.seek(0) and there's a callback that sits at 0. Since events are suppressed on that seek() by default, nothing will fire, but when the playhead moves off of that position, the callback should fire. This behavior is what people intuitively expect. We set the _rawPrevTime to be a precise tiny number to indicate this scenario rather than using another property/variable which would increase memory usage. This technique is less readable, but more efficient. 7526 } 7527 7528 } else if (time < 0.0000001) { //to work around occasional floating point math artifacts, round super small values to 0. 7529 this._totalTime = this._time = 0; 7530 this.ratio = this._ease._calcEnd ? this._ease.getRatio(0) : 0; 7531 if (prevTime !== 0 || (duration === 0 && prevRawPrevTime > 0)) { 7532 callback = "onReverseComplete"; 7533 isComplete = this._reversed; 7534 } 7535 if (time < 0) { 7536 this._active = false; 7537 if (duration === 0) if (this._initted || !this.vars.lazy || force) { //zero-duration tweens are tricky because we must discern the momentum/direction of time in order to determine whether the starting values should be rendered or the ending values. If the "playhead" of its timeline goes past the zero-duration tween in the forward direction or lands directly on it, the end values should be rendered, but if the timeline's "playhead" moves past it in the backward direction (from a postitive time to a negative time), the starting values must be rendered. 7538 if (prevRawPrevTime >= 0 && !(prevRawPrevTime === _tinyNum && this.data === "isPause")) { 7539 force = true; 7540 } 7541 this._rawPrevTime = rawPrevTime = (!suppressEvents || time || prevRawPrevTime === time) ? time : _tinyNum; //when the playhead arrives at EXACTLY time 0 (right on top) of a zero-duration tween, we need to discern if events are suppressed so that when the playhead moves again (next time), it'll trigger the callback. If events are NOT suppressed, obviously the callback would be triggered in this render. Basically, the callback should fire either when the playhead ARRIVES or LEAVES this exact spot, not both. Imagine doing a timeline.seek(0) and there's a callback that sits at 0. Since events are suppressed on that seek() by default, nothing will fire, but when the playhead moves off of that position, the callback should fire. This behavior is what people intuitively expect. We set the _rawPrevTime to be a precise tiny number to indicate this scenario rather than using another property/variable which would increase memory usage. This technique is less readable, but more efficient. 7542 } 7543 } 7544 if (!this._initted || (this._startAt && this._startAt.progress())) { //if we render the very beginning (time == 0) of a fromTo(), we must force the render (normal tweens wouldn't need to render at a time of 0 when the prevTime was also 0). This is also mandatory to make sure overwriting kicks in immediately. Also, we check progress() because if startAt has already rendered at its end, we should force a render at its beginning. Otherwise, if you put the playhead directly on top of where a fromTo({immediateRender:false}) starts, and then move it backwards, the from() won't revert its values. 7545 force = true; 7546 } 7547 } else { 7548 this._totalTime = this._time = time; 7549 7550 if (this._easeType) { 7551 var r = time / duration, type = this._easeType, pow = this._easePower; 7552 if (type === 1 || (type === 3 && r >= 0.5)) { 7553 r = 1 - r; 7554 } 7555 if (type === 3) { 7556 r *= 2; 7557 } 7558 if (pow === 1) { 7559 r *= r; 7560 } else if (pow === 2) { 7561 r *= r * r; 7562 } else if (pow === 3) { 7563 r *= r * r * r; 7564 } else if (pow === 4) { 7565 r *= r * r * r * r; 7566 } 7567 7568 if (type === 1) { 7569 this.ratio = 1 - r; 7570 } else if (type === 2) { 7571 this.ratio = r; 7572 } else if (time / duration < 0.5) { 7573 this.ratio = r / 2; 7574 } else { 7575 this.ratio = 1 - (r / 2); 7576 } 7577 7578 } else { 7579 this.ratio = this._ease.getRatio(time / duration); 7580 } 7581 } 7582 7583 if (this._time === prevTime && !force) { 7584 return; 7585 } else if (!this._initted) { 7586 this._init(); 7587 if (!this._initted || this._gc) { //immediateRender tweens typically won't initialize until the playhead advances (_time is greater than 0) in order to ensure that over
7587writing occurs properly. Also, if all of the tweening properties have been overwritten (which would cause _gc to be true, as set in _init()), we shouldn't continue otherwise an onStart callback could be called for example. 7588 return; 7589 } else if (!force && this._firstPT && ((this.vars.lazy !== false && this._duration) || (this.vars.lazy && !this._duration))) { 7590 this._time = this._totalTime = prevTime; 7591 this._rawPrevTime = prevRawPrevTime; 7592 _lazyTweens.push(this); 7593 this._lazy = [time, suppressEvents]; 7594 return; 7595 } 7596 //_ease is initially set to defaultEase, so now that init() has run, _ease is set properly and we need to recalculate the ratio. Overall this is faster than using conditional logic earlier in the method to avoid having to set ratio twice because we only init() once but renderTime() gets called VERY frequently. 7597 if (this._time && !isComplete) { 7598 this.ratio = this._ease.getRatio(this._time / duration); 7599 } else if (isComplete && this._ease._calcEnd) { 7600 this.ratio = this._ease.getRatio((this._time === 0) ? 0 : 1); 7601 } 7602 } 7603 if (this._lazy !== false) { //in case a lazy render is pending, we should flush it because the new render is occurring now (imagine a lazy tween instantiating and then immediately the user calls tween.seek(tween.duration()), skipping to the end - the end render would be forced, and then if we didn't flush the lazy render, it'd fire AFTER the seek(), rendering it at the wrong time. 7604 this._lazy = false; 7605 } 7606 if (!this._active) if (!this._paused && this._time !== prevTime && time >= 0) { 7607 this._active = true; //so that if the user renders a tween (as opposed to the timeline rendering it), the timeline is forced to re-render and align it with the proper time/frame on the next rendering cycle. Maybe the tween already finished but the user manually re-renders it as halfway done. 7608 } 7609 if (prevTime === 0) { 7610 if (this._startAt) { 7611 if (time >= 0) { 7612 this._startAt.render(time, true, force); 7613 } else if (!callback) { 7614 callback = "_dummyGS"; //if no callback is defined, use a dummy value just so that the condition at the end evaluates as true because _startAt should render AFTER the normal render loop when the time is negative. We could handle this in a more intuitive way, of course, but the render loop is the MOST important thing to optimize, so this technique allows us to avoid adding extra conditional logic in a high-frequency area. 7615 } 7616 } 7617 if (this.vars.onStart) if (this._time !== 0 || duration === 0) if (!suppressEvents) { 7618 this._callback("onStart"); 7619 } 7620 } 7621 pt = this._firstPT; 7622 while (pt) { 7623 if (pt.f) { 7624 pt.t[pt.p](pt.c * this.ratio + pt.s); 7625 } else { 7626 pt.t[pt.p] = pt.c * this.ratio + pt.s; 7627 } 7628 pt = pt._next; 7629 } 7630 7631 if (this._onUpdate) { 7632 if (time < 0) if (this._startAt && time !== -0.0001) { //if the tween is positioned at the VERY beginning (_startTime 0) of its parent timeline, it's illegal for the playhead to go back further, so we should not render the recorded startAt values. 7633 this._startAt.render(time, true, force); //note: for performance reasons, we tuck this conditional logic inside less traveled areas (most tweens don't have an onUpdate). We'd just have it at the end before the onComplete, but the values should be updated before any onUpdate is called, so we ALSO put it here and then if it's not called, we do so later near the onComplete. 7634 } 7635 if (!suppressEvents) if (this._time !== prevTime || isComplete || force) { 7636 this._callback("onUpdate"); 7637 } 7638 } 7639 if (callback) if (!this._gc || force) { //check _gc because there's a chance that kill() could be called in an onUpdate 7640 if (time < 0 && this._startAt && !this._onUpdate && time !== -0.0001) { //-0.0001 is a special value that we use when looping back to the beginning of a repeated TimelineMax, in which case we shouldn't render the _startAt values. 7641 this._startAt.render(time, true, force); 7642 } 7643 if (isComplete) { 7644 if (this._timeline.autoRemoveChildren) { 7645 this._enabled(false, false); 7646 } 7647 this._active = false;
7648 } 7649 if (!suppressEvents && this.vars[callback]) { 7650 this._callback(callback); 7651 } 7652 if (duration === 0 && this._rawPrevTime === _tinyNum && rawPrevTime !== _tinyNum) { //the onComplete or onReverseComplete could trigger movement of the playhead and for zero-duration tweens (which must discern direction) that land directly back on their start time, we don't want to fire again on the next render. Think of several addPause()'s in a timeline that forces the playhead to a certain spot, but what if it's already paused and another tween is tweening the "time" of the timeline? Each time it moves [forward] past that spot, it would move back, and since suppressEvents is true, it'd reset _rawPrevTime to _tinyNum so that when it begins again, the callback would fire (so ultimately it could bounce back and forth during that tween). Again, this is a very uncommon scenario, but possible nonetheless. 7653 this._rawPrevTime = 0; 7654 } 7655 } 7656 }; 7657 7658 p._kill = function(vars, target, overwritingTween) { 7659 if (vars === "all") { 7660 vars = null; 7661 } 7662 if (vars == null) if (target == null || target === this.target) { 7663 this._lazy = false; 7664 return this._enabled(false, false); 7665 } 7666 target = (typeof(target) !== "string") ? (target || this._targets || this.target) : TweenLite.selector(target) || target; 7667 var simultaneousOverwrite = (overwritingTween && this._time && overwritingTween._startTime === this._startTime && this._timeline === overwritingTween._timeline), 7668 firstPT = this._firstPT, 7669 i, overwrittenProps, p, pt, propLookup, changed, killProps, record, killed; 7670 if ((_isArray(target) || _isSelector(target)) && typeof(target[0]) !== "number") { 7671 i = target.length; 7672 while (--i > -1) { 7673 if (this._kill(vars, target[i], overwritingTween)) { 7674 changed = true; 7675 } 7676 } 7677 } else { 7678 if (this._targets) { 7679 i = this._targets.length; 7680 while (--i > -1) { 7681 if (target === this._targets[i]) { 7682 propLookup = this._propLookup[i] || {}; 7683 this._overwrittenProps = this._overwrittenProps || []; 7684 overwrittenProps = this._overwrittenProps[i] = vars ? this._overwrittenProps[i] || {} : "all"; 7685 break; 7686 } 7687 } 7688 } else if (target !== this.target) { 7689 return false; 7690 } else { 7691 propLookup = this._propLookup; 7692 overwrittenProps = this._overwrittenProps = vars ? this._overwrittenProps || {} : "all"; 7693 } 7694 7695 if (propLookup) { 7696 killProps = vars || propLookup; 7697 record = (vars !== overwrittenProps && overwrittenProps !== "all" && vars !== propLookup && (typeof(vars) !== "object" || !vars._tempKill)); //_tempKill is a super-secret way to delete a particular tweening property but NOT have it remembered as an official overwritten property (like in BezierPlugin) 7698 if (overwritingTween && (TweenLite.onOverwrite || this.vars.onOverwrite)) { 7699 for (p in killProps) { 7700 if (propLookup[p]) { 7701 if (!killed) { 7702 killed = []; 7703 } 7704 killed.push(p); 7705 } 7706 } 7707 if ((killed || !vars) && !_onOverwrite(this, overwritingTween, target, killed)) { //if the onOverwrite returned false, that means the user wants to override the overwriting (cancel it). 7708 return false; 7709 } 7710 } 7711 7712 for (p in killProps) { 7713 if ((pt = propLookup[p])) { 7714 if (simultaneousOverwrite) { //if another tween overwrites this one and they both start at exactly the same time, yet this tween has already rendered once (for example, at 0.001) because it's first in the queue, we should revert the values to where they were at 0 so that the starting values aren't contaminated on the overwriting tween. 7715 if (pt.f) { 7716 pt.t[pt.p](pt.s); 7717 } else { 7718 pt.t[pt.p] = pt.s; 7719 } 7720 changed = true; 7721 } 7722 if (pt.pg && pt.t._kill(killProps)) { 7723 changed = true; //some plugins need to be notified so they can perform cleanup tasks first 7724 } 7725 if (!pt.pg || pt.t._overwriteProps.length === 0) { 7726 if (pt._prev) { 7727 pt._prev._next = pt._next; 7728 } else if (pt === this._firstPT) { 7729 this._firstPT = pt._next; 7730 } 7731 if (pt._next) { 7732 pt._next._prev = pt._prev; 7733 } 7734 pt._next = pt._prev = null; 7735 } 7736 delete propLookup[p]; 7737 } 7738 if (record) { 7739 overwrittenProps[p] = 1; 7740 } 7741 } 7742 if (!this._firstPT && this._initted && firstPT) { //if all tweening properties are killed, kill the tween. Without this line, if there's a tween with multiple targets and then you killTweensOf() each target individually, the tween would technically still remain active and fire its onComplete even though there aren't any more properties tweening. 7743 this._enabled(false, false); 7744 } 7745 } 7746 } 7747 return changed; 7748 }; 7749 7750 p.invalidate = function() { 7751 if (this._notifyPluginsOfEnabled) { 7752 TweenLite._onPluginEvent("_onDisable", this); 7753 } 7754 this._firstPT = this._overwrittenProps = this._startAt = this._onUpdate = null; 7755 this._notifyPluginsOfEnabled = this._active = this._lazy = false;
7756 this._propLookup = (this._targets) ? {} : []; 7757 Animation.prototype.invalidate.call(this); 7758 if (this.vars.immediateRender) { 7759 this._time = -_tinyNum; //forces a render without having to set the render() "force" parameter to true because we want to allow lazying by default (using the "force" parameter always forces an immediate full render) 7760 this.render(Math.min(0, -this._delay)); //in case delay is negative. 7761 } 7762 return this; 7763 }; 7764 7765 p._enabled = function(enabled, ignoreTimeline) { 7766 if (!_tickerActive) { 7767 _ticker.wake(); 7768 } 7769 if (enabled && this._gc) { 7770 var targets = this._targets, 7771 i; 7772 if (targets) { 7773 i = targets.length; 7774 while (--i > -1) { 7775 this._siblings[i] = _register(targets[i], this, true); 7776 } 7777 } else { 7778 this._siblings = _register(this.target, this, true); 7779 } 7780 } 7781 Animation.prototype._enabled.call(this, enabled, ignoreTimeline); 7782 if (this._notifyPluginsOfEnabled) if (this._firstPT) { 7783 return TweenLite._onPluginEvent((enabled ? "_onEnable" : "_onDisable"), this); 7784 } 7785 return false; 7786 }; 7787 7788 7789//----TweenLite static methods ----------------------------------------------------- 7790 7791 TweenLite.to = function(target, duration, vars) { 7792 return new TweenLite(target, duration, vars); 7793 }; 7794 7795 TweenLite.from = function(target, duration, vars) { 7796 vars.runBackwards = true; 7797 vars.immediateRender = (vars.immediateRender != false); 7798 return new TweenLite(target, duration, vars); 7799 }; 7800 7801 TweenLite.fromTo = function(target, duration, fromVars, toVars) { 7802 toVars.startAt = fromVars; 7803 toVars.immediateRender = (toVars.immediateRender != false && fromVars.immediateRender != false); 7804 return new TweenLite(target, duration, toVars); 7805 }; 7806 7807 TweenLite.delayedCall = function(delay, callback, params, scope, useFrames) { 7808 return new TweenLite(callback, 0, {delay:delay, onComplete:callback, onCompleteParams:params, callbackScope:scope, onReverseComplete:callback, onReverseCompleteParams:params, immediateRender:false, lazy:false, useFrames:useFrames, overwrite:0}); 7809 }; 7810 7811 TweenLite.set = function(target, vars) { 7812 return new TweenLite(target, 0, vars); 7813 }; 7814 7815 TweenLite.getTweensOf = function(target, onlyActive) { 7816 if (target == null) { return []; } 7817 target = (typeof(target) !== "string") ? target : TweenLite.selector(target) || target; 7818 var i, a, j, t; 7819 if ((_isArray(target) || _isSelector(target)) && typeof(target[0]) !== "number") { 7820 i = target.length; 7821 a = []; 7822 while (--i > -1) { 7823 a = a.concat(TweenLite.getTweensOf(target[i], onlyActive)); 7824 } 7825 i = a.length; 7826 //now get rid of any duplicates (tweens of arrays of objects could cause duplicates) 7827 while (--i > -1) { 7828 t = a[i]; 7829 j = i; 7830 while (--j > -1) { 7831 if (t === a[j]) { 7832 a.splice(i, 1); 7833 } 7834 } 7835 } 7836 } else if (target._gsTweenID) { 7837 a = _register(target).concat(); 7838 i = a.length; 7839 while (--i > -1) { 7840 if (a[i]._gc || (onlyActive && !a[i].isActive())) { 7841 a.splice(i, 1); 7842 } 7843 } 7844 } 7845 return a || []; 7846 }; 7847 7848 TweenLite.killTweensOf = TweenLite.killDelayedCallsTo = function(target, onlyActive, vars) { 7849 if (typeof(onlyActive) === "object") { 7850 vars = onlyActive; //for backwards compatibility (before "onlyActive" parameter was inserted) 7851 onlyActive = false; 7852 } 7853 var a = TweenLite.getTweensOf(target, onlyActive), 7854 i = a.length; 7855 while (--i > -1) { 7856 a[i]._kill(vars, target); 7857 } 7858 }; 7859 7860 7861 7862/* 7863 * ---------------------------------------------------------------- 7864 * TweenPlugin (could easily be split out as a separate file/class, but included for ease of use (so that people don't need to include another script call before loading plugins which is easy to forget) 7865 * ---------------------------------------------------------------- 7866 */ 7867 var TweenPlugin = _class("plugins.TweenPlugin", function(props, priority) { 7868 this._overwriteProps = (props || "").split(","); 7869 this._propName = this._overwriteProps[0]; 7870 this._priority = priority || 0; 7871 this._super = TweenPlugin.prototype; 7872 }, true); 7873 7874 p = TweenPlugin.prototype; 7875 TweenPlugin.version = "1.19.0"; 7876 TweenPlugin.API = 2; 7877 p._firstPT = null; 7878 p._addTween = _addPropTween; 7879 p.setRatio = _setRatio; 7880 7881 p._kill = function(lookup) { 7882 var a = this._overwriteProps, 7883 pt = this._firstPT, 7884 i; 7885 if (lookup[this._propName] != null) { 7886 this._overwriteProps = []; 7887 } else { 7888 i = a.length; 7889 while (--i > -1) { 7890 if (lookup[a[i]] != null) { 7891 a.splice(i, 1); 7892 } 7893 } 7894 } 7895 while (pt) { 7896 if (lookup[pt.n] != null) { 7897 if (pt._next) { 7898 pt._next._prev = pt._prev; 7899 } 7900 if (pt._prev) { 7901 pt._prev._next = pt._next; 7902 pt._prev = null; 7903 } else if (this._firstPT === pt) { 7904 this._firstPT = pt._next; 7905 } 7906 } 7907 pt = pt._next; 7908 } 7909 return false;
7910 }; 7911 7912 p._mod = p._roundProps = function(lookup) { 7913 var pt = this._firstPT, 7914 val; 7915 while (pt) { 7916 val = lookup[this._propName] || (pt.n != null && lookup[ pt.n.split(this._propName + "_").join("") ]); 7917 if (val && typeof(val) === "function") { //some properties that are very plugin-specific add a prefix named after the _propName plus an underscore, so we need to ignore that extra stuff here. 7918 if (pt.f === 2) { 7919 pt.t._applyPT.m = val; 7920 } else { 7921 pt.m = val; 7922 } 7923 } 7924 pt = pt._next; 7925 } 7926 }; 7927 7928 TweenLite._onPluginEvent = function(type, tween) { 7929 var pt = tween._firstPT, 7930 changed, pt2, first, last, next; 7931 if (type === "_onInitAllProps") { 7932 //sorts the PropTween linked list in order of priority because some plugins need to render earlier/later than others, like MotionBlurPlugin applies its effects after all x/y/alpha tweens have rendered on each frame. 7933 while (pt) { 7934 next = pt._next; 7935 pt2 = first; 7936 while (pt2 && pt2.pr > pt.pr) { 7937 pt2 = pt2._next; 7938 } 7939 if ((pt._prev = pt2 ? pt2._prev : last)) { 7940 pt._prev._next = pt; 7941 } else { 7942 first = pt; 7943 } 7944 if ((pt._next = pt2)) { 7945 pt2._prev = pt; 7946 } else { 7947 last = pt; 7948 } 7949 pt = next; 7950 } 7951 pt = tween._firstPT = first; 7952 } 7953 while (pt) { 7954 if (pt.pg) if (typeof(pt.t[type]) === "function") if (pt.t[type]()) { 7955 changed = true; 7956 } 7957 pt = pt._next; 7958 } 7959 return changed; 7960 }; 7961 7962 TweenPlugin.activate = function(plugins) { 7963 var i = plugins.length; 7964 while (--i > -1) { 7965 if (plugins[i].API === TweenPlugin.API) { 7966 _plugins[(new plugins[i]())._propName] = plugins[i]; 7967 } 7968 } 7969 return true; 7970 }; 7971 7972 //provides a more concise way to define plugins that have no dependencies besides TweenPlugin and TweenLite, wrapping common boilerplate stuff into one function (added in 1.9.0). You don't NEED to use this to define a plugin - the old way still works and can be useful in certain (rare) situations. 7973 _gsDefine.plugin = function(config) { 7974 if (!config || !config.propName || !config.init || !config.API) { throw "illegal plugin definition."; } 7975 var propName = config.propName, 7976 priority = config.priority || 0, 7977 overwriteProps = config.overwriteProps, 7978 map = {init:"_onInitTween", set:"setRatio", kill:"_kill", round:"_mod", mod:"_mod", initAll:"_onInitAllProps"}, 7979 Plugin = _class("plugins." + propName.charAt(0).toUpperCase() + propName.substr(1) + "Plugin", 7980 function() { 7981 TweenPlugin.call(this, propName, priority); 7982 this._overwriteProps = overwriteProps || []; 7983 }, (config.global === true)), 7984 p = Plugin.prototype = new TweenPlugin(propName), 7985 prop; 7986 p.constructor = Plugin; 7987 Plugin.API = config.API; 7988 for (prop in map) { 7989 if (typeof(config[prop]) === "function") { 7990 p[map[prop]] = config[prop]; 7991 } 7992 } 7993 Plugin.version = config.version; 7994 TweenPlugin.activate([Plugin]); 7995 return Plugin; 7996 }; 7997 7998 7999 //now run through all the dependencies discovered and if any are missing, log that to the console as a warning. This is why it's best to have TweenLite load last - it can check all the dependencies for you. 8000 a = window._gsQueue; 8001 if (a) { 8002 for (i = 0; i < a.length; i++) { 8003 a[i](); 8004 } 8005 for (p in _defLookup) { 8006 if (!_defLookup[p].func) { 8007 window.console.log("GSAP encountered missing dependency: " + p); 8008 } 8009 } 8010 } 8011 8012 _tickerActive = false; //ensures that the first official animation forces a ticker.tick() to update the time when it is instantiated 8013 8014})((typeof(module) !== "undefined" && module.exports && typeof(global) !== "undefined") ? global : this || window, "TweenMax");
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.