1/******************************************** 2 - THEMEPUNCH TOOLS Ver. 1.0 - 3 Last Update of Tools 27.02.2015 4*********************************************/ 5 6 7/* 8* @fileOverview TouchSwipe - jQuery Plugin 9* @version 1.6.12 10* 11* @author Matt Bryson http://www.github.com/mattbryson 12* @see https://github.com/mattbryson/TouchSwipe-Jquery-Plugin 13* @see http://labs.rampinteractive.co.uk/touchSwipe/ 14* @see http://plugins.jquery.com/project/touchSwipe 15* 16* Copyright (c) 2010-2015 Matt Bryson 17* Dual licensed under the MIT or GPL Version 2 licenses. 18* 19*/ 20 21/* 22* 23* Changelog 24* $Date: 2010-12-12 (Wed, 12 Dec 2010) $ 25* $version: 1.0.0 26* $version: 1.0.1 - removed multibyte comments 27* 28* $Date: 2011-21-02 (Mon, 21 Feb 2011) $ 29* $version: 1.1.0 - added allowPageScroll property to allow swiping and scrolling of page 30* - changed handler signatures so one handler can be used for multiple events 31* $Date: 2011-23-02 (Wed, 23 Feb 2011) $ 32* $version: 1.2.0 - added click handler. This is fired if the user simply clicks and does not swipe. The event object and click target are passed to handler. 33* - If you use the http://code.google.com/p/jquery-ui-for-ipad-and-iphone/ plugin, you can also assign jQuery mouse events to children of a touchSwipe object. 34* $version: 1.2.1 - removed console log! 35* 36* $version: 1.2.2 - Fixed bug where scope was not preserved in callback methods. 37* 38* $Date: 2011-28-04 (Thurs, 28 April 2011) $ 39* $version: 1.2.4 - Changed licence terms to be MIT or GPL inline with jQuery. Added check for support of touch events to stop non compatible browsers erroring. 40* 41* $Date: 2011-27-09 (Tues, 27 September 2011) $
42* $version: 1.2.5 - Added support for testing swipes with mouse on desktop browser (thanks to https://github.com/joelhy) 43* 44* $Date: 2012-14-05 (Mon, 14 May 2012) $ 45* $version: 1.2.6 - Added timeThreshold between start and end touch, so user can ignore slow swipes (thanks to Mark Chase). Default is null, all swipes are detected 46* 47* $Date: 2012-05-06 (Tues, 05 June 2012) $ 48* $version: 1.2.7 - Changed time threshold to have null default for backwards compatibility. Added duration param passed back in events, and refactored how time is handled. 49* 50* $Date: 2012-05-06 (Tues, 05 June 2012) $ 51* $version: 1.2.8 - Added the possibility to return a value like null or false in the trigger callback. In that way we can control when the touch start/move should take effect or not (simply by returning in some cases return null; or return false;) This effects the ontouchstart/ontouchmove event. 52* 53* $Date: 2012-06-06 (Wed, 06 June 2012) $ 54* $version: 1.3.0 - Refactored whole plugin to allow for methods to be executed, as well as exposed defaults for user override. Added 'enable', 'disable', and 'destroy' methods 55* 56* $Date: 2012-05-06 (Fri, 05 June 2012) $ 57* $version: 1.3.1 - Bug fixes - bind() with false as last argument is no longer supported in jQuery 1.6, also, if you just click, the duration is now returned correctly. 58* 59* $Date: 2012-29-07 (Sun, 29 July 2012) $ 60* $version: 1.3.2 - Added fallbackToMouseEvents option to NOT capture mouse events on non touch devices. 61* - Added "all" fingers value to the fingers property, so any combination of fingers triggers the swipe, allowing event handlers to check the finger count 62* 63* $Date: 2012-09-08 (Thurs, 9 Aug 2012) $ 64* $version: 1.3.3 - Code tidy prep for minefied version 65* 66* $Date: 2012-04-10 (wed, 4 Oct 2012) $ 67* $version: 1.4.0 - Added pinch support, pinchIn and pinchOut 68* 69* $Date: 2012-11-10 (Thurs, 11 Oct 2012) $ 70* $version: 1.5.0 - Added excludedElements, a jquery selector that specifies child elements that do NOT trigger swipes. By default, this is one select that removes all form, input select, button and anchor elements. 71* 72* $Date: 2012-22-10 (Mon, 22 Oct 2012) $ 73* $version: 1.5.1 - Fixed bug with jQuery 1.8 and trailing comma in excludedElements 74* - Fixed bug with IE and eventPreventDefault() 75* $Date: 2013-01-12 (Fri, 12 Jan 2013) $ 76* $version: 1.6.0 - Fixed bugs with pinching, mainly when both pinch and swipe enabled, as well as adding time threshold for multifinger gestures, so releasing one finger beofre the other doesnt trigger as single finger gesture. 77* - made the demo site all static local HTML pages so they can be run locally by a developer 78* - added jsDoc comments and added documentation for the plugin 79* - code tidy 80* - added triggerOnTouchLeave property that will end the event when the user swipes off the element. 81* $Date: 2013-03-23 (Sat, 23 Mar 2013) $
82* $version: 1.6.1 - Added support for ie8 touch events 83* $version: 1.6.2 - Added support for events binding with on / off / bind in jQ for all callback names. 84* - Deprecated the 'click' handler in favour of tap. 85* - added cancelThreshold property 86* - added option method to update init options at runtime 87* $version 1.6.3 - added doubletap, longtap events and longTapThreshold, doubleTapThreshold property 88* 89* $Date: 2013-04-04 (Thurs, 04 April 2013) $ 90* $version 1.6.4 - Fixed bug with cancelThreshold introduced in 1.6.3, where swipe status no longer fired start event, and stopped once swiping back. 91* 92* $Date: 2013-08-24 (Sat, 24 Aug 2013) $ 93* $version 1.6.5 - Merged a few pull requests fixing various bugs, added AMD support. 94* 95* $Date: 2014-06-04 (Wed, 04 June 2014) $ 96* $version 1.6.6 - Merge of pull requests. 97* - IE10 touch support 98* - Only prevent default event handling on valid swipe 99* - Separate license/changelog comment 100* - Detect if the swipe is valid at the end of the touch event. 101* - Pass fingerdata to event handlers. 102* - Add 'hold' gesture 103* - Be more tolerant about the tap distance 104* - Typos and minor fixes 105* 106* $Date: 2015-22-01 (Thurs, 22 Jan 2015) $ 107* $version 1.6.7 - Added patch from https://github.com/mattbryson/TouchSwipe-Jquery-Plugin/issues/206 to fix memory leak 108* 109* $Date: 2015-2-2 (Mon, 2 Feb 2015) $ 110* $version 1.6.8 - Added preventDefaultEvents option to proxy events regardless. 111* - Fixed issue with swipe and pinch not triggering at the same time 112* 113* $Date: 2015-9-6 (Tues, 9 June 2015) $ 114* $version 1.6.9 - Added PR from jdalton/hybrid to fix pointer events 115* - Added scrolling demo 116* - Added version property to plugin 117* 118* $Date: 2015-1-10 (Wed, 1 October 2015) $ 119* $version 1.6.10 - Added PR from beatspace to fix tap events 120* $version 1.6.11 - Added PRs from indri-indri ( Doc tidyup), kkirsche ( Bower tidy up ), UziTech (preventDefaultEvents fixes ) 121* - Allowed setting multiple options via .swipe("options", options_hash) and more simply .swipe(options_hash) or exisitng instances 122* $version 1.6.12 - Fixed bug with multi finger releases above 2 not triggering events 123*/ 124 125/** 126 * See (http://jquery.com/). 127 * @name $ 128 * @class 129 * See the jQuery Library (http://jquery.com/) for full details. This just 130 * documents the function and classes that are added to jQuery by this plug-in. 131 */ 132 133/** 134 * See (http://jquery.com/) 135 * @name fn 136 * @class 137 * See the jQuery Library (http://jquery.com/) for full details. This just 138 * documents the function and classes that are added to jQuery by this plug-in. 139 * @memberOf $ 140 */ 141 142 143 144(function (factory) { 145 if (typeof define === 'function' && define.amd && define.amd.jQuery) { 146 // AMD. Register as anonymous module. 147 define(['jquery'], factory); 148 } else { 149 // Browser globals. 150 factory(jQuery); 151 } 152}(function ($) { 153 "use strict"; 154 155 //Constants 156 var VERSION = "1.6.12", 157 LEFT = "left", 158 RIGHT = "right", 159 UP = "up", 160 DOWN = "down", 161 IN = "in", 162 OUT = "out", 163 164 NONE = "none", 165 AUTO = "auto", 166 167 SWIPE = "swipe", 168 PINCH = "pinch", 169 TAP = "tap", 170 DOUBLE_TAP = "doubletap", 171 LONG_TAP = "longtap", 172 HOLD = "hold", 173 174 HORIZONTAL = "horizontal", 175 VERTICAL = "vertical", 176 177 ALL_FINGERS = "all", 178 179 DOUBLE_TAP_THRESHOLD = 10, 180 181 PHASE_START = "start", 182 PHASE_MOVE = "move", 183 PHASE_END = "end", 184 PHASE_CANCEL = "cancel", 185 186 SUPPORTS_TOUCH = 'ontouchstart' in window, 187 188 SUPPORTS_POINTER_IE10 = window.navigator.msPointerEnabled && !window.navigator.pointerEnabled, 189 190 SUPPORTS_POINTER = window.navigator.pointerEnabled || window.navigator.msPointerEnabled, 191 192 PLUGIN_NS = 'TouchSwipe'; 193 194 195 196 /** 197 * The default configuration, and available options to configure touch swipe with. 198 * You can set the default values by updating any of the properties prior to instantiation. 199 * @name $.fn.swipe.defaults 200 * @namespace 201 * @property {int} [fingers=1] The number of fingers to detect in a swipe. Any swipes that do not meet this requirement will NOT trigger swipe handlers. 202 * @property {int} [threshold=75] The number of pixels that the user must move their finger by before it is considered a swipe. 203 * @property {int} [cancelThreshold=null] The number of pixels that the user must move their finger back from the original swipe direction to cancel the gesture. 204 * @property {int} [pinchThreshold=20] The number of pixels that the user must pinch their finger by before it is considered a pinch. 205 * @property {int} [maxTimeThreshold=null] Time, in milliseconds, between touchStart and touchEnd must NOT exceed in order to be considered a swipe. 206 * @property {int} [fingerReleaseThreshold=250] Time in milliseconds between releasing multiple fingers. If 2 fingers are down, and are released one after the other, if they are within this threshold, it counts as a simultaneous release. 207 * @property {int} [longTapThreshold=500] Time in milliseconds between tap and release for a long tap 208 * @property {int} [doubleTapThreshold=200] Time in milliseconds between 2 taps to count as a double tap 209 * @property {function} [swipe=null] A handler to catch all swipes. See {@link $.fn.swipe#event:swipe} 210 * @property {function} [swipeLeft=null] A handler that is triggered for "left" swipes. See {@link $.fn.swipe#event:swipeLeft} 211 * @property {function} [swipeRight=null] A handler that is triggered for "right" swipes. See {@link $.fn.swipe#event:swipeRight} 212 * @property {function} [swipeUp=null] A handler that is triggered for "up" swipes. See {@link $.fn.swipe#event:swipeUp} 213 * @property {function} [swipeDown=null] A handler that is triggered for "down" swipes. See {@link $.fn.swipe#event:swipeDown} 214 * @property {function} [swipeStatus=null] A handler triggered for every phase of the swipe. See {@link $.fn.swipe#event:swipeStatus} 215 * @property {function} [pinchIn=null] A handler triggered for pinch in events. See {@link $.fn.swipe#event:pinchIn} 216 * @property {function} [pinchOut=null] A handler triggered for pinch out events. See {@link $.fn.swipe#event:pinchOut} 217 * @property {function} [pinchStatus=null] A handler triggered for every phase of a pinch. See {@link $.fn.swipe#event:pinchStatus} 218 * @property {function} [tap=null] A handler triggered when a user just taps on the item, rather than swipes it. If they do not move, tap is triggered, if they do move, it is not. 219 * @property {function} [doubleTap=null] A handler triggered when a user double taps on the item. The delay between taps can be set with the doubleTapThreshold property. See {@link $.fn.swipe.defaults#doubleTapThreshold} 220 * @property {function} [longTap=null] A handler triggered when a user long taps on the item. The delay between start and end can be set with the longTapThreshold property. See {@link $.fn.swipe.defaults#longTapThreshold} 221 * @property (function) [hold=null] A handler triggered when a user reaches longTapThreshold on the item. See {@link $.fn.swipe.defaults#longTapThreshold} 222 * @property {boolean} [triggerOnTouchEnd=true] If true, the swipe events are triggered when the touch end event is received (user releases finger). If false, it will be triggered on reaching the threshold, and then cancel the touch event automatically. 223 * @property {boolean} [triggerOnTouchLeave=false] If true, then when the user leaves the swipe object, the swipe will end and trigger appropriate handlers. 224 * @property {string|undefined} [allowPageScroll='auto'] How the browser handles page scrolls when the user is swiping on a touchSwipe object. See {@link $.fn.swipe.pageScroll}. <br/><br/> 225 <code>"auto"</code> : all undefined swipes will cause the page to scroll in that direction. <br/> 226 <code>"none"</code> : the page will not scroll when user swipes. <br/> 227 <code>"horizontal"</code> : will force page to scroll on horizontal swipes. <br/> 228 <code>"vertical"</code> : will force page to scroll on vertical swipes. <br/> 229 * @property {boolean} [fallbackToMouseEvents=true] If true mouse events are used when run on a non touch device, false will stop swipes being triggered by mouse events on non tocuh devices. 230 * @property {string} [excludedElements="button, input, select, textarea, a, .noSwipe"] A jquery selector that specifies child elements that do NOT trigger swipes. By default this excludes all form, input, select, button, anchor and .noSwipe elements. 231 * @property {boolean} [preventDefaultEvents=true] by default default events are cancelled, so the page doesn't move. You can dissable this so both native events fire as well as your handlers. 232 233 */ 234 var defaults = { 235 fingers: 1, 236 threshold: 75, 237 cancelThreshold:null, 238 pinchThreshold:20, 239 maxTimeThreshold: null, 240 fingerReleaseThreshold:250, 241 longTapThreshold:500, 242 doubleTapThreshold:200, 243 swipe: null, 244 swipeLeft: null, 245 swipeRight: null, 246 swipeUp: null, 247 swipeDown: null, 248 swipeStatus: null, 249 pinchIn:null, 250 pinchOut:null, 251 pinchStatus:null, 252 click:null, //Deprecated since 1.6.2 253 tap:null, 254 doubleTap:null, 255 longTap:null, 256 hold:null, 257 triggerOnTouchEnd: true, 258 triggerOnTouchLeave:false, 259 allowPageScroll: "auto", 260 fallbackToMouseEvents: true, 261 excludedElements:"label, button, input, select, textarea, a, .noSwipe", 262 preventDefaultEvents:true 263 }; 264 265 266 267 /** 268 * Applies TouchSwipe behaviour to one or more jQuery objects. 269 * The TouchSwipe plugin can be instantiated via this method, or methods within
270 * TouchSwipe can be executed via this method as per jQuery plugin architecture. 271 * An existing plugin can have its options changed simply by re calling .swipe(options) 272 * @see TouchSwipe 273 * @class 274 * @param {Mixed} method If the current DOMNode is a TouchSwipe object, and <code>method</code> is a TouchSwipe method, then 275 * the <code>method</code> is executed, and any following arguments are passed to the TouchSwipe method. 276 * If <code>method</code> is an object, then the TouchSwipe class is instantiated on the current DOMNode, passing the 277 * configuration properties defined in the object. See TouchSwipe 278 * 279 */ 280 $.fn.swipe = function (method) { 281 var $this = $(this), 282 plugin = $this.data(PLUGIN_NS); 283 284 //Check if we are already instantiated and trying to execute a method 285 if (plugin && typeof method === 'string') { 286 if (plugin[method]) { 287 return plugin[method].apply(this, Array.prototype.slice.call(arguments, 1)); 288 } else { 289 $.error('Method ' + method + ' does not exist on jQuery.swipe'); 290 } 291 } 292 293 //Else update existing plugin with new options hash 294 else if (plugin && typeof method === 'object') { 295 plugin['option'].apply(this, arguments); 296 } 297 298 //Else not instantiated and trying to pass init object (or nothing) 299 else if (!plugin && (typeof method === 'object' || !method)) { 300 return init.apply(this, arguments); 301 } 302 303 return $this; 304 }; 305 306 /** 307 * The version of the plugin 308 * @readonly 309 */ 310 $.fn.swipe.version = VERSION; 311 312 313 314 //Expose our defaults so a user could override the plugin defaults 315 $.fn.swipe.defaults = defaults; 316 317 /** 318 * The phases that a touch event goes through. The <code>phase</code> is passed to the event handlers. 319 * These properties are read only, attempting to change them will not alter the values passed to the event handlers. 320 * @namespace 321 * @readonly 322 * @property {string} PHASE_START Constant indicating the start phase of the touch event. Value is <code>"start"</code>. 323 * @property {string} PHASE_MOVE Constant indicating the move phase of the touch event. Value is <code>"move"</code>. 324 * @property {string} PHASE_END Constant indicating the end phase of the touch event. Value is <code>"end"</code>. 325 * @property {string} PHASE_CANCEL Constant indicating the cancel phase of the touch event. Value is <code>"cancel"</code>. 326 */ 327 $.fn.swipe.phases = { 328 PHASE_START: PHASE_START, 329 PHASE_MOVE: PHASE_MOVE, 330 PHASE_END: PHASE_END, 331 PHASE_CANCEL: PHASE_CANCEL 332 }; 333 334 /** 335 * The direction constants that are passed to the event handlers. 336 * These properties are read only, attempting to change them will not alter the values passed to the event handlers. 337 * @namespace 338 * @readonly 339 * @property {string} LEFT Constant indicating the left direction. Value is <code>"left"</code>. 340 * @property {string} RIGHT Constant indicating the right direction. Value is <code>"right"</code>. 341 * @property {string} UP Constant indicating the up direction. Value is <code>"up"</code>. 342 * @property {string} DOWN Constant indicating the down direction. Value is <code>"cancel"</code>. 343 * @property {string} IN Constant indicating the in direction. Value is <code>"in"</code>. 344 * @property {string} OUT Constant indicating the out direction. Value is <code>"out"</code>. 345 */ 346 $.fn.swipe.directions = { 347 LEFT: LEFT, 348 RIGHT: RIGHT, 349 UP: UP, 350 DOWN: DOWN, 351 IN : IN, 352 OUT: OUT 353 }; 354 355 /** 356 * The page scroll constants that can be used to set the value of <code>allowPageScroll</code> option 357 * These properties are read only 358 * @namespace 359 * @readonly 360 * @see $.fn.swipe.defaults#allowPageScroll 361 * @property {string} NONE Constant indicating no page scrolling is allowed. Value is <code>"none"</code>. 362 * @property {string} HORIZONTAL Constant indicating horizontal page scrolling is allowed. Value is <code>"horizontal"</code>. 363 * @property {string} VERTICAL Constant indicating vertical page scrolling is allowed. Value is <code>"vertical"</code>. 364 * @property {string} AUTO Constant indicating either horizontal or vertical will be allowed, depending on the swipe handlers registered. Value is <code>"auto"</code>. 365 */ 366 $.fn.swipe.pageScroll = { 367 NONE: NONE, 368 HORIZONTAL: HORIZONTAL, 369 VERTICAL: VERTICAL, 370 AUTO: AUTO 371 }; 372 373 /** 374 * Constants representing the number of fingers used in a swipe. These are used to set both the value of <code>fingers</code> in the 375 * options object, as well as the value of the <code>fingers</code> event property. 376 * These properties are read only, attempting to change them will not alter the values passed to the event handlers. 377 * @namespace 378 * @readonly 379 * @see $.fn.swipe.defaults#fingers 380 * @property {string} ONE Constant indicating 1 finger is to be detected / was detected. Value is <code>1</code>. 381 * @property {string} TWO Constant indicating 2 fingers are to be detected / were detected. Value is <code>2</code>. 382 * @property {string} THREE Constant indicating 3 finger are to be detected / were detected. Value is <code>3</code>. 383 * @property {string} FOUR Constant indicating 4 finger are to be detected / were detected. Not all devices support this. Value is <code>4</code>. 384 * @property {string} FIVE Constant indicating 5 finger are to be detected / were detected. Not all devices support this. Value is <code>5</code>. 385 * @property {string} ALL Constant indicating any combination of finger are to be detected. Value is <code>"all"</code>. 386 */ 387 $.fn.swipe.fingers = { 388 ONE: 1, 389 TWO: 2, 390 THREE: 3, 391 FOUR: 4, 392 FIVE: 5, 393 ALL: ALL_FINGERS 394 }; 395 396 /** 397 * Initialise the plugin for each DOM element matched
vendor: 18,166 bytes, lines 398-938
398 * This creates a new instance of the main TouchSwipe class for each DOM element, and then 399 * saves a reference to that instance in the elements data property. 400 * @internal 401 */ 402 function init(options) { 403 //Prep and extend the options 404 if (options && (options.allowPageScroll === undefined && (options.swipe !== undefined || options.swipeStatus !== undefined))) { 405 options.allowPageScroll = NONE; 406 } 407 408 //Check for deprecated options 409 //Ensure that any old click handlers are assigned to the new tap, unless we have a tap 410 if(options.click!==undefined && options.tap===undefined) { 411 options.tap = options.click; 412 } 413 414 if (!options) { 415 options = {}; 416 } 417 418 //pass empty object so we dont modify the defaults 419 options = $.extend({}, $.fn.swipe.defaults, options); 420 421 //For each element instantiate the plugin 422 return this.each(function () { 423 var $this = $(this); 424 425 //Check we havent already initialised the plugin 426 var plugin = $this.data(PLUGIN_NS); 427 428 if (!plugin) { 429 plugin = new TouchSwipe(this, options); 430 $this.data(PLUGIN_NS, plugin); 431 } 432 }); 433 } 434 435 /** 436 * Main TouchSwipe Plugin Class. 437 * Do not use this to construct your TouchSwipe object, use the jQuery plugin method $.fn.swipe(); {@link $.fn.swipe} 438 * @private 439 * @name TouchSwipe 440 * @param {DOMNode} element The HTML DOM object to apply to plugin to 441 * @param {Object} options The options to configure the plugin with. @link {$.fn.swipe.defaults} 442 * @see $.fh.swipe.defaults 443 * @see $.fh.swipe 444 * @class 445 */ 446 function TouchSwipe(element, options) { 447 448 //take a local/instacne level copy of the options - should make it this.options really... 449 var options = $.extend({}, options); 450 451 var useTouchEvents = (SUPPORTS_TOUCH || SUPPORTS_POINTER || !options.fallbackToMouseEvents), 452 START_EV = useTouchEvents ? (SUPPORTS_POINTER ? (SUPPORTS_POINTER_IE10 ? 'MSPointerDown' : 'pointerdown') : 'touchstart') : 'mousedown', 453 MOVE_EV = useTouchEvents ? (SUPPORTS_POINTER ? (SUPPORTS_POINTER_IE10 ? 'MSPointerMove' : 'pointermove') : 'touchmove') : 'mousemove', 454 END_EV = useTouchEvents ? (SUPPORTS_POINTER ? (SUPPORTS_POINTER_IE10 ? 'MSPointerUp' : 'pointerup') : 'touchend') : 'mouseup', 455 LEAVE_EV = useTouchEvents ? null : 'mouseleave', //we manually detect leave on touch devices, so null event here 456 CANCEL_EV = (SUPPORTS_POINTER ? (SUPPORTS_POINTER_IE10 ? 'MSPointerCancel' : 'pointercancel') : 'touchcancel'); 457 458 459 460 //touch properties 461 var distance = 0, 462 direction = null, 463 duration = 0, 464 startTouchesDistance = 0, 465 endTouchesDistance = 0, 466 pinchZoom = 1, 467 pinchDistance = 0, 468 pinchDirection = 0, 469 maximumsMap=null; 470 471 472 473 //jQuery wrapped element for this instance 474 var $element = $(element); 475 476 //Current phase of th touch cycle 477 var phase = "start"; 478 479 // the current number of fingers being used. 480 var fingerCount = 0; 481 482 //track mouse points / delta 483 var fingerData = {}; 484 485 //track times 486 var startTime = 0, 487 endTime = 0, 488 previousTouchEndTime=0, 489 fingerCountAtRelease=0, 490 doubleTapStartTime=0; 491 492 //Timeouts 493 var singleTapTimeout=null, 494 holdTimeout=null; 495 496 // Add gestures to all swipable areas if supported 497 try { 498 $element.bind(START_EV, touchStart); 499 $element.bind(CANCEL_EV, touchCancel); 500 } 501 catch (e) { 502 $.error('events not supported ' + START_EV + ',' + CANCEL_EV + ' on jQuery.swipe'); 503 } 504 505 // 506 //Public methods 507 // 508 509 /** 510 * re-enables the swipe plugin with the previous configuration 511 * @function 512 * @name $.fn.swipe#enable 513 * @return {DOMNode} The Dom element that was registered with TouchSwipe 514 * @example $("#element").swipe("enable"); 515 */ 516 this.enable = function () { 517 $element.bind(START_EV, touchStart); 518 $element.bind(CANCEL_EV, touchCancel); 519 return $element; 520 }; 521 522 /** 523 * disables the swipe plugin 524 * @function 525 * @name $.fn.swipe#disable 526 * @return {DOMNode} The Dom element that is now registered with TouchSwipe 527 * @example $("#element").swipe("disable"); 528 */ 529 this.disable = function () { 530 removeListeners(); 531 return $element; 532 }; 533 534 /** 535 * Destroy the swipe plugin completely. To use any swipe methods, you must re initialise the plugin. 536 * @function 537 * @name $.fn.swipe#destroy 538 * @example $("#element").swipe("destroy"); 539 */ 540 this.destroy = function () { 541 removeListeners(); 542 $element.data(PLUGIN_NS, null); 543 $element = null; 544 }; 545 546 547 /** 548 * Allows run time updating of the swipe configuration options. 549 * @function 550 * @name $.fn.swipe#option 551 * @param {String} property The option property to get or set, or a has of multiple options to set 552 * @param {Object} [value] The value to set the property to 553 * @return {Object} If only a property name is passed, then that property value is returned. If nothing is passed the current options hash is returned. 554 * @example $("#element").swipe("option", "threshold"); // return the threshold 555 * @example $("#element").swipe("option", "threshold", 100); // set the threshold after init 556 * @example $("#element").swipe("option", {threshold:100, fingers:3} ); // set multiple properties after init 557 * @example $("#element").swipe({threshold:100, fingers:3} ); // set multiple properties after init - the "option" method is optional! 558 * @example $("#element").swipe("option"); // Return the current options hash 559 * @see $.fn.swipe.defaults 560 * 561 */ 562 this.option = function (property, value) { 563 564 if(typeof property === 'object') { 565 options = $.extend(options, property); 566 } else if(options[property]!==undefined) { 567 if(value===undefined) { 568 return options[property]; 569 } else { 570 options[property] = value; 571 } 572 } else if (!property) { 573 return options; 574 } else { 575 $.error('Option ' + property + ' does not exist on jQuery.swipe.options'); 576 } 577 578 return null; 579 } 580 581 582 583 // 584 // Private methods 585 // 586 587 // 588 // EVENTS 589 // 590 /** 591 * Event handler for a touch start event. 592 * Stops the default click event from triggering and stores where we touched 593 * @inner 594 * @param {object} jqEvent The normalised jQuery event object. 595 */ 596 function touchStart(jqEvent) { 597 598 //If we already in a touch event (a finger already in use) then ignore subsequent ones.. 599 if( getTouchInProgress() ) 600 return; 601 602 //Check if this element matches any in the excluded elements selectors, or its parent is excluded, if so, DON'T swipe 603 if( $(jqEvent.target).closest( options.excludedElements, $element ).length>0 ) 604 return; 605 606 //As we use Jquery bind for events, we need to target the original event object 607 //If these events are being programmatically triggered, we don't have an original event object, so use the Jq one. 608 var event = jqEvent.originalEvent ? jqEvent.originalEvent : jqEvent; 609 610 var ret, 611 touches = event.touches, 612 evt = touches ? touches[0] : event; 613 614 phase = PHASE_START; 615 616 //If we support touches, get the finger count 617 if (touches) { 618 // get the total number of fingers touching the screen 619 fingerCount = touches.length; 620 } 621 //Else this is the desktop, so stop the browser from dragging content 622 else if(options.preventDefaultEvents !== false) { 623 jqEvent.preventDefault(); //call this on jq event so we are cross browser 624 } 625 626 //clear vars.. 627 distance = 0; 628 direction = null; 629 pinchDirection=null; 630 duration = 0; 631 startTouchesDistance=0; 632 endTouchesDistance=0; 633 pinchZoom = 1; 634 pinchDistance = 0; 635 maximumsMap=createMaximumsData(); 636 cancelMultiFingerRelease(); 637 638 //Create the default finger data 639 createFingerData( 0, evt ); 640 641 // check the number of fingers is what we are looking for, or we are capturing pinches 642 if (!touches || (fingerCount === options.fingers || options.fingers === ALL_FINGERS) || hasPinches()) { 643 // get the coordinates of the touch 644 startTime = getTimeStamp(); 645 646 if(fingerCount==2) { 647 //Keep track of the initial pinch distance, so we can calculate the diff later 648 //Store second finger data as start 649 createFingerData( 1, touches[1] ); 650 startTouchesDistance = endTouchesDistance = calculateTouchesDistance(fingerData[0].start, fingerData[1].start); 651 } 652 653 if (options.swipeStatus || options.pinchStatus) { 654 ret = triggerHandler(event, phase); 655 } 656 } 657 else { 658 //A touch with more or less than the fingers we are looking for, so cancel 659 ret = false; 660 } 661 662 //If we have a return value from the users handler, then return and cancel 663 if (ret === false) { 664 phase = PHASE_CANCEL; 665 triggerHandler(event, phase); 666 return ret; 667 } 668 else { 669 if (options.hold) { 670 holdTimeout = setTimeout($.proxy(function() { 671 //Trigger the event 672 $element.trigger('hold', [event.target]); 673 //Fire the callback 674 if(options.hold) { 675 ret = options.hold.call($element, event, event.target); 676 } 677 }, this), options.longTapThreshold ); 678 } 679 680 setTouchInProgress(true); 681 } 682 683 return null; 684 }; 685 686 687 688 /** 689 * Event handler for a touch move event. 690 * If we change fingers during move, then cancel the event 691 * @inner 692 * @param {object} jqEvent The normalised jQuery event object. 693 */ 694 function touchMove(jqEvent) { 695 696 //As we use Jquery bind for events, we need to target the original event object 697 //If these events are being programmatically triggered, we don't have an original event object, so use the Jq one. 698 var event = jqEvent.originalEvent ? jqEvent.originalEvent : jqEvent; 699 700 //If we are ending, cancelling, or within the threshold of 2 fingers being released, don't track anything.. 701 if (phase === PHASE_END || phase === PHASE_CANCEL || inMultiFingerRelease()) 702 return; 703 704 var ret, 705 touches = event.touches, 706 evt = touches ? touches[0] : event; 707 708 709 //Update the finger data 710 var currentFinger = updateFingerData(evt); 711 endTime = getTimeStamp(); 712 713 if (touches) { 714 fingerCount = touches.length; 715 } 716 717 if (options.hold) 718 clearTimeout(holdTimeout); 719 720 phase = PHASE_MOVE; 721 722 //If we have 2 fingers get Touches distance as well 723 if(fingerCount==2) { 724 725 //Keep track of the initial pinch distance, so we can calculate the diff later 726 //We do this here as well as the start event, in case they start with 1 finger, and the press 2 fingers 727 if(startTouchesDistance==0) { 728 //Create second finger if this is the first time... 729 createFingerData( 1, touches[1] ); 730 731 startTouchesDistance = endTouchesDistance = calculateTouchesDistance(fingerData[0].start, fingerData[1].start); 732 } else { 733 //Else just update the second finger 734 updateFingerData(touches[1]); 735 736 endTouchesDistance = calculateTouchesDistance(fingerData[0].end, fingerData[1].end); 737 pinchDirection = calculatePinchDirection(fingerData[0].end, fingerData[1].end); 738 } 739 740 741 pinchZoom = calculatePinchZoom(startTouchesDistance, endTouchesDistance); 742 pinchDistance = Math.abs(startTouchesDistance - endTouchesDistance); 743 } 744 745 746 747 748 if ( (fingerCount === options.fingers || options.fingers === ALL_FINGERS) || !touches || hasPinches() ) { 749 750 direction = calculateDirection(currentFinger.start, currentFinger.end); 751 752 //Check if we need to prevent default event (page scroll / pinch zoom) or not 753 validateDefaultEvent(jqEvent, direction); 754 755 //Distance and duration are all off the main finger 756 distance = calculateDistance(currentFinger.start, currentFinger.end); 757 duration = calculateDuration(); 758 759 //Cache the maximum distance we made in this direction 760 setMaxDistance(direction, distance); 761 762 763 if (options.swipeStatus || options.pinchStatus) { 764 ret = triggerHandler(event, phase); 765 } 766 767 768 //If we trigger end events when threshold are met, or trigger events when touch leaves element 769 if(!options.triggerOnTouchEnd || options.triggerOnTouchLeave) { 770 771 var inBounds = true; 772 773 //If checking if we leave the element, run the bounds check (we can use touchleave as its not supported on webkit) 774 if(options.triggerOnTouchLeave) { 775 var bounds = getbounds( this ); 776 inBounds = isInBounds( currentFinger.end, bounds ); 777 } 778 779 //Trigger end handles as we swipe if thresholds met or if we have left the element if the user has asked to check these.. 780 if(!options.triggerOnTouchEnd && inBounds) { 781 phase = getNextPhase( PHASE_MOVE ); 782 } 783 //We end if out of bounds here, so set current phase to END, and check if its modified 784 else if(options.triggerOnTouchLeave && !inBounds ) { 785 phase = getNextPhase( PHASE_END ); 786 } 787 788 if(phase==PHASE_CANCEL || phase==PHASE_END) { 789 triggerHandler(event, phase); 790 } 791 } 792 } 793 else { 794 phase = PHASE_CANCEL; 795 triggerHandler(event, phase); 796 } 797 798 if (ret === false) { 799 phase = PHASE_CANCEL; 800 triggerHandler(event, phase); 801 } 802 } 803 804 805 806 807 /** 808 * Event handler for a touch end event. 809 * Calculate the direction and trigger events 810 * @inner 811 * @param {object} jqEvent The normalised jQuery event object. 812 */ 813 function touchEnd(jqEvent) { 814 //As we use Jquery bind for events, we need to target the original event object 815 //If these events are being programmatically triggered, we don't have an original event object, so use the Jq one. 816 var event = jqEvent.originalEvent ? jqEvent.originalEvent : jqEvent, 817 touches = event.touches; 818 819 //If we are still in a touch with the device wait a fraction and see if the other finger comes up 820 //if it does within the threshold, then we treat it as a multi release, not a single release and end the touch / swipe 821 if (touches) { 822 if(touches.length && !inMultiFingerRelease()) { 823 startMultiFingerRelease(); 824 return true; 825 } else if (touches.length && inMultiFingerRelease()) { 826 return true; 827 } 828 } 829 830 //If a previous finger has been released, check how long ago, if within the threshold, then assume it was a multifinger release. 831 //This is used to allow 2 fingers to release fractionally after each other, whilst maintainig the event as containg 2 fingers, not 1 832 if(inMultiFingerRelease()) { 833 fingerCount=fingerCountAtRelease; 834 } 835 836 //Set end of swipe 837 endTime = getTimeStamp(); 838 839 //Get duration incase move was never fired 840 duration = calculateDuration(); 841 842 //If we trigger handlers at end of swipe OR, we trigger during, but they didnt trigger and we are still in the move phase 843 if(didSwipeBackToCancel() || !validateSwipeDistance()) { 844 phase = PHASE_CANCEL; 845 triggerHandler(event, phase); 846 } else if (options.triggerOnTouchEnd || (options.triggerOnTouchEnd == false && phase === PHASE_MOVE)) { 847 //call this on jq event so we are cross browser 848 if(options.preventDefaultEvents !== false) { 849 jqEvent.preventDefault(); 850 } 851 phase = PHASE_END; 852 triggerHandler(event, phase); 853 } 854 //Special cases - A tap should always fire on touch end regardless, 855 //So here we manually trigger the tap end handler by itself 856 //We dont run trigger handler as it will re-trigger events that may have fired already 857 else if (!options.triggerOnTouchEnd && hasTap()) { 858 //Trigger the pinch events... 859 phase = PHASE_END; 860 triggerHandlerForGesture(event, phase, TAP); 861 } 862 else if (phase === PHASE_MOVE) { 863 phase = PHASE_CANCEL; 864 triggerHandler(event, phase); 865 } 866 867 setTouchInProgress(false); 868 869 return null; 870 } 871 872 873 874 /** 875 * Event handler for a touch cancel event. 876 * Clears current vars 877 * @inner 878 */ 879 function touchCancel() { 880 // reset the variables back to default values 881 fingerCount = 0; 882 endTime = 0; 883 startTime = 0; 884 startTouchesDistance=0; 885 endTouchesDistance=0; 886 pinchZoom=1; 887 888 //If we were in progress of tracking a possible multi touch end, then re set it. 889 cancelMultiFingerRelease(); 890 891 setTouchInProgress(false); 892 } 893 894 895 /** 896 * Event handler for a touch leave event. 897 * This is only triggered on desktops, in touch we work this out manually 898 * as the touchleave event is not supported in webkit 899 * @inner 900 */ 901 function touchLeave(jqEvent) { 902 //If these events are being programmatically triggered, we don't have an original event object, so use the Jq one. 903 var event = jqEvent.originalEvent ? jqEvent.originalEvent : jqEvent; 904 905 //If we have the trigger on leave property set.... 906 if(options.triggerOnTouchLeave) { 907 phase = getNextPhase( PHASE_END ); 908 triggerHandler(event, phase); 909 } 910 } 911 912 /** 913 * Removes all listeners that were associated with the plugin 914 * @inner 915 */ 916 function removeListeners() { 917 $element.unbind(START_EV, touchStart); 918 $element.unbind(CANCEL_EV, touchCancel); 919 $element.unbind(MOVE_EV, touchMove); 920 $element.unbind(END_EV, touchEnd); 921 922 //we only have leave events on desktop, we manually calculate leave on touch as its not supported in webkit 923 if(LEAVE_EV) { 924 $element.unbind(LEAVE_EV, touchLeave); 925 } 926 927 setTouchInProgress(false); 928 } 929 930 931 /** 932 * Checks if the time and distance thresholds have been met, and if so then the appropriate handlers are fired. 933 */ 934 function getNextPhase(currentPhase) { 935 936 var nextPhase = currentPhase; 937 938 // Ensure we have valid swipe (under time and over distance and check if we are out of bound...)
939 var validTime = validateSwipeTime(); 940 var validDistance = validateSwipeDistance(); 941 var didCancel = didSwipeBackToCancel(); 942 943 //If we have exceeded our time, then cancel 944 if(!validTime || didCancel) { 945 nextPhase = PHASE_CANCEL; 946 } 947 //Else if we are moving, and have reached distance then end 948 else if (validDistance && currentPhase == PHASE_MOVE && (!options.triggerOnTouchEnd || options.triggerOnTouchLeave) ) { 949 nextPhase = PHASE_END; 950 } 951 //Else if we have ended by leaving and didn't reach distance, then cancel 952 else if (!validDistance && currentPhase==PHASE_END && options.triggerOnTouchLeave) { 953 nextPhase = PHASE_CANCEL; 954 } 955 956 return nextPhase; 957 } 958 959 960 /** 961 * Trigger the relevant event handler 962 * The handlers are passed the original event, the element that was swiped, and in the case of the catch all handler, the direction that was swiped, "left", "right", "up", or "down" 963 * @param {object} event the original event object 964 * @param {string} phase the phase of the swipe (start, end cancel etc) {@link $.fn.swipe.phases} 965 * @inner 966 */ 967 function triggerHandler(event, phase) { 968 969 var ret, 970 touches = event.touches; 971 972 //Swipes and pinches are not mutually exclusive - can happend at same time, so need to trigger 2 events potentially 973 if( (didSwipe() && hasSwipes()) || (didPinch() && hasPinches()) ) { 974 // SWIPE GESTURES 975 if(didSwipe() && hasSwipes()) { //hasSwipes as status needs to fire even if swipe is invalid 976 //Trigger the swipe events... 977 ret = triggerHandlerForGesture(event, phase, SWIPE); 978 } 979 980 // PINCH GESTURES (if the above didn't cancel) 981 if((didPinch() && hasPinches()) && ret!==false) { 982 //Trigger the pinch events... 983 ret = triggerHandlerForGesture(event, phase, PINCH); 984 } 985 } 986 else { 987 988 // CLICK / TAP (if the above didn't cancel) 989 if(didDoubleTap() && ret!==false) { 990 //Trigger the tap events... 991 ret = triggerHandlerForGesture(event, phase, DOUBLE_TAP); 992 } 993 994 // CLICK / TAP (if the above didn't cancel) 995 else if(didLongTap() && ret!==false) { 996 //Trigger the tap events... 997 ret = triggerHandlerForGesture(event, phase, LONG_TAP); 998 } 999 1000 // CLICK / TAP (if the above didn't cancel) 1001 else if(didTap() && ret!==false) { 1002 //Trigger the tap event.. 1003 ret = triggerHandlerForGesture(event, phase, TAP); 1004 } 1005 } 1006 1007 // If we are cancelling the gesture, then manually trigger the reset handler 1008 if (phase === PHASE_CANCEL) { 1009 if(hasSwipes() ) { 1010 ret = triggerHandlerForGesture(event, phase, SWIPE); 1011 } 1012 1013 if(hasPinches()) { 1014 ret = triggerHandlerForGesture(event, phase, PINCH); 1015 } 1016 touchCancel(event); 1017 } 1018 1019 // If we are ending the gesture, then manually trigger the reset handler IF all fingers are off 1020 if(phase === PHASE_END) { 1021 //If we support touch, then check that all fingers are off before we cancel 1022 if (touches) { 1023 if(!touches.length) { 1024 touchCancel(event); 1025 } 1026 } 1027 else { 1028 touchCancel(event); 1029 } 1030 } 1031 1032 return ret; 1033 } 1034 1035 1036 1037 /** 1038 * Trigger the relevant event handler 1039 * The handlers are passed the original event, the element that was swiped, and in the case of the catch all handler, the direction that was swiped, "left", "right", "up", or "down" 1040 * @param {object} event the original event object 1041 * @param {string} phase the phase of the swipe (start, end cancel etc) {@link $.fn.swipe.phases} 1042 * @param {string} gesture the gesture to trigger a handler for : PINCH or SWIPE {@link $.fn.swipe.gestures} 1043 * @return Boolean False, to indicate that the event should stop propagation, or void. 1044 * @inner 1045 */ 1046 function triggerHandlerForGesture(event, phase, gesture) { 1047 1048 var ret; 1049 1050 //SWIPES.... 1051 if(gesture==SWIPE) { 1052 //Trigger status every time.. 1053 1054 //Trigger the event... 1055 $element.trigger('swipeStatus', [phase, direction || null, distance || 0, duration || 0, fingerCount, fingerData]); 1056 1057 //Fire the callback 1058 if (options.swipeStatus) { 1059 ret = options.swipeStatus.call($element, event, phase, direction || null, distance || 0, duration || 0, fingerCount, fingerData); 1060 //If the status cancels, then dont run the subsequent event handlers.. 1061 if(ret===false) return false; 1062 } 1063 1064 1065 1066 1067 if (phase == PHASE_END && validateSwipe()) {
vendor: 25,281 bytes, lines 1068-1915
1068 //Fire the catch all event 1069 $element.trigger('swipe', [direction, distance, duration, fingerCount, fingerData]); 1070 1071 //Fire catch all callback 1072 if (options.swipe) { 1073 ret = options.swipe.call($element, event, direction, distance, duration, fingerCount, fingerData); 1074 //If the status cancels, then dont run the subsequent event handlers.. 1075 if(ret===false) return false; 1076 } 1077 1078 //trigger direction specific event handlers 1079 switch (direction) { 1080 case LEFT: 1081 //Trigger the event 1082 $element.trigger('swipeLeft', [direction, distance, duration, fingerCount, fingerData]); 1083 1084 //Fire the callback 1085 if (options.swipeLeft) { 1086 ret = options.swipeLeft.call($element, event, direction, distance, duration, fingerCount, fingerData); 1087 } 1088 break; 1089 1090 case RIGHT: 1091 //Trigger the event 1092 $element.trigger('swipeRight', [direction, distance, duration, fingerCount, fingerData]); 1093 1094 //Fire the callback 1095 if (options.swipeRight) { 1096 ret = options.swipeRight.call($element, event, direction, distance, duration, fingerCount, fingerData); 1097 } 1098 break; 1099 1100 case UP: 1101 //Trigger the event 1102 $element.trigger('swipeUp', [direction, distance, duration, fingerCount, fingerData]); 1103 1104 //Fire the callback 1105 if (options.swipeUp) { 1106 ret = options.swipeUp.call($element, event, direction, distance, duration, fingerCount, fingerData); 1107 } 1108 break; 1109 1110 case DOWN: 1111 //Trigger the event 1112 $element.trigger('swipeDown', [direction, distance, duration, fingerCount, fingerData]); 1113 1114 //Fire the callback 1115 if (options.swipeDown) { 1116 ret = options.swipeDown.call($element, event, direction, distance, duration, fingerCount, fingerData); 1117 } 1118 break; 1119 } 1120 } 1121 } 1122 1123 1124 //PINCHES.... 1125 if(gesture==PINCH) { 1126 //Trigger the event 1127 $element.trigger('pinchStatus', [phase, pinchDirection || null, pinchDistance || 0, duration || 0, fingerCount, pinchZoom, fingerData]); 1128 1129 //Fire the callback 1130 if (options.pinchStatus) { 1131 ret = options.pinchStatus.call($element, event, phase, pinchDirection || null, pinchDistance || 0, duration || 0, fingerCount, pinchZoom, fingerData); 1132 //If the status cancels, then dont run the subsequent event handlers.. 1133 if(ret===false) return false; 1134 } 1135 1136 if(phase==PHASE_END && validatePinch()) { 1137 1138 switch (pinchDirection) { 1139 case IN: 1140 //Trigger the event 1141 $element.trigger('pinchIn', [pinchDirection || null, pinchDistance || 0, duration || 0, fingerCount, pinchZoom, fingerData]); 1142 1143 //Fire the callback 1144 if (options.pinchIn) { 1145 ret = options.pinchIn.call($element, event, pinchDirection || null, pinchDistance || 0, duration || 0, fingerCount, pinchZoom, fingerData); 1146 } 1147 break; 1148 1149 case OUT: 1150 //Trigger the event 1151 $element.trigger('pinchOut', [pinchDirection || null, pinchDistance || 0, duration || 0, fingerCount, pinchZoom, fingerData]); 1152 1153 //Fire the callback 1154 if (options.pinchOut) { 1155 ret = options.pinchOut.call($element, event, pinchDirection || null, pinchDistance || 0, duration || 0, fingerCount, pinchZoom, fingerData); 1156 } 1157 break; 1158 } 1159 } 1160 } 1161 1162 1163 1164 1165 1166 if(gesture==TAP) { 1167 if(phase === PHASE_CANCEL || phase === PHASE_END) { 1168 1169 1170 //Cancel any existing double tap 1171 clearTimeout(singleTapTimeout); 1172 //Cancel hold timeout 1173 clearTimeout(holdTimeout); 1174 1175 //If we are also looking for doubelTaps, wait incase this is one... 1176 if(hasDoubleTap() && !inDoubleTap()) { 1177 //Cache the time of this tap 1178 doubleTapStartTime = getTimeStamp(); 1179 1180 //Now wait for the double tap timeout, and trigger this single tap 1181 //if its not cancelled by a double tap 1182 singleTapTimeout = setTimeout($.proxy(function() { 1183 doubleTapStartTime=null; 1184 //Trigger the event 1185 $element.trigger('tap', [event.target]); 1186 1187 1188 //Fire the callback 1189 if(options.tap) { 1190 ret = options.tap.call($element, event, event.target); 1191 } 1192 }, this), options.doubleTapThreshold ); 1193 1194 } else { 1195 doubleTapStartTime=null; 1196 1197 //Trigger the event 1198 $element.trigger('tap', [event.target]); 1199 1200 1201 //Fire the callback 1202 if(options.tap) { 1203 ret = options.tap.call($element, event, event.target); 1204 } 1205 } 1206 } 1207 } 1208 1209 else if (gesture==DOUBLE_TAP) { 1210 if(phase === PHASE_CANCEL || phase === PHASE_END) { 1211 //Cancel any pending singletap 1212 clearTimeout(singleTapTimeout); 1213 doubleTapStartTime=null; 1214 1215 //Trigger the event 1216 $element.trigger('doubletap', [event.target]); 1217 1218 //Fire the callback 1219 if(options.doubleTap) { 1220 ret = options.doubleTap.call($element, event, event.target); 1221 } 1222 } 1223 } 1224 1225 else if (gesture==LONG_TAP) { 1226 if(phase === PHASE_CANCEL || phase === PHASE_END) { 1227 //Cancel any pending singletap (shouldnt be one) 1228 clearTimeout(singleTapTimeout); 1229 doubleTapStartTime=null; 1230 1231 //Trigger the event 1232 $element.trigger('longtap', [event.target]); 1233 1234 //Fire the callback 1235 if(options.longTap) { 1236 ret = options.longTap.call($element, event, event.target); 1237 } 1238 } 1239 } 1240 1241 return ret; 1242 } 1243 1244 1245 1246 1247 // 1248 // GESTURE VALIDATION 1249 // 1250 1251 /** 1252 * Checks the user has swipe far enough 1253 * @return Boolean if <code>threshold</code> has been set, return true if the threshold was met, else false. 1254 * If no threshold was set, then we return true. 1255 * @inner 1256 */ 1257 function validateSwipeDistance() { 1258 var valid = true; 1259 //If we made it past the min swipe distance.. 1260 if (options.threshold !== null) { 1261 valid = distance >= options.threshold; 1262 } 1263 1264 return valid; 1265 } 1266 1267 /** 1268 * Checks the user has swiped back to cancel. 1269 * @return Boolean if <code>cancelThreshold</code> has been set, return true if the cancelThreshold was met, else false. 1270 * If no cancelThreshold was set, then we return true. 1271 * @inner 1272 */ 1273 function didSwipeBackToCancel() { 1274 var cancelled = false; 1275 if(options.cancelThreshold !== null && direction !==null) { 1276 cancelled = (getMaxDistance( direction ) - distance) >= options.cancelThreshold; 1277 } 1278 1279 return cancelled; 1280 } 1281 1282 /** 1283 * Checks the user has pinched far enough 1284 * @return Boolean if <code>pinchThreshold</code> has been set, return true if the threshold was met, else false. 1285 * If no threshold was set, then we return true. 1286 * @inner 1287 */ 1288 function validatePinchDistance() { 1289 if (options.pinchThreshold !== null) { 1290 return pinchDistance >= options.pinchThreshold; 1291 } 1292 return true; 1293 } 1294 1295 /** 1296 * Checks that the time taken to swipe meets the minimum / maximum requirements 1297 * @return Boolean 1298 * @inner 1299 */ 1300 function validateSwipeTime() { 1301 var result; 1302 //If no time set, then return true 1303 1304 if (options.maxTimeThreshold) { 1305 if (duration >= options.maxTimeThreshold) { 1306 result = false; 1307 } else { 1308 result = true; 1309 } 1310 } 1311 else { 1312 result = true; 1313 } 1314 1315 return result; 1316 } 1317 1318 1319 1320 /** 1321 * Checks direction of the swipe and the value allowPageScroll to see if we should allow or prevent the default behaviour from occurring. 1322 * This will essentially allow page scrolling or not when the user is swiping on a touchSwipe object. 1323 * @param {object} jqEvent The normalised jQuery representation of the event object. 1324 * @param {string} direction The direction of the event. See {@link $.fn.swipe.directions} 1325 * @see $.fn.swipe.directions 1326 * @inner 1327 */ 1328 function validateDefaultEvent(jqEvent, direction) { 1329 1330 //If we have no pinches, then do this 1331 //If we have a pinch, and we we have 2 fingers or more down, then dont allow page scroll. 1332 1333 //If the option is set, allways allow the event to bubble up (let user handle wiredness) 1334 if( options.preventDefaultEvents === false) { 1335 return; 1336 } 1337 1338 if (options.allowPageScroll === NONE) { 1339 jqEvent.preventDefault(); 1340 } else { 1341 var auto = options.allowPageScroll === AUTO; 1342 1343 switch (direction) { 1344 case LEFT: 1345 if ((options.swipeLeft && auto) || (!auto && options.allowPageScroll != HORIZONTAL)) { 1346 jqEvent.preventDefault(); 1347 } 1348 break; 1349 1350 case RIGHT: 1351 if ((options.swipeRight && auto) || (!auto && options.allowPageScroll != HORIZONTAL)) { 1352 jqEvent.preventDefault(); 1353 } 1354 break; 1355 1356 case UP: 1357 if ((options.swipeUp && auto) || (!auto && options.allowPageScroll != VERTICAL)) { 1358 jqEvent.preventDefault(); 1359 } 1360 break; 1361 1362 case DOWN: 1363 if ((options.swipeDown && auto) || (!auto && options.allowPageScroll != VERTICAL)) { 1364 jqEvent.preventDefault(); 1365 } 1366 break; 1367 } 1368 } 1369 1370 } 1371 1372 1373 // PINCHES 1374 /** 1375 * Returns true of the current pinch meets the thresholds 1376 * @return Boolean 1377 * @inner 1378 */ 1379 function validatePinch() { 1380 var hasCorrectFingerCount = validateFingers(); 1381 var hasEndPoint = validateEndPoint(); 1382 var hasCorrectDistance = validatePinchDistance(); 1383 return hasCorrectFingerCount && hasEndPoint && hasCorrectDistance; 1384 1385 } 1386 1387 /** 1388 * Returns true if any Pinch events have been registered 1389 * @return Boolean 1390 * @inner 1391 */ 1392 function hasPinches() { 1393 //Enure we dont return 0 or null for false values 1394 return !!(options.pinchStatus || options.pinchIn || options.pinchOut); 1395 } 1396 1397 /** 1398 * Returns true if we are detecting pinches, and have one 1399 * @return Boolean 1400 * @inner 1401 */ 1402 function didPinch() { 1403 //Enure we dont return 0 or null for false values 1404 return !!(validatePinch() && hasPinches()); 1405 } 1406 1407 1408 1409 1410 // SWIPES 1411 /** 1412 * Returns true if the current swipe meets the thresholds 1413 * @return Boolean 1414 * @inner 1415 */ 1416 function validateSwipe() { 1417 //Check validity of swipe 1418 var hasValidTime = validateSwipeTime(); 1419 var hasValidDistance = validateSwipeDistance(); 1420 var hasCorrectFingerCount = validateFingers(); 1421 var hasEndPoint = validateEndPoint(); 1422 var didCancel = didSwipeBackToCancel(); 1423 1424 // if the user swiped more than the minimum length, perform the appropriate action 1425 // hasValidDistance is null when no distance is set 1426 var valid = !didCancel && hasEndPoint && hasCorrectFingerCount && hasValidDistance && hasValidTime; 1427 1428 return valid; 1429 } 1430 1431 /** 1432 * Returns true if any Swipe events have been registered 1433 * @return Boolean 1434 * @inner 1435 */ 1436 function hasSwipes() { 1437 //Enure we dont return 0 or null for false values 1438 return !!(options.swipe || options.swipeStatus || options.swipeLeft || options.swipeRight || options.swipeUp || options.swipeDown); 1439 } 1440 1441 1442 /** 1443 * Returns true if we are detecting swipes and have one 1444 * @return Boolean 1445 * @inner 1446 */ 1447 function didSwipe() { 1448 //Enure we dont return 0 or null for false values 1449 return !!(validateSwipe() && hasSwipes()); 1450 } 1451 1452 /** 1453 * Returns true if we have matched the number of fingers we are looking for 1454 * @return Boolean 1455 * @inner 1456 */ 1457 function validateFingers() { 1458 //The number of fingers we want were matched, or on desktop we ignore 1459 return ((fingerCount === options.fingers || options.fingers === ALL_FINGERS) || !SUPPORTS_TOUCH); 1460 } 1461 1462 /** 1463 * Returns true if we have an end point for the swipe 1464 * @return Boolean 1465 * @inner 1466 */ 1467 function validateEndPoint() { 1468 //We have an end value for the finger 1469 return fingerData[0].end.x !== 0; 1470 } 1471 1472 // TAP / CLICK 1473 /** 1474 * Returns true if a click / tap events have been registered 1475 * @return Boolean 1476 * @inner 1477 */ 1478 function hasTap() { 1479 //Enure we dont return 0 or null for false values 1480 return !!(options.tap) ; 1481 } 1482 1483 /** 1484 * Returns true if a double tap events have been registered 1485 * @return Boolean 1486 * @inner 1487 */ 1488 function hasDoubleTap() { 1489 //Enure we dont return 0 or null for false values 1490 return !!(options.doubleTap) ; 1491 } 1492 1493 /** 1494 * Returns true if any long tap events have been registered 1495 * @return Boolean 1496 * @inner 1497 */ 1498 function hasLongTap() { 1499 //Enure we dont return 0 or null for false values 1500 return !!(options.longTap) ; 1501 } 1502 1503 /** 1504 * Returns true if we could be in the process of a double tap (one tap has occurred, we are listening for double taps, and the threshold hasn't past. 1505 * @return Boolean 1506 * @inner 1507 */ 1508 function validateDoubleTap() { 1509 if(doubleTapStartTime==null){ 1510 return false; 1511 } 1512 var now = getTimeStamp(); 1513 return (hasDoubleTap() && ((now-doubleTapStartTime) <= options.doubleTapThreshold)); 1514 } 1515 1516 /** 1517 * Returns true if we could be in the process of a double tap (one tap has occurred, we are listening for double taps, and the threshold hasn't past. 1518 * @return Boolean 1519 * @inner 1520 */ 1521 function inDoubleTap() { 1522 return validateDoubleTap(); 1523 } 1524 1525 1526 /** 1527 * Returns true if we have a valid tap 1528 * @return Boolean 1529 * @inner 1530 */ 1531 function validateTap() { 1532 return ((fingerCount === 1 || !SUPPORTS_TOUCH) && (isNaN(distance) || distance < options.threshold)); 1533 } 1534 1535 /** 1536 * Returns true if we have a valid long tap 1537 * @return Boolean 1538 * @inner 1539 */ 1540 function validateLongTap() { 1541 //slight threshold on moving finger 1542 return ((duration > options.longTapThreshold) && (distance < DOUBLE_TAP_THRESHOLD)); 1543 } 1544 1545 /** 1546 * Returns true if we are detecting taps and have one 1547 * @return Boolean 1548 * @inner 1549 */ 1550 function didTap() { 1551 //Enure we dont return 0 or null for false values 1552 return !!(validateTap() && hasTap()); 1553 } 1554 1555 1556 /** 1557 * Returns true if we are detecting double taps and have one 1558 * @return Boolean 1559 * @inner 1560 */ 1561 function didDoubleTap() { 1562 //Enure we dont return 0 or null for false values 1563 return !!(validateDoubleTap() && hasDoubleTap()); 1564 } 1565 1566 /** 1567 * Returns true if we are detecting long taps and have one 1568 * @return Boolean 1569 * @inner 1570 */ 1571 function didLongTap() { 1572 //Enure we dont return 0 or null for false values 1573 return !!(validateLongTap() && hasLongTap()); 1574 } 1575 1576 1577 1578 1579 // MULTI FINGER TOUCH 1580 /** 1581 * Starts tracking the time between 2 finger releases, and keeps track of how many fingers we initially had up 1582 * @inner 1583 */ 1584 function startMultiFingerRelease() { 1585 previousTouchEndTime = getTimeStamp(); 1586 fingerCountAtRelease = event.touches.length+1; 1587 } 1588 1589 /** 1590 * Cancels the tracking of time between 2 finger releases, and resets counters 1591 * @inner 1592 */ 1593 function cancelMultiFingerRelease() { 1594 previousTouchEndTime = 0; 1595 fingerCountAtRelease = 0; 1596 } 1597 1598 /** 1599 * Checks if we are in the threshold between 2 fingers being released 1600 * @return Boolean 1601 * @inner 1602 */ 1603 function inMultiFingerRelease() { 1604 1605 var withinThreshold = false; 1606 1607 if(previousTouchEndTime) { 1608 var diff = getTimeStamp() - previousTouchEndTime 1609 if( diff<=options.fingerReleaseThreshold ) { 1610 withinThreshold = true; 1611 } 1612 } 1613 1614 return withinThreshold; 1615 } 1616 1617 1618 /** 1619 * gets a data flag to indicate that a touch is in progress 1620 * @return Boolean 1621 * @inner 1622 */ 1623 function getTouchInProgress() { 1624 //strict equality to ensure only true and false are returned 1625 return !!($element.data(PLUGIN_NS+'_intouch') === true); 1626 } 1627 1628 /** 1629 * Sets a data flag to indicate that a touch is in progress 1630 * @param {boolean} val The value to set the property to 1631 * @inner 1632 */ 1633 function setTouchInProgress(val) { 1634 1635 //Add or remove event listeners depending on touch status 1636 if(val===true) { 1637 $element.bind(MOVE_EV, touchMove); 1638 $element.bind(END_EV, touchEnd); 1639 1640 //we only have leave events on desktop, we manually calcuate leave on touch as its not supported in webkit 1641 if(LEAVE_EV) { 1642 $element.bind(LEAVE_EV, touchLeave); 1643 } 1644 } else { 1645 1646 $element.unbind(MOVE_EV, touchMove, false); 1647 $element.unbind(END_EV, touchEnd, false); 1648 1649 //we only have leave events on desktop, we manually calcuate leave on touch as its not supported in webkit 1650 if(LEAVE_EV) { 1651 $element.unbind(LEAVE_EV, touchLeave, false); 1652 } 1653 } 1654 1655 1656 //strict equality to ensure only true and false can update the value 1657 $element.data(PLUGIN_NS+'_intouch', val === true); 1658 } 1659 1660 1661 /** 1662 * Creates the finger data for the touch/finger in the event object. 1663 * @param {int} id The id to store the finger data under (usually the order the fingers were pressed) 1664 * @param {object} evt The event object containing finger data 1665 * @return finger data object 1666 * @inner 1667 */ 1668 function createFingerData(id, evt) { 1669 var f = { 1670 start:{ x: 0, y: 0 }, 1671 end:{ x: 0, y: 0 } 1672 }; 1673 f.start.x = f.end.x = evt.pageX||evt.clientX; 1674 f.start.y = f.end.y = evt.pageY||evt.clientY; 1675 fingerData[id] = f; 1676 return f; 1677 } 1678 1679 /** 1680 * Updates the finger data for a particular event object 1681 * @param {object} evt The event object containing the touch/finger data to upadte 1682 * @return a finger data object. 1683 * @inner 1684 */ 1685 function updateFingerData(evt) { 1686 var id = evt.identifier!==undefined ? evt.identifier : 0; 1687 var f = getFingerData( id ); 1688 1689 if (f === null) { 1690 f = createFingerData(id, evt); 1691 } 1692 1693 f.end.x = evt.pageX||evt.clientX; 1694 f.end.y = evt.pageY||evt.clientY; 1695 1696 return f; 1697 } 1698 1699 /** 1700 * Returns a finger data object by its event ID. 1701 * Each touch event has an identifier property, which is used 1702 * to track repeat touches 1703 * @param {int} id The unique id of the finger in the sequence of touch events. 1704 * @return a finger data object. 1705 * @inner 1706 */ 1707 function getFingerData(id) { 1708 return fingerData[id] || null; 1709 } 1710 1711 1712 /** 1713 * Sets the maximum distance swiped in the given direction. 1714 * If the new value is lower than the current value, the max value is not changed. 1715 * @param {string} direction The direction of the swipe 1716 * @param {int} distance The distance of the swipe 1717 * @inner 1718 */ 1719 function setMaxDistance(direction, distance) { 1720 distance = Math.max(distance, getMaxDistance(direction) ); 1721 maximumsMap[direction].distance = distance; 1722 } 1723 1724 /** 1725 * gets the maximum distance swiped in the given direction. 1726 * @param {string} direction The direction of the swipe 1727 * @return int The distance of the swipe 1728 * @inner 1729 */ 1730 function getMaxDistance(direction) { 1731 if (maximumsMap[direction]) return maximumsMap[direction].distance; 1732 return undefined; 1733 } 1734 1735 /** 1736 * Creats a map of directions to maximum swiped values. 1737 * @return Object A dictionary of maximum values, indexed by direction. 1738 * @inner 1739 */ 1740 function createMaximumsData() { 1741 var maxData={}; 1742 maxData[LEFT]=createMaximumVO(LEFT); 1743 maxData[RIGHT]=createMaximumVO(RIGHT); 1744 maxData[UP]=createMaximumVO(UP); 1745 maxData[DOWN]=createMaximumVO(DOWN); 1746 1747 return maxData; 1748 } 1749 1750 /** 1751 * Creates a map maximum swiped values for a given swipe direction 1752 * @param {string} The direction that these values will be associated with 1753 * @return Object Maximum values 1754 * @inner 1755 */ 1756 function createMaximumVO(dir) { 1757 return { 1758 direction:dir, 1759 distance:0 1760 } 1761 } 1762 1763 1764 // 1765 // MATHS / UTILS 1766 // 1767 1768 /** 1769 * Calculate the duration of the swipe 1770 * @return int 1771 * @inner 1772 */ 1773 function calculateDuration() { 1774 return endTime - startTime; 1775 } 1776 1777 /** 1778 * Calculate the distance between 2 touches (pinch) 1779 * @param {point} startPoint A point object containing x and y co-ordinates 1780 * @param {point} endPoint A point object containing x and y co-ordinates 1781 * @return int; 1782 * @inner 1783 */ 1784 function calculateTouchesDistance(startPoint, endPoint) { 1785 var diffX = Math.abs(startPoint.x - endPoint.x); 1786 var diffY = Math.abs(startPoint.y - endPoint.y); 1787 1788 return Math.round(Math.sqrt(diffX*diffX+diffY*diffY)); 1789 } 1790 1791 /** 1792 * Calculate the zoom factor between the start and end distances 1793 * @param {int} startDistance Distance (between 2 fingers) the user started pinching at 1794 * @param {int} endDistance Distance (between 2 fingers) the user ended pinching at 1795 * @return float The zoom value from 0 to 1. 1796 * @inner 1797 */ 1798 function calculatePinchZoom(startDistance, endDistance) { 1799 var percent = (endDistance/startDistance) * 1; 1800 return percent.toFixed(2); 1801 } 1802 1803 1804 /** 1805 * Returns the pinch direction, either IN or OUT for the given points 1806 * @return string Either {@link $.fn.swipe.directions.IN} or {@link $.fn.swipe.directions.OUT} 1807 * @see $.fn.swipe.directions 1808 * @inner 1809 */ 1810 function calculatePinchDirection() { 1811 if(pinchZoom<1) { 1812 return OUT; 1813 } 1814 else { 1815 return IN; 1816 } 1817 } 1818 1819 1820 /** 1821 * Calculate the length / distance of the swipe 1822 * @param {point} startPoint A point object containing x and y co-ordinates 1823 * @param {point} endPoint A point object containing x and y co-ordinates 1824 * @return int 1825 * @inner 1826 */ 1827 function calculateDistance(startPoint, endPoint) { 1828 return Math.round(Math.sqrt(Math.pow(endPoint.x - startPoint.x, 2) + Math.pow(endPoint.y - startPoint.y, 2))); 1829 } 1830 1831 /** 1832 * Calculate the angle of the swipe 1833 * @param {point} startPoint A point object containing x and y co-ordinates 1834 * @param {point} endPoint A point object containing x and y co-ordinates 1835 * @return int 1836 * @inner 1837 */ 1838 function calculateAngle(startPoint, endPoint) { 1839 var x = startPoint.x - endPoint.x; 1840 var y = endPoint.y - startPoint.y; 1841 var r = Math.atan2(y, x); //radians 1842 var angle = Math.round(r * 180 / Math.PI); //degrees 1843 1844 //ensure value is positive 1845 if (angle < 0) { 1846 angle = 360 - Math.abs(angle); 1847 } 1848 1849 return angle; 1850 } 1851 1852 /** 1853 * Calculate the direction of the swipe 1854 * This will also call calculateAngle to get the latest angle of swipe 1855 * @param {point} startPoint A point object containing x and y co-ordinates 1856 * @param {point} endPoint A point object containing x and y co-ordinates 1857 * @return string Either {@link $.fn.swipe.directions.LEFT} / {@link $.fn.swipe.directions.RIGHT} / {@link $.fn.swipe.directions.DOWN} / {@link $.fn.swipe.directions.UP} 1858 * @see $.fn.swipe.directions 1859 * @inner 1860 */ 1861 function calculateDirection(startPoint, endPoint ) { 1862 var angle = calculateAngle(startPoint, endPoint); 1863 1864 if ((angle <= 45) && (angle >= 0)) { 1865 return LEFT; 1866 } else if ((angle <= 360) && (angle >= 315)) { 1867 return LEFT; 1868 } else if ((angle >= 135) && (angle <= 225)) { 1869 return RIGHT; 1870 } else if ((angle > 45) && (angle < 135)) { 1871 return DOWN; 1872 } else { 1873 return UP; 1874 } 1875 } 1876 1877 1878 /** 1879 * Returns a MS time stamp of the current time 1880 * @return int 1881 * @inner 1882 */ 1883 function getTimeStamp() { 1884 var now = new Date(); 1885 return now.getTime(); 1886 } 1887 1888 1889 1890 /** 1891 * Returns a bounds object with left, right, top and bottom properties for the element specified. 1892 * @param {DomNode} The DOM node to get the bounds for. 1893 */ 1894 function getbounds( el ) { 1895 el = $(el); 1896 var offset = el.offset(); 1897 1898 var bounds = { 1899 left:offset.left, 1900 right:offset.left+el.outerWidth(), 1901 top:offset.top, 1902 bottom:offset.top+el.outerHeight() 1903 } 1904 1905 return bounds; 1906 } 1907 1908 1909 /** 1910 * Checks if the point object is in the bounds object. 1911 * @param {object} point A point object. 1912 * @param {int} point.x The x value of the point. 1913 * @param {int} point.y The x value of the point. 1914 * @param {object} bounds The bounds object to test 1915 * @param {int}
1915 bounds.left The leftmost value 1916 * @param {int} bounds.right The righttmost value 1917 * @param {int} bounds.top The topmost value 1918 * @param {int} bounds.bottom The bottommost value 1919 */ 1920 function isInBounds(point, bounds) { 1921 return (point.x > bounds.left && point.x < bounds.right && point.y > bounds.top && point.y < bounds.bottom); 1922 }; 1923 1924 1925 } 1926 1927 1928 1929 1930/** 1931 * A catch all handler that is triggered for all swipe directions. 1932 * @name $.fn.swipe#swipe 1933 * @event 1934 * @default null 1935 * @param {EventObject} event The original event object 1936 * @param {int} direction The direction the user swiped in. See {@link $.fn.swipe.directions} 1937 * @param {int} distance The distance the user swiped 1938 * @param {int} duration The duration of the swipe in milliseconds 1939 * @param {int} fingerCount The number of fingers used. See {@link $.fn.swipe.fingers} 1940 * @param {object} fingerData The coordinates of fingers in event 1941 */ 1942 1943 1944 1945 1946/** 1947 * A handler that is triggered for "left" swipes. 1948 * @name $.fn.swipe#swipeLeft 1949 * @event 1950 * @default null 1951 * @param {EventObject} event The original event object 1952 * @param {int} direction The direction the user swiped in. See {@link $.fn.swipe.directions} 1953 * @param {int} distance The distance the user swiped 1954 * @param {int} duration The duration of the swipe in milliseconds 1955 * @param {int} fingerCount The number of fingers used. See {@link $.fn.swipe.fingers} 1956 * @param {object} fingerData The coordinates of fingers in event 1957 */ 1958 1959/** 1960 * A handler that is triggered for "right" swipes. 1961 * @name $.fn.swipe#swipeRight 1962 * @event 1963 * @default null 1964 * @param {EventObject} event The original event object 1965 * @param {int} direction The direction the user swiped in. See {@link $.fn.swipe.directions} 1966 * @param {int} distance The distance the user swiped 1967 * @param {int} duration The duration of the swipe in milliseconds 1968 * @param {int} fingerCount The number of fingers used. See {@link $.fn.swipe.fingers} 1969 * @param {object} fingerData The coordinates of fingers in event 1970 */ 1971 1972/** 1973 * A handler that is triggered for "up" swipes. 1974 * @name $.fn.swipe#swipeUp 1975 * @event 1976 * @default null 1977 * @param {EventObject} event The original event object 1978 * @param {int} direction The direction the user swiped in. See {@link $.fn.swipe.directions} 1979 * @param {int} distance The distance the user swiped 1980 * @param {int} duration The duration of the swipe in milliseconds 1981 * @param {int} fingerCount The number of fingers used. See {@link $.fn.swipe.fingers} 1982 * @param {object} fingerData The coordinates of fingers in event 1983 */ 1984 1985/** 1986 * A handler that is triggered for "down" swipes. 1987 * @name $.fn.swipe#swipeDown 1988 * @event 1989 * @default null 1990 * @param {EventObject} event The original event object 1991 * @param {int} direction The direction the user swiped in. See {@link $.fn.swipe.directions} 1992 * @param {int} distance The distance the user swiped 1993 * @param {int} duration The duration of the swipe in milliseconds 1994 * @param {int} fingerCount The number of fingers used. See {@link $.fn.swipe.fingers} 1995 * @param {object} fingerData The coordinates of fingers in event 1996 */ 1997 1998/** 1999 * A handler triggered for every phase of the swipe. This handler is constantly fired for the duration of the pinch. 2000 * This is triggered regardless of swipe thresholds. 2001 * @name $.fn.swipe#swipeStatus 2002 * @event 2003 * @default null 2004 * @param {EventObject} event The original event object 2005 * @param {string} phase The phase of the swipe event. See {@link $.fn.swipe.phases} 2006 * @param {string} direction The direction the user swiped in. This is null if the user has yet to move. See {@link $.fn.swipe.directions} 2007 * @param {int} distance The distance the user swiped. This is 0 if the user has yet to move. 2008 * @param {int} duration The duration of the swipe in milliseconds 2009 * @param {int} fingerCount The number of fingers used. See {@link $.fn.swipe.fingers} 2010 * @param {object} fingerData The coordinates of fingers in event 2011 */ 2012 2013/** 2014 * A handler triggered for pinch in events. 2015 * @name $.fn.swipe#pinchIn 2016 * @event 2017 * @default null 2018 * @param {EventObject} event The original event object 2019 * @param {int} direction The direction the user pinched in. See {@link $.fn.swipe.directions} 2020 * @param {int} distance The distance the user pinched 2021 * @param {int} duration The duration of the swipe in milliseconds 2022 * @param {int} fingerCount The number of fingers used. See {@link $.fn.swipe.fingers} 2023 * @param {int} zoom The zoom/scale level the user pinched too, 0-1. 2024 * @param {object} fingerData The coordinates of fingers in event 2025 */ 2026 2027/** 2028 * A handler triggered for pinch out events. 2029 * @name $.fn.swipe#pinchOut 2030 * @event 2031 * @default null 2032 * @param {EventObject} event The original event object 2033 * @param {int} direction The direction the user pinched in. See {@link $.fn.swipe.directions} 2034 * @param {int} distance The distance the user pinched 2035 * @param {int} duration The duration of the swipe in milliseconds 2036 * @param {int} fingerCount The number of fingers used. See {@link $.fn.swipe.fingers} 2037 * @param {int} zoom The zoom/scale level the user pinched too, 0-1. 2038 * @param {object} fingerData The coordinates of fingers in event 2039 */ 2040 2041/** 2042 * A handler triggered for all pinch events. This handler is constantly f
2042ired for the duration of the pinch. This is triggered regardless of thresholds. 2043 * @name $.fn.swipe#pinchStatus 2044 * @event 2045 * @default null 2046 * @param {EventObject} event The original event object 2047 * @param {int} direction The direction the user pinched in. See {@link $.fn.swipe.directions} 2048 * @param {int} distance The distance the user pinched 2049 * @param {int} duration The duration of the swipe in milliseconds 2050 * @param {int} fingerCount The number of fingers used. See {@link $.fn.swipe.fingers} 2051 * @param {int} zoom The zoom/scale level the user pinched too, 0-1. 2052 * @param {object} fingerData The coordinates of fingers in event 2053 */ 2054 2055/** 2056 * A click handler triggered when a user simply clicks, rather than swipes on an element. 2057 * This is deprecated since version 1.6.2, any assignment to click will be assigned to the tap handler. 2058 * You cannot use <code>on</code> to bind to this event as the default jQ <code>click</code> event will be triggered. 2059 * Use the <code>tap</code> event instead. 2060 * @name $.fn.swipe#click 2061 * @event 2062 * @deprecated since version 1.6.2, please use {@link $.fn.swipe#tap} instead 2063 * @default null 2064 * @param {EventObject} event The original event object 2065 * @param {DomObject} target The element clicked on. 2066 */ 2067 2068 /** 2069 * A click / tap handler triggered when a user simply clicks or taps, rather than swipes on an element. 2070 * @name $.fn.swipe#tap 2071 * @event 2072 * @default null 2073 * @param {EventObject} event The original event object 2074 * @param {DomObject} target The element clicked on. 2075 */ 2076 2077/** 2078 * A double tap handler triggered when a user double clicks or taps on an element. 2079 * You can set the time delay for a double tap with the {@link $.fn.swipe.defaults#doubleTapThreshold} property. 2080 * Note: If you set both <code>doubleTap</code> and <code>tap</code> handlers, the <code>tap</code> event will be delayed by the <code>doubleTapThreshold</code> 2081 * as the script needs to check if its a double tap. 2082 * @name $.fn.swipe#doubleTap 2083 * @see $.fn.swipe.defaults#doubleTapThreshold 2084 * @event 2085 * @default null 2086 * @param {EventObject} event The original event object 2087 * @param {DomObject} target The element clicked on. 2088 */ 2089 2090 /** 2091 * A long tap handler triggered once a tap has been release if the tap was longer than the longTapThreshold. 2092 * You can set the time delay for a long tap with the {@link $.fn.swipe.defaults#longTapThreshold} property. 2093 * @name $.fn.swipe#longTap 2094 * @see $.fn.swipe.defaults#longTapThreshold 2095 * @event 2096 * @default null 2097 * @param {EventObject} event The original event object 2098 * @param {DomObject} target The element clicked on. 2099 */ 2100 2101 /** 2102 * A hold tap handler triggered as soon as the longTapThreshold is reached 2103 * You can set the time delay for a long tap with the {@link $.fn.swipe.defaults#longTapThreshold} property. 2104 * @name $.fn.swipe#hold 2105 * @see $.fn.swipe.defaults#longTapThreshold 2106 * @event 2107 * @default null 2108 * @param {EventObject} event The original event object 2109 * @param {DomObject} target The element clicked on. 2110 */ 2111 2112})); 2113 2114 2115// THEMEPUNCH INTERNAL HANDLINGS 2116if(typeof(console) === 'undefined') { 2117 var console = {}; 2118 console.log = console.error = console.info = console.debug = console.warn = console.trace = console.dir = console.dirxml = console.group = console.groupEnd = console.time = console.timeEnd = console.assert = console.profile = console.groupCollapsed = function() {}; 2119} 2120 2121// THEMEPUNCH LOGS 2122if (window.tplogs==true) 2123 try { 2124 console.groupCollapsed("ThemePunch GreenSocks Logs"); 2125 } catch(e) { } 2126 2127// SANDBOX GREENSOCK 2128var oldgs = window.GreenSockGlobals; 2129 oldgs_queue = window._gsQueue; 2130 2131var punchgs = window.GreenSockGlobals = {}; 2132 2133if (window.tplogs==true) 2134 try { 2135 console.info("Build GreenSock SandBox for ThemePunch Plugins"); 2136 console.info("GreenSock TweenLite Engine Initalised by ThemePunch Plugin"); 2137 } catch(e) {} 2138 2139/*! 2140 * VERSION: 1.18.0 2141 * DATE: 2015-09-03 2142 * UPDATES AND DOCS AT: http://greensock.com 2143 * 2144 * @license Copyright (c) 2008-2015, GreenSock. All rights reserved. 2145 * This work is subject to the terms at http://greensock.com/standard-license or for
2146 * Club GreenSock members, the software agreement that was issued with your membership. 2147 * 2148 * @author: Jack Doyle, [email protected] 2149 */ 2150(function(window, moduleName) { 2151 2152 "use strict"; 2153 var _globals = window.GreenSockGlobals = window.GreenSockGlobals || window; 2154 if (_globals.TweenLite) { 2155 return; //in case the core set of classes is already loaded, don't instantiate twice. 2156 } 2157 var _namespace = function(ns) { 2158 var a = ns.split("."), 2159 p = _globals, i; 2160 for (i = 0; i < a.length; i++) { 2161 p[a[i]] = p = p[a[i]] || {}; 2162 } 2163 return p; 2164 }, 2165 gs = _namespace("com.greensock"), 2166 _tinyNum = 0.0000000001, 2167 _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() 2168 var b = [], 2169 l = a.length, 2170 i; 2171 for (i = 0; i !== l; b.push(a[i++])) {} 2172 return b; 2173 }, 2174 _emptyFunc = function() {}, 2175 _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) 2176 var toString = Object.prototype.toString, 2177 array = toString.call([]); 2178 return function(obj) { 2179 return obj != null && (obj instanceof Array || (typeof(obj) === "object" && !!obj.push && toString.call(obj) === array)); 2180 }; 2181 }()), 2182 a, i, p, _ticker, _tickerActive, 2183 _defLookup = {}, 2184 2185 /** 2186 * @constructor 2187 * Defines a GreenSock class, optionally with an array of dependencies that must be instantiated first and passed into the definition. 2188 * This allows users to load GreenSock JS files in any order even if they have interdependencies (like CSSPlugin extends TweenPlugin which is 2189 * inside TweenLite.js, but if CSSPlugin is loaded first, it should wait to run its code until TweenLite.js loads and instantiates TweenPlugin 2190 * and then pass TweenPlugin to CSSPlugin's definition). This is all done automatically and internally. 2191 * 2192 * Every definition will be added to a "com.greensock" global object (typically window, but if a window.GreenSockGlobals object is found, 2193 * 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, 2194 * it is ALSO referenced at window.TweenLite. However some classes aren't considered global, like the base com.greensock.core.Animation class, so 2195 * those will only be at the package like window.com.greensock.core.Animation. Again, if you define a GreenSockGlobals object on the window, everything 2196 * gets tucked neatly inside there instead of on the window directly. This allows you to do advanced things like load multiple versions of GreenSock 2197 * 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 2198 * sandbox the banner one like: 2199 * 2200 * <script> 2201 * 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. 2202 * </script> 2203 * <script src="js/greensock/v1.7/TweenMax.js"></script> 2204 * <script> 2205 * 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(...) 2206 * </script> 2207 * <script src="js/greensock/v1.6/TweenMax.js"></script> 2208 * <script> 2209 * gs.TweenLite.to(...); //would use v1.7 2210 * TweenLite.to(...); //would use v1.6 2211 * </script> 2212 * 2213 * @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". 2214 * @param {!Array.<string>}
2214 dependencies An array of dependencies (described as their namespaces minus "com.greensock." prefix). For example ["TweenLite","plugins.TweenPlugin","core.Animation"] 2215 * @param {!function():Object} func The function that should be called and passed the resolved dependencies which will return the actual class for this definition. 2216 * @param {boolean=} global If true, the class will be added to the global scope (typically window unless you define a window.GreenSockGlobals object) 2217 */ 2218 Definition = function(ns, dependencies, func, global) { 2219 this.sc = (_defLookup[ns]) ? _defLookup[ns].sc : []; //subclasses 2220 _defLookup[ns] = this; 2221 this.gsClass = null; 2222 this.func = func; 2223 var _classes = []; 2224 this.check = function(init) { 2225 var i = dependencies.length, 2226 missing = i, 2227 cur, a, n, cl, hasModule; 2228 while (--i > -1) { 2229 if ((cur = _defLookup[dependencies[i]] || new Definition(dependencies[i], [])).gsClass) { 2230 _classes[i] = cur.gsClass; 2231 missing--; 2232 } else if (init) { 2233 cur.sc.push(this); 2234 } 2235 } 2236 if (missing === 0 && func) { 2237 a = ("com.greensock." + ns).split("."); 2238 n = a.pop(); 2239 cl = _namespace(a.join("."))[n] = this.gsClass = func.apply(func, _classes); 2240 2241 //exports to multiple environments 2242 if (global) { 2243 _globals[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.) 2244 hasModule = (typeof(module) !== "undefined" && module.exports); 2245 if (!hasModule && typeof(define) === "function" && define.amd){ //AMD 2246 define((window.GreenSockAMDPath ? window.GreenSockAMDPath + "/" : "") + ns.split(".").pop(), [], function() { return cl; }); 2247 } else if (ns === moduleName && hasModule){ //node 2248 module.exports = cl; 2249 } 2250 } 2251 for (i = 0; i < this.sc.length; i++) { 2252 this.sc[i].check(); 2253 } 2254 } 2255 }; 2256 this.check(true); 2257 }, 2258 2259 //used to create Definition instances (which basically registers a class that has dependencies). 2260 _gsDefine = window._gsDefine = function(ns, dependencies, func, global) { 2261 return new Definition(ns, dependencies, func, global); 2262 }, 2263 2264 //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). 2265 _class = gs._class = function(ns, func, global) { 2266 func = func || function() {}; 2267 _gsDefine(ns, [], function(){ return func; }, global); 2268 return func; 2269 }; 2270 2271 _gsDefine.globals = _globals; 2272 2273 2274 2275/* 2276 * ---------------------------------------------------------------- 2277 * Ease 2278 * ---------------------------------------------------------------- 2279 */ 2280 var _baseParams = [0, 0, 1, 1], 2281 _blankArray = [], 2282 Ease = _class("easing.Ease", function(func, extraParams, type, power) { 2283 this._func = func; 2284 this._type = type || 0; 2285 this._power = power || 0; 2286 this._params = extraParams ? _baseParams.concat(extraParams) : _baseParams; 2287 }, true), 2288 _easeMap = Ease.map = {}, 2289 _easeReg = Ease.register = function(ease, names, types, create) { 2290 var na = names.split(","), 2291 i = na.length, 2292 ta = (types || "easeIn,easeOut,easeInOut").split(","), 2293 e, name, j, type; 2294 while (--i > -1) { 2295 name = na[i]; 2296 e = create ? _class("easing."+name, null, true) : gs.easing[name] || {}; 2297 j = ta.length; 2298 while (--j > -1) { 2299 type = ta[j]; 2300 _easeMap[name + "." + type] = _easeMap[type + name] = e[type] = ease.getRatio ? ease : ease[type] || new ease(); 2301 } 2302 } 2303 }; 2304 2305 p = Ease.prototype; 2306 p._calcEnd = false; 2307 p.getRatio = function(p) { 2308 if (this._func) { 2309 this._params[0] = p; 2310 return this._func.apply(null, this._params); 2311 } 2312 var t = this._type, 2313 pw = this._power,
2314 r = (t === 1) ? 1 - p : (t === 2) ? p : (p < 0.5) ? p * 2 : (1 - p) * 2; 2315 if (pw === 1) { 2316 r *= r; 2317 } else if (pw === 2) { 2318 r *= r * r; 2319 } else if (pw === 3) { 2320 r *= r * r * r; 2321 } else if (pw === 4) { 2322 r *= r * r * r * r; 2323 } 2324 return (t === 1) ? 1 - r : (t === 2) ? r : (p < 0.5) ? r / 2 : 1 - (r / 2); 2325 }; 2326 2327 //create all the standard eases like Linear, Quad, Cubic, Quart, Quint, Strong, Power0, Power1, Power2, Power3, and Power4 (each with easeIn, easeOut, and easeInOut) 2328 a = ["Linear","Quad","Cubic","Quart","Quint,Strong"]; 2329 i = a.length; 2330 while (--i > -1) { 2331 p = a[i]+",Power"+i; 2332 _easeReg(new Ease(null,null,1,i), p, "easeOut", true); 2333 _easeReg(new Ease(null,null,2,i), p, "easeIn" + ((i === 0) ? ",easeNone" : "")); 2334 _easeReg(new Ease(null,null,3,i), p, "easeInOut"); 2335 } 2336 _easeMap.linear = gs.easing.Linear.easeIn; 2337 _easeMap.swing = gs.easing.Quad.easeInOut; //for jQuery folks 2338 2339 2340/* 2341 * ---------------------------------------------------------------- 2342 * EventDispatcher 2343 * ---------------------------------------------------------------- 2344 */ 2345 var EventDispatcher = _class("events.EventDispatcher", function(target) { 2346 this._listeners = {}; 2347 this._eventTarget = target || this; 2348 }); 2349 p = EventDispatcher.prototype; 2350 2351 p.addEventListener = function(type, callback, scope, useParam, priority) { 2352 priority = priority || 0; 2353 var list = this._listeners[type], 2354 index = 0, 2355 listener, i; 2356 if (list == null) { 2357 this._listeners[type] = list = []; 2358 } 2359 i = list.length; 2360 while (--i > -1) { 2361 listener = list[i]; 2362 if (listener.c === callback && listener.s === scope) { 2363 list.splice(i, 1); 2364 } else if (index === 0 && listener.pr < priority) { 2365 index = i + 1; 2366 } 2367 } 2368 list.splice(index, 0, {c:callback, s:scope, up:useParam, pr:priority}); 2369 if (this === _ticker && !_tickerActive) { 2370 _ticker.wake(); 2371 } 2372 }; 2373 2374 p.removeEventListener = function(type, callback) { 2375 var list = this._listeners[type], i; 2376 if (list) { 2377 i = list.length; 2378 while (--i > -1) { 2379 if (list[i].c === callback) { 2380 list.splice(i, 1); 2381 return; 2382 } 2383 } 2384 } 2385 }; 2386 2387 p.dispatchEvent = function(type) { 2388 var list = this._listeners[type], 2389 i, t, listener; 2390 if (list) { 2391 i = list.length; 2392 t = this._eventTarget; 2393 while (--i > -1) { 2394 listener = list[i]; 2395 if (listener) { 2396 if (listener.up) { 2397 listener.c.call(listener.s || t, {type:type, target:t}); 2398 } else { 2399 listener.c.call(listener.s || t); 2400 } 2401 } 2402 } 2403 } 2404 }; 2405 2406 2407/* 2408 * ---------------------------------------------------------------- 2409 * Ticker 2410 * ---------------------------------------------------------------- 2411 */ 2412 var _reqAnimFrame = window.requestAnimationFrame, 2413 _cancelAnimFrame = window.cancelAnimationFrame, 2414 _getTime = Date.now || function() {return new Date().getTime();}, 2415 _lastUpdate = _getTime(); 2416 2417 //now try to determine the requestAnimationFrame and cancelAnimationFrame functions and if none are found, we'll use a setTimeout()/clearTimeout() polyfill. 2418 a = ["ms","moz","webkit","o"]; 2419 i = a.length; 2420 while (--i > -1 && !_reqAnimFrame) { 2421 _reqAnimFrame = window[a[i] + "RequestAnimationFrame"]; 2422 _cancelAnimFrame = window[a[i] + "CancelAnimationFrame"] || window[a[i] + "CancelRequestAnimationFrame"]; 2423 } 2424 2425 _class("Ticker", function(fps, useRAF) { 2426 var _self = this, 2427 _startTime = _getTime(), 2428 _useRAF = (useRAF !== false && _reqAnimFrame), 2429 _lagThreshold = 500, 2430 _adjustedLag = 33, 2431 _tickWord = "tick", //helps reduce gc burden 2432 _fps, _req, _id, _gap, _nextTime, 2433 _tick = function(manual) { 2434 var elapsed = _getTime() - _lastUpdate, 2435 overlap, dispatch; 2436 if (elapsed > _lagThreshold) { 2437 _startTime += elapsed - _adjustedLag; 2438 } 2439 _lastUpdate += elapsed; 2440 _self.time = (_lastUpdate - _startTime) / 1000; 2441 overlap = _self.time - _nextTime; 2442 if (!_fps || overlap > 0 || manual === true) { 2443 _self.frame++; 2444 _nextTime += overlap + (overlap >= _gap ? 0.004 : _gap - overlap); 2445 dispatch = true; 2446 } 2447 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. 2448 _id = _req(_tick); 2449 }
2450 if (dispatch) { 2451 _self.dispatchEvent(_tickWord); 2452 } 2453 }; 2454 2455 EventDispatcher.call(_self); 2456 _self.time = _self.frame = 0; 2457 _self.tick = function() { 2458 _tick(true); 2459 }; 2460 2461 _self.lagSmoothing = function(threshold, adjustedLag) { 2462 _lagThreshold = threshold || (1 / _tinyNum); //zero should be interpreted as basically unlimited 2463 _adjustedLag = Math.min(adjustedLag, _lagThreshold, 0); 2464 }; 2465 2466 _self.sleep = function() { 2467 if (_id == null) { 2468 return; 2469 } 2470 if (!_useRAF || !_cancelAnimFrame) { 2471 clearTimeout(_id); 2472 } else { 2473 _cancelAnimFrame(_id); 2474 } 2475 _req = _emptyFunc; 2476 _id = null; 2477 if (_self === _ticker) { 2478 _tickerActive = false; 2479 } 2480 }; 2481 2482 _self.wake = function() { 2483 if (_id !== null) { 2484 _self.sleep(); 2485 } 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(). 2486 _lastUpdate = _getTime() - _lagThreshold + 5; 2487 } 2488 _req = (_fps === 0) ? _emptyFunc : (!_useRAF || !_reqAnimFrame) ? function(f) { return setTimeout(f, ((_nextTime - _self.time) * 1000 + 1) | 0); } : _reqAnimFrame; 2489 if (_self === _ticker) { 2490 _tickerActive = true; 2491 } 2492 _tick(2); 2493 }; 2494 2495 _self.fps = function(value) { 2496 if (!arguments.length) { 2497 return _fps; 2498 } 2499 _fps = value; 2500 _gap = 1 / (_fps || 60); 2501 _nextTime = this.time + _gap; 2502 _self.wake(); 2503 }; 2504 2505 _self.useRAF = function(value) { 2506 if (!arguments.length) { 2507 return _useRAF; 2508 } 2509 _self.sleep(); 2510 _useRAF = value; 2511 _self.fps(_fps); 2512 }; 2513 _self.fps(fps); 2514 2515 //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. 2516 setTimeout(function() { 2517 if (_useRAF && _self.frame < 5) { 2518 _self.useRAF(false); 2519 } 2520 }, 1500); 2521 }); 2522 2523 p = gs.Ticker.prototype = new gs.events.EventDispatcher(); 2524 p.constructor = gs.Ticker; 2525 2526 2527/* 2528 * ---------------------------------------------------------------- 2529 * Animation 2530 * ---------------------------------------------------------------- 2531 */ 2532 var Animation = _class("core.Animation", function(duration, vars) { 2533 this.vars = vars = vars || {}; 2534 this._duration = this._totalDuration = duration || 0; 2535 this._delay = Number(vars.delay) || 0; 2536 this._timeScale = 1; 2537 this._active = (vars.immediateRender === true); 2538 this.data = vars.data; 2539 this._reversed = (vars.reversed === true); 2540 2541 if (!_rootTimeline) { 2542 return; 2543 } 2544 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. 2545 _ticker.wake(); 2546 } 2547 2548 var tl = this.vars.useFrames ? _rootFramesTimeline : _rootTimeline; 2549 tl.add(this, tl._time); 2550 2551 if (this.vars.paused) { 2552 this.paused(true); 2553 } 2554 }); 2555 2556 _ticker = Animation.ticker = new gs.Ticker(); 2557 p = Animation.prototype; 2558 p._dirty = p._gc = p._initted = p._paused = false; 2559 p._totalTime = p._time = 0; 2560 p._rawPrevTime = -1; 2561 p._next = p._last = p._onUpdate = p._timeline = p.timeline = null; 2562 p._paused = false; 2563 2564 2565 //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. 2566 var _checkTimeout = function() { 2567 if (_tickerActive && _getTime() - _lastUpdate > 2000) { 2568 _ticker.wake(); 2569 } 2570 setTimeout(_checkTimeout, 2000); 2571 }; 2572 _checkTimeout(); 2573 2574 2575 p.play = function(from, suppressEvents) { 2576 if (from != null) { 2577 this.seek(from, suppressEvents); 2578 } 2579 return this.reversed(false).paused(false); 2580 }; 2581 2582 p.pause = function(atTime, suppressEvents) { 2583 if (atTime != null) { 2584 this.seek(atTime, suppressEvents); 2585 } 2586 return this.paused(true); 2587 }; 2588 2589 p.resume = function(from, suppressEvents) { 2590 if (from != null) { 2591 this.seek(from, suppressEvents); 2592 } 2593 return this.paused(false); 2594 }; 2595 2596 p.seek = function(time, suppressEvents) { 2597 return this.totalTime(Number(time), suppressEvents !== false); 2598 }; 2599 2600 p.restart = function(includeDelay, suppressEvents) { 2601 return this.reversed(false).paused(false).totalTime(includeDelay ? -this._delay : 0, (suppressEvents !== false), true); 2602 }; 2603 2604 p.reverse = function(from, suppressEvents) { 2605 if (from != null) { 2606 this.seek((from || this.totalDuration()), suppressEvents); 2607 } 2608 return this.reversed(true).paused(false); 2609 }; 2610 2611 p.render = function(time, suppressEvents, force) { 2612 //stub - we override this method in subclasses. 2613 }; 2614 2615 p.invalidate = function() { 2616 this._time = this._totalTime = 0; 2617 this._initted = this._gc = false; 2618 this._rawPrevTime = -1; 2619 if (this._gc || !this.timeline) { 2620 this._enabled(true); 2621 } 2622 return this; 2623 }; 2624 2625 p.isActive = function() { 2626 var tl = this._timeline, //the 2 root timelines won't have a _timeline; they're always active. 2627 startTime = this._startTime, 2628 rawTime; 2629 return (!tl || (!this._gc && !this._paused && tl.isActive() && (rawTime = tl.rawTime()) >= startTime && rawTime < startTime + this.totalDuration() / this._timeScale)); 2630 }; 2631 2632 p._enabled = function (enabled, ignoreTimeline) { 2633 if (!_tickerActive) { 2634 _ticker.wake(); 2635 } 2636 this._gc = !enabled; 2637 this._active = this.isActive(); 2638 if (ignoreTimeline !== true) { 2639 if (enabled && !this.timeline) { 2640 this._timeline.add(this, this._startTime - this._delay); 2641 } else if (!enabled && this.timeline) { 2642 this._timeline._remove(this, true); 2643 } 2644 } 2645 return false; 2646 }; 2647 2648 2649 p._kill = function(vars, target) { 2650 return this._enabled(false, false); 2651 }; 2652 2653 p.kill = function(vars, target) { 2654 this._kill(vars, target); 2655 return this; 2656 }; 2657 2658 p._uncache = function(includeSelf) { 2659 var tween = includeSelf ? this : this.timeline; 2660 while (tween) { 2661 tween._dirty = true; 2662 tween = tween.timeline; 2663 } 2664 return this; 2665 }; 2666 2667 p._swapSelfInParams = function(params) { 2668 var i = params.length, 2669 copy = params.concat(); 2670 while (--i > -1) { 2671 if (params[i] === "{self}") { 2672 copy[i] = this; 2673 } 2674 } 2675 return copy; 2676 }; 2677 2678 p._callback = function(type) { 2679 var v = this.vars; 2680 v[type].apply(v[type + "Scope"] || v.callbackScope || this, v[type + "Params"] || _blankArray);
2681 }; 2682 2683//----Animation getters/setters -------------------------------------------------------- 2684 2685 p.eventCallback = function(type, callback, params, scope) { 2686 if ((type || "").substr(0,2) === "on") { 2687 var v = this.vars; 2688 if (arguments.length === 1) { 2689 return v[type]; 2690 } 2691 if (callback == null) { 2692 delete v[type]; 2693 } else { 2694 v[type] = callback; 2695 v[type + "Params"] = (_isArray(params) && params.join("").indexOf("{self}") !== -1) ? this._swapSelfInParams(params) : params; 2696 v[type + "Scope"] = scope; 2697 } 2698 if (type === "onUpdate") { 2699 this._onUpdate = callback; 2700 } 2701 } 2702 return this; 2703 }; 2704 2705 p.delay = function(value) { 2706 if (!arguments.length) { 2707 return this._delay; 2708 } 2709 if (this._timeline.smoothChildTiming) { 2710 this.startTime( this._startTime + value - this._delay ); 2711 } 2712 this._delay = value; 2713 return this; 2714 }; 2715 2716 p.duration = function(value) { 2717 if (!arguments.length) { 2718 this._dirty = false; 2719 return this._duration; 2720 } 2721 this._duration = this._totalDuration = value; 2722 this._uncache(true); //true in case it's a TweenMax or TimelineMax that has a repeat - we'll need to refresh the totalDuration. 2723 if (this._timeline.smoothChildTiming) if (this._time > 0) if (this._time < this._duration) if (value !== 0) { 2724 this.totalTime(this._totalTime * (value / this._duration), true); 2725 } 2726 return this; 2727 }; 2728 2729 p.totalDuration = function(value) { 2730 this._dirty = false; 2731 return (!arguments.length) ? this._totalDuration : this.duration(value); 2732 }; 2733 2734 p.time = function(value, suppressEvents) { 2735 if (!arguments.length) { 2736 return this._time; 2737 } 2738 if (this._dirty) { 2739 this.totalDuration(); 2740 } 2741 return this.totalTime((value > this._duration) ? this._duration : value, suppressEvents); 2742 }; 2743 2744 p.totalTime = function(time, suppressEvents, uncapped) { 2745 if (!_tickerActive) { 2746 _ticker.wake(); 2747 } 2748 if (!arguments.length) { 2749 return this._totalTime; 2750 } 2751 if (this._timeline) { 2752 if (time < 0 && !uncapped) { 2753 time += this.totalDuration(); 2754 } 2755 if (this._timeline.smoothChildTiming) { 2756 if (this._dirty) { 2757 this.totalDuration(); 2758 } 2759 var totalDuration = this._totalDuration, 2760 tl = this._timeline; 2761 if (time > totalDuration && !uncapped) { 2762 time = totalDuration; 2763 } 2764 this._startTime = (this._paused ? this._pauseTime : tl._time) - ((!this._reversed ? time : totalDuration - time) / this._timeScale); 2765 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. 2766 this._uncache(false); 2767 } 2768 //in case any of the ancestor timelines had completed but should now be enabled, we should reset their 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. 2769 if (tl._timeline) { 2770 while (tl._timeline) { 2771 if (tl._timeline._time !== (tl._startTime + tl._totalTime) / tl._timeScale) { 2772 tl.totalTime(tl._totalTime, true); 2773 } 2774 tl = tl._timeline; 2775 } 2776 } 2777 } 2778 if (this._gc) { 2779 this._enabled(true, false); 2780 } 2781 if (this._totalTime !== time || this._duration === 0) { 2782 if (_lazyTweens.length) { 2783 _lazyRender(); 2784 } 2785 this.render(time, suppressEvents, false); 2786 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. 2787 _lazyRender(); 2788 } 2789 } 2790 } 2791 return this; 2792 }; 2793 2794 p.progress = p.totalProgress = function(value, suppressEvents) { 2795 var duration = this.duration(); 2796 return (!arguments.length) ? (duration ? this._time / duration : this.ratio) : this.totalTime(duration * value, suppressEvents); 2797 }; 2798 2799 p.startTime = function(value) { 2800 if (!arguments.length) { 2801 return this._startTime; 2802 } 2803 if (value !== this._startTime) { 2804 this._startTime = value; 2805 if (this.timeline) if (this.timeline._sortChildren) { 2806 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. 2807 } 2808 } 2809 return this; 2810 }; 2811 2812 p.endTime = function(includeRepeats) { 2813 return this._startTime + ((includeRepeats != false) ? this.totalDuration() : this.duration()) / this._timeScale; 2814 }; 2815 2816 p.timeScale = function(value) { 2817 if (!arguments.length) { 2818 return this._timeScale; 2819 } 2820 value = value || _tinyNum; //can't allow zero because it'll throw the math off
2821 if (this._timeline && this._timeline.smoothChildTiming) { 2822 var pauseTime = this._pauseTime, 2823 t = (pauseTime || pauseTime === 0) ? pauseTime : this._timeline.totalTime(); 2824 this._startTime = t - ((t - this._startTime) * this._timeScale / value); 2825 } 2826 this._timeScale = value; 2827 return this._uncache(false); 2828 }; 2829 2830 p.reversed = function(value) { 2831 if (!arguments.length) { 2832 return this._reversed; 2833 } 2834 if (value != this._reversed) { 2835 this._reversed = value; 2836 this.totalTime(((this._timeline && !this._timeline.smoothChildTiming) ? this.totalDuration() - this._totalTime : this._totalTime), true); 2837 } 2838 return this; 2839 }; 2840 2841 p.paused = function(value) { 2842 if (!arguments.length) { 2843 return this._paused; 2844 } 2845 var tl = this._timeline, 2846 raw, elapsed; 2847 if (value != this._paused) if (tl) { 2848 if (!_tickerActive && !value) { 2849 _ticker.wake(); 2850 } 2851 raw = tl.rawTime(); 2852 elapsed = raw - this._pauseTime; 2853 if (!value && tl.smoothChildTiming) { 2854 this._startTime += elapsed; 2855 this._uncache(false); 2856 } 2857 this._pauseTime = value ? raw : null; 2858 this._paused = value; 2859 this._active = this.isActive(); 2860 if (!value && elapsed !== 0 && this._initted && this.duration()) { 2861 raw = tl.smoothChildTiming ? this._totalTime : (raw - this._startTime) / this._timeScale; 2862 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. 2863 } 2864 } 2865 if (this._gc && !value) { 2866 this._enabled(true, false); 2867 } 2868 return this; 2869 }; 2870 2871 2872/* 2873 * ---------------------------------------------------------------- 2874 * SimpleTimeline 2875 * ---------------------------------------------------------------- 2876 */ 2877 var SimpleTimeline = _class("core.SimpleTimeline", function(vars) { 2878 Animation.call(this, 0, vars); 2879 this.autoRemoveChildren = this.smoothChildTiming = true; 2880 }); 2881 2882 p = SimpleTimeline.prototype = new Animation(); 2883 p.constructor = SimpleTimeline; 2884 p.kill()._gc = false; 2885 p._first = p._last = p._recent = null; 2886 p._sortChildren = false; 2887 2888 p.add = p.insert = function(child, position, align, stagger) { 2889 var prevTween, st; 2890 child._startTime = Number(position || 0) + child._delay; 2891 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). 2892 child._pauseTime = child._startTime + ((this.rawTime() - child._startTime) / child._timeScale); 2893 } 2894 if (child.timeline) { 2895 child.timeline._remove(child, true); //removes from existing timeline so that it can be properly added to this one. 2896 } 2897 child.timeline = child._timeline = this; 2898 if (child._gc) { 2899 child._enabled(true, true); 2900 } 2901 prevTween = this._last; 2902 if (this._sortChildren) { 2903 st = child._startTime; 2904 while (prevTween && prevTween._startTime > st) { 2905 prevTween = prevTween._prev; 2906 } 2907 } 2908 if (prevTween) { 2909 child._next = prevTween._next; 2910 prevTween._next = child; 2911 } else { 2912 child._next = this._first; 2913 this._first = child; 2914 } 2915 if (child._next) { 2916 child._next._prev = child; 2917 } else { 2918 this._last = child; 2919 } 2920 child._prev = prevTween; 2921 this._recent = child; 2922 if (this._timeline) { 2923 this._uncache(true); 2924 } 2925 return this; 2926 }; 2927 2928 p._remove = function(tween, skipDisable) { 2929 if (tween.timeline === this) { 2930 if (!skipDisable) { 2931 tween._enabled(false, true); 2932 } 2933 2934 if (tween._prev) { 2935 tween._prev._next = tween._next; 2936 } else if (this._first === tween) { 2937 this._first = tween._next; 2938 } 2939 if (tween._next) { 2940 tween._next._prev = tween._prev; 2941 } else if (this._last === tween) { 2942 this._last = tween._prev; 2943 } 2944 tween._next = tween._prev = tween.timeline = null; 2945 if (tween === this._recent) { 2946 this._recent = this._last; 2947 } 2948 2949 if (this._timeline) { 2950 this._uncache(true); 2951 } 2952 } 2953 return this; 2954 }; 2955 2956 p.render = function(time, suppressEvents, force) { 2957 var tween = this._first, 2958 next; 2959 this._totalTime = this._time = this._rawPrevTime = time; 2960 while (tween) { 2961 next = tween._next; //record it here because the value could change after rendering... 2962 if (tween._active || (time >= tween._startTime && !tween._paused)) { 2963 if (!tween._reversed) { 2964 tween.render((time - tween._startTime) * tween._timeScale, suppressEvents, force); 2965 } else { 2966 tween.render(((!tween._dirty) ? tween._totalDuration : tween.totalDuration()) - ((time - tween._startTime) * tween._timeScale), suppressEvents, force); 2967 } 2968 } 2969 tween = next; 2970 } 2971 }; 2972 2973 p.rawTime = function() { 2974 if (!_tickerActive) { 2975 _ticker.wake(); 2976 } 2977 return this._totalTime; 2978 }; 2979 2980/* 2981 * ---------------------------------------------------------------- 2982 * TweenLite 2983 * ---------------------------------------------------------------- 2984 */ 2985 var TweenLite = _class("TweenLite", function(target, duration, vars) { 2986 Animation.call(this, duration, vars); 2987 this.render = TweenLite.prototype.render; //speed optimization (avoid prototype lookup on this "hot" method) 2988 2989 if (target == null) { 2990 throw "Cannot tween a null target."; 2991 } 2992 2993 this.target = target = (typeof(target) !== "string") ? target : TweenLite.selector(target) || target; 2994 2995 var isSelector = (target.jquery || (target.length && target !== window && target[0] && (target[0] === window || (target[0].nodeType && target[0].style && !target.nodeType)))),
2996 overwrite = this.vars.overwrite, 2997 i, targ, targets; 2998 2999 this._overwrite = overwrite = (overwrite == null) ? _overwriteLookup[TweenLite.defaultOverwrite] : (typeof(overwrite) === "number") ? overwrite >> 0 : _overwriteLookup[overwrite]; 3000 3001 if ((isSelector || target instanceof Array || (target.push && _isArray(target))) && typeof(target[0]) !== "number") { 3002 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() 3003 this._propLookup = []; 3004 this._siblings = []; 3005 for (i = 0; i < targets.length; i++) { 3006 targ = targets[i]; 3007 if (!targ) { 3008 targets.splice(i--, 1); 3009 continue; 3010 } else if (typeof(targ) === "string") { 3011 targ = targets[i--] = TweenLite.selector(targ); //in case it's an array of strings 3012 if (typeof(targ) === "string") { 3013 targets.splice(i+1, 1); //to avoid an endless loop (can't imagine why the selector would return a string, but just in case) 3014 } 3015 continue; 3016 } 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. 3017 targets.splice(i--, 1); 3018 this._targets = targets = targets.concat(_slice(targ)); 3019 continue; 3020 } 3021 this._siblings[i] = _register(targ, this, false); 3022 if (overwrite === 1) if (this._siblings[i].length > 1) { 3023 _applyOverwrite(targ, this, null, 1, this._siblings[i]); 3024 } 3025 } 3026 3027 } else { 3028 this._propLookup = {}; 3029 this._siblings = _register(target, this, false); 3030 if (overwrite === 1) if (this._siblings.length > 1) { 3031 _applyOverwrite(target, this, null, 1, this._siblings); 3032 } 3033 } 3034 if (this.vars.immediateRender || (duration === 0 && this._delay === 0 && this.vars.immediateRender !== false)) { 3035 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) 3036 this.render(-this._delay); 3037 } 3038 }, true), 3039 _isSelector = function(v) { 3040 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. 3041 }, 3042 _autoCSS = function(vars, target) { 3043 var css = {}, 3044 p; 3045 for (p in vars) { 3046 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. 3047 css[p] = vars[p]; 3048 delete vars[p]; 3049 } 3050 } 3051 vars.css = css; 3052 }; 3053 3054 p = TweenLite.prototype = new Animation(); 3055 p.constructor = TweenLite; 3056 p.kill()._gc = false; 3057 3058//----TweenLite defaults, overwrite management, and root updates ---------------------------------------------------- 3059 3060 p.ratio = 0; 3061 p._firstPT = p._targets = p._overwrittenProps = p._startAt = null; 3062 p._notifyPluginsOfEnabled = p._lazy = false; 3063 3064 TweenLite.version = "1.18.0"; 3065 TweenLite.defaultEase = p._ease = new Ease(null, null, 1, 1); 3066 TweenLite.defaultOverwrite = "auto"; 3067 TweenLite.ticker = _ticker; 3068 TweenLite.autoSleep = 120; 3069 TweenLite.lagSmoothing = function(threshold, adjustedLag) { 3070 _ticker.lagSmoothing(threshold, adjustedLag); 3071 }; 3072 3073 TweenLite.selector = window.$ || window.jQuery || function(e) { 3074 var selector = window.$ || window.jQuery; 3075 if (selector) { 3076 TweenLite.selector = selector; 3077 return selector(e); 3078 } 3079 return (typeof(document) === "undefined") ? e : (document.querySelectorAll ? document.querySelectorAll(e) : document.getElementById((e.charAt(0) === "#") ? e.substr(1) : e)); 3080 }; 3081 3082 var _lazyTweens = [], 3083 _lazyLookup = {}, 3084 _numbersExp = /(?:(-|-=|\+=)?\d*\.?\d*(?:e[\-+]?\d+)?)[0-9]/ig, 3085 //_nonNumbersExp = /(?:([\-+](?!(\d|=)))|[^\d\-+=e]|(e(?![\-+][\d])))+/ig, 3086 _setRatio = function(v) { 3087 var pt = this._firstPT, 3088 min = 0.000001, 3089 val; 3090 while (pt) { 3091 val = !pt.blob ? pt.c * v + pt.s : v ? this.join("") : this.start; 3092 if (pt.r) { 3093 val = Math.round(val); 3094 } else if (val < min) if (val >
3094 -min) { //prevents issues with converting very small numbers to strings in the browser 3095 val = 0; 3096 } 3097 if (!pt.f) { 3098 pt.t[pt.p] = val; 3099 } else if (pt.fp) { 3100 pt.t[pt.p](pt.fp, val); 3101 } else { 3102 pt.t[pt.p](val); 3103 } 3104 pt = pt._next; 3105 } 3106 }, 3107 //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("")). 3108 _blobDif = function(start, end, filter, pt) { 3109 var a = [start, end], 3110 charIndex = 0, 3111 s = "", 3112 color = 0, 3113 startNums, endNums, num, i, l, nonNumbers, currentNum; 3114 a.start = start; 3115 if (filter) { 3116 filter(a); //pass an array with the starting and ending values and let the filter do whatever it needs to the values. 3117 start = a[0]; 3118 end = a[1]; 3119 } 3120 a.length = 0; 3121 startNums = start.match(_numbersExp) || []; 3122 endNums = end.match(_numbersExp) || []; 3123 if (pt) { 3124 pt._next = null; 3125 pt.blob = 1; 3126 a._firstPT = pt; //apply last in the linked list (which means inserting it first) 3127 } 3128 l = endNums.length; 3129 for (i = 0; i < l; i++) { 3130 currentNum = endNums[i]; 3131 nonNumbers = end.substr(charIndex, end.indexOf(currentNum, charIndex)-charIndex); 3132 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. 3133 charIndex += nonNumbers.length; 3134 if (color) { //sense rgba() values and round them. 3135 color = (color + 1) % 5; 3136 } else if (nonNumbers.substr(-5) === "rgba(") { 3137 color = 1; 3138 } 3139 if (currentNum === startNums[i] || startNums.length <= i) { 3140 s += currentNum; 3141 } else { 3142 if (s) { 3143 a.push(s); 3144 s = ""; 3145 } 3146 num = parseFloat(startNums[i]); 3147 a.push(num); 3148 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, r:(color && color < 4)}; 3149 //note: we don't set _prev because we'll never need to remove individual PropTweens from this list. 3150 } 3151 charIndex += currentNum.length; 3152 } 3153 s += end.substr(charIndex); 3154 if (s) { 3155 a.push(s); 3156 } 3157 a.setRatio = _setRatio; 3158 return a; 3159 }, 3160 //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. 3161 _addPropTween = function(target, prop, start, end, overwriteProp, round, funcParam, stringFilter) { 3162 var s = (start === "get") ? target[prop] : start, 3163 type = typeof(target[prop]), 3164 isRelative = (typeof(end) === "string" && end.charAt(1) === "="), 3165 pt = {t:target, p:prop, s:s, f:(type === "function"), pg:0, n:overwriteProp || prop, r:round, pr:0, c:isRelative ? parseInt(end.charAt(0) + "1", 10) * parseFloat(end.substr(2)) : (parseFloat(end) - s) || 0}, 3166 blob, getterName; 3167 if (type !== "number") { 3168 if (type === "function" && start === "get") { 3169 getterName = ((prop.indexOf("set") || typeof(target["get" + prop.substr(3)]) !== "function") ? prop : "get" + prop.substr(3)); 3170 pt.s = s = funcParam ? target[getterName](funcParam) : target[getterName](); 3171 } 3172 if (typeof(s) === "string" && (funcParam || isNaN(s))) { 3173 //a blob (string that has multiple numbers in it) 3174 pt.fp = funcParam; 3175 blob = _blobDif(s, end, stringFilter || TweenLite.defaultStringFilter, pt); 3176 pt = {t:blob, p:"setRatio", s:0, c:1, f:2, pg:0, n:overwriteProp || prop, pr:0}; //"2" indicates it's a Blob property tween. Needed for RoundPropsPlugin for example. 3177 } else if (!isRelative) { 3178 pt.c = (parseFloat(end) - parseFloat(s)) || 0; 3179 } 3180 } 3181 if (pt.c) { //only add it to the linked list if there's a change. 3182 if ((pt._next = this._firstPT)) { 3183 pt._next._prev = pt; 3184 } 3185 this._firstPT = pt; 3186 return pt; 3187 } 3188 }, 3189 _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. 3190 _plugins = TweenLite._plugins = {}, 3191 _tweenLookup = _internals.tweenLookup = {}, 3192 _tweenLookupNum = 0, 3193 _reservedProps = _internals.reservedProps = {ease:1, delay:1, overwrite:1, onComplete:1, onCompleteParams:1, onCompleteScope:1, useFrames:1, runBackwards:1, startAt:1, onUp
3193date: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}, 3194 _overwriteLookup = {none:0, all:1, auto:2, concurrent:3, allOnStart:4, preexisting:5, "true":1, "false":0}, 3195 _rootFramesTimeline = Animation._rootFramesTimeline = new SimpleTimeline(), 3196 _rootTimeline = Animation._rootTimeline = new SimpleTimeline(), 3197 _nextGCFrame = 30, 3198 _lazyRender = _internals.lazyRender = function() { 3199 var i = _lazyTweens.length, 3200 tween; 3201 _lazyLookup = {}; 3202 while (--i > -1) { 3203 tween = _lazyTweens[i]; 3204 if (tween && tween._lazy !== false) { 3205 tween.render(tween._lazy[0], tween._lazy[1], true); 3206 tween._lazy = false; 3207 } 3208 } 3209 _lazyTweens.length = 0; 3210 }; 3211 3212 _rootTimeline._startTime = _ticker.time; 3213 _rootFramesTimeline._startTime = _ticker.frame; 3214 _rootTimeline._active = _rootFramesTimeline._active = true; 3215 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". 3216 3217 Animation._updateRoot = TweenLite.render = function() { 3218 var i, a, p; 3219 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. 3220 _lazyRender(); 3221 } 3222 _rootTimeline.render((_ticker.time - _rootTimeline._startTime) * _rootTimeline._timeScale, false, false); 3223 _rootFramesTimeline.render((_ticker.frame - _rootFramesTimeline._startTime) * _rootFramesTimeline._timeScale, false, false); 3224 if (_lazyTweens.length) { 3225 _lazyRender(); 3226 } 3227 if (_ticker.frame >= _nextGCFrame) { //dump garbage every 120 frames or whatever the user sets TweenLite.autoSleep to 3228 _nextGCFrame = _ticker.frame + (parseInt(TweenLite.autoSleep, 10) || 120); 3229 for (p in _tweenLookup) { 3230 a = _tweenLookup[p].tweens; 3231 i = a.length; 3232 while (--i > -1) { 3233 if (a[i]._gc) { 3234 a.splice(i, 1); 3235 } 3236 } 3237 if (a.length === 0) { 3238 delete _tweenLookup[p]; 3239 } 3240 } 3241 //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 3242 p = _rootTimeline._first; 3243 if (!p || p._paused) if (TweenLite.autoSleep && !_rootFramesTimeline._first && _ticker._listeners.tick.length === 1) { 3244 while (p && p._paused) { 3245 p = p._next; 3246 } 3247 if (!p) { 3248 _ticker.sleep(); 3249 } 3250 } 3251 } 3252 }; 3253 3254 _ticker.addEventListener("tick", Animation._updateRoot); 3255 3256 var _register = function(target, tween, scrub) { 3257 var id = target._gsTweenID, a, i; 3258 if (!_tweenLookup[id || (target._gsTweenID = id = "t" + (_tweenLookupNum++))]) { 3259 _tweenLookup[id] = {target:target, tweens:[]}; 3260 } 3261 if (tween) { 3262 a = _tweenLookup[id].tweens; 3263 a[(i = a.length)] = tween; 3264 if (scrub) { 3265 while (--i > -1) { 3266 if (a[i] === tween) { 3267 a.splice(i, 1); 3268 } 3269 } 3270 } 3271 } 3272 return _tweenLookup[id].tweens; 3273 }, 3274 _onOverwrite = function(overwrittenTween, overwritingTween, target, killedProps) { 3275 var func = overwrittenTween.vars.onOverwrite, r1, r2; 3276 if (func) { 3277 r1 = func(overwrittenTween, overwritingTween, target, killedProps); 3278 } 3279 func = TweenLite.onOverwrite; 3280 if (func) { 3281 r2 = func(overwrittenTween, overwritingTween, target, killedProps); 3282 } 3283 return (r1 !== false && r2 !== false); 3284 }, 3285 _applyOverwrite = function(target, tween, props, mode, siblings) { 3286 var i, changed, curTween, l; 3287 if (mode === 1 || mode >= 4) { 3288 l = siblings.length; 3289 for (i = 0; i < l; i++) { 3290 if ((curTween = siblings[i]) !== tween) { 3291 if (!curTween._gc) { 3292 if (curTween._kill(null, target, tween)) { 3293 changed = true; 3294 } 3295 } 3296 } else if (mode === 5) { 3297 break; 3298 } 3299 } 3300 return changed; 3301 } 3302 //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) 3303 var startTime = tween._startTime + _tinyNum, 3304 overlaps = [], 3305 oCount = 0, 3306 zeroDur = (tween._duration === 0), 3307 globalStart; 3308 i = siblings.length; 3309 while (--i > -1) { 3310 if ((curTween = siblings[i]) === tween || curTween._gc || curTween._paused) { 3311 //ignore 3312 } else if (curTween._timeline !== tween._timeline) { 3313 globalStart = globalStart || _checkOverlap(tween, 0, zeroDur); 3314 if (_checkOverlap(curTween, globalStart, zeroDur) === 0) { 3315 overlaps[oCount++] = curTween; 3316 } 3317 } else if (curTween._startTime <= startTime) if (curTween._startTime + curTween.totalDuration() / curTween._timeScale > startTime) if (!((zeroDur || !curTween._initted) && startTime - curTween._startTime <= 0.0000000002)) { 3318 overlaps[oCount++] = curTween; 3319 } 3320 } 3321 3322 i = oCount; 3323 while (--i > -1) { 3324 curTween = overlaps[i]; 3325 if (mode === 2) if (curTween._kill(props, target, tween)) { 3326 changed = true; 3327 } 3328 if (mode !== 2 || (!curTween._firstPT && curTween._initted)) { 3329 if (mode !== 2 && !_onOverwrite(curTween, tween)) { 3330 continue; 3331 } 3332 if (curTween._enabled(false, false)) { //if all property tweens have been overwritten, kill the tween. 3333 changed = true; 3334 } 3335 } 3336 } 3337 return changed; 3338 }, 3339 _checkOverlap = function(tween, reference, zeroDur) { 3340 var tl = tween._timeline, 3341 ts = tl._timeScale, 3342 t = tween._startTime; 3343 while (tl._timeline) { 3344 t += tl._startTime; 3345 ts *= tl._timeScale; 3346 if (tl._paused) { 3347 return -100; 3348 } 3349 tl = tl._timeline; 3350 } 3351 t /= ts; 3352 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; 3353 }; 3354 3355 3356//---- TweenLite instance methods ----------------------------------------------------------------------------- 3357 3358 p._init = function() { 3359 var v = this.vars, 3360 op = this._overwrittenProps, 3361 dur = this._duration, 3362 immediate = !!v.immediateRender, 3363 ease = v.ease, 3364 i, initPlugins, pt, p, startVars; 3365 if (v.startAt) { 3366 if (this._startAt) { 3367 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. 3368 this._startAt.kill(); 3369 } 3370 startVars = {}; 3371 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); 3372 startVars[p] = v.startAt[p]; 3373 } 3374 startVars.overwrite = false; 3375 startVars.immediateRender = true; 3376 startVars.lazy = (immediate && v.lazy !== false);
3377 startVars.startAt = startVars.delay = null; //no nesting of startAt objects allowed (otherwise it could cause an infinite loop). 3378 this._startAt = TweenLite.to(this.target, 0, startVars); 3379 if (immediate) { 3380 if (this._time > 0) { 3381 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()). 3382 } else if (dur !== 0) { 3383 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. 3384 } 3385 } 3386 } else if (v.runBackwards && dur !== 0) { 3387 //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) 3388 if (this._startAt) { 3389 this._startAt.render(-1, true); 3390 this._startAt.kill(); 3391 this._startAt = null; 3392 } else { 3393 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 3394 immediate = false; 3395 } 3396 pt = {}; 3397 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. 3398 if (!_reservedProps[p] || p === "autoCSS") { 3399 pt[p] = v[p]; 3400 } 3401 } 3402 pt.overwrite = 0; 3403 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. 3404 pt.lazy = (immediate && v.lazy !== false); 3405 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) 3406 this._startAt = TweenLite.to(this.target, 0, pt); 3407 if (!immediate) { 3408 this._startAt._init(); //ensures that the initial values are recorded 3409 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. 3410 if (this.vars.immediateRender) { 3411 this._startAt = null; 3412 } 3413 } else if (this._time === 0) { 3414 return; 3415 } 3416 } 3417 } 3418 this._ease = ease = (!ease) ? TweenLite.defaultEase : (ease instanceof Ease) ? ease : (typeof(ease) === "function") ? new Ease(ease, v.easeParams) : _easeMap[ease] || TweenLite.defaultEase; 3419 if (v.easeParams instanceof Array && ease.config) { 3420 this._ease = ease.config.apply(ease, v.easeParams); 3421 } 3422 this._easeType = this._ease._type; 3423 this._easePower = this._ease._power; 3424 this._firstPT = null; 3425 3426 if (this._targets) { 3427 i = this._targets.length; 3428 while (--i > -1) { 3429 if ( this._initProps( this._targets[i], (this._propLookup[i] = {}), this._siblings[i], (op ? op[i] : null)) ) { 3430 initPlugins = true; 3431 } 3432 } 3433 } else { 3434 initPlugins = this._initProps(this.target, this._propLookup, this._siblings, op); 3435 } 3436 3437 if (initPlugins) { 3438 TweenLite._onPluginEvent("_onInitAllProps", this); //reorders the array in order of priority. Uses a static TweenPlugin method in order to minimize file size in TweenLite 3439 } 3440 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. 3441 this._enabled(false, false); 3442 } 3443 if (v.runBackwards) { 3444 pt = this._firstPT; 3445 while (pt) { 3446 pt.s += pt.c; 3447 pt.c = -pt.c; 3448 pt = pt._next; 3449 } 3450 } 3451 this._onUpdate = v.onUpdate; 3452 this._initted = true; 3453 }; 3454 3455 p._initProps = function(target, propLookup, siblings, overwrittenProps) { 3456 var p, i, initPlugins, plugin, pt, v; 3457 if (target == null) { 3458 return false; 3459 } 3460 3461 if (_lazyLookup[target._gsTweenID]) { 3462 _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) 3463 } 3464 3465 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. 3466 _autoCSS(this.vars, target); 3467 } 3468 for (p in this.vars) { 3469 v = this.vars[p]; 3470 if (_reservedProps[p]) { 3471 if (v) if ((v instanceof Array) || (v.push && _isArray(v))) if (v.join("").indexOf("{self}") !== -1) { 3472 this.vars[p] = v = this._swapSelfInParams(v, this); 3473 } 3474 3475 } else if (_plugins[p] && (plugin = new _plugins[p]())._onInitTween(target, this.vars[p], this)) { 3476 3477 //t - target [object] 3478 //p - property [string] 3479 //s - start [number] 3480 //c - change [number] 3481 //f - isFunction [boolean] 3482 //n - name [string] 3483 //pg - isPlugin [boolean] 3484 //pr - priority [number] 3485 this._firstPT = pt = {_next:this._firstPT, t:plugin, p:"setRatio", s:0, c:1, f:1, n:p, pg:1, pr:plugin._priority};
3486 i = plugin._overwriteProps.length; 3487 while (--i > -1) { 3488 propLookup[plugin._overwriteProps[i]] = this._firstPT; 3489 } 3490 if (plugin._priority || plugin._onInitAllProps) { 3491 initPlugins = true; 3492 } 3493 if (plugin._onDisable || plugin._onEnable) { 3494 this._notifyPluginsOfEnabled = true; 3495 } 3496 if (pt._next) { 3497 pt._next._prev = pt; 3498 } 3499 3500 } else { 3501 propLookup[p] = _addPropTween.call(this, target, p, "get", v, p, 0, null, this.vars.stringFilter); 3502 } 3503 } 3504 3505 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) 3506 return this._initProps(target, propLookup, siblings, overwrittenProps); 3507 } 3508 if (this._overwrite > 1) if (this._firstPT) if (siblings.length > 1) if (_applyOverwrite(target, this, propLookup, this._overwrite, siblings)) { 3509 this._kill(propLookup, target); 3510 return this._initProps(target, propLookup, siblings, overwrittenProps); 3511 } 3512 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. 3513 _lazyLookup[target._gsTweenID] = true; 3514 } 3515 return initPlugins; 3516 }; 3517 3518 p.render = function(time, suppressEvents, force) { 3519 var prevTime = this._time, 3520 duration = this._duration, 3521 prevRawPrevTime = this._rawPrevTime, 3522 isComplete, callback, pt, rawPrevTime; 3523 if (time >= duration) { 3524 this._totalTime = this._time = duration; 3525 this.ratio = this._ease._calcEnd ? this._ease.getRatio(1) : 1; 3526 if (!this._reversed ) { 3527 isComplete = true; 3528 callback = "onComplete"; 3529 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. 3530 } 3531 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. 3532 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. 3533 time = 0; 3534 } 3535 if (time === 0 || prevRawPrevTime < 0 || (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. 3536 force = true; 3537 if (prevRawPrevTime > _tinyNum) { 3538 callback = "onReverseComplete"; 3539 } 3540 } 3541 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. 3542 } 3543 3544 } else if (time < 0.0000001) { //to work around occasional floating point math artifacts, round super small values to 0. 3545 this._totalTime = this._time = 0; 3546 this.ratio = this._ease._calcEnd ? this._ease.getRatio(0) : 0; 3547 if (prevTime !== 0 || (duration === 0 && prevRawPrevTime > 0)) { 3548 callback = "onReverseComplete"; 3549 isComplete = this._reversed; 3550 } 3551 if (time < 0) { 3552 this._active = false; 3553 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. 3554 if (prevRawPrevTime >= 0 && !(prevRawPrevTime === _tinyNum && this.data === "isPause")) { 3555 force = true; 3556 } 3557 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. 3558 } 3559 } 3560 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. 3561 force = true; 3562 } 3563 } else { 3564 this._totalTime = this._time = time; 3565 3566 if (this._easeType) { 3567 var r = time / duration, type = this._easeType, pow = this._easePower; 3568 if (type === 1 || (type === 3 && r >= 0.5)) { 3569 r = 1 - r; 3570 } 3571 if (type === 3) { 3572 r *= 2; 3573 } 3574 if (pow === 1) { 3575 r *= r; 3576 } else if (pow === 2) { 3577 r *= r * r; 3578 } else if (pow === 3) { 3579 r *= r * r * r; 3580 } else if (pow === 4) { 3581 r *= r * r * r * r; 3582 } 3583 3584 if (type === 1) { 3585 this.ratio = 1 - r; 3586 } else if (type === 2) { 3587 this.ratio = r; 3588 } else if (time / duration < 0.5) { 3589 this.ratio = r / 2; 3590 } else { 3591 this.ratio = 1 - (r / 2); 3592 } 3593 3594 } else { 3595 this.ratio = this._ease.getRatio(time / duration); 3596 } 3597 } 3598 3599 if (this._time === prevTime && !force) { 3600 return; 3601 } else if (!this._initted) { 3602 this._init(); 3603 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
3603writing 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. 3604 return; 3605 } else if (!force && this._firstPT && ((this.vars.lazy !== false && this._duration) || (this.vars.lazy && !this._duration))) { 3606 this._time = this._totalTime = prevTime; 3607 this._rawPrevTime = prevRawPrevTime; 3608 _lazyTweens.push(this); 3609 this._lazy = [time, suppressEvents]; 3610 return; 3611 } 3612 //_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. 3613 if (this._time && !isComplete) { 3614 this.ratio = this._ease.getRatio(this._time / duration); 3615 } else if (isComplete && this._ease._calcEnd) { 3616 this.ratio = this._ease.getRatio((this._time === 0) ? 0 : 1); 3617 } 3618 } 3619 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. 3620 this._lazy = false; 3621 } 3622 if (!this._active) if (!this._paused && this._time !== prevTime && time >= 0) { 3623 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. 3624 } 3625 if (prevTime === 0) { 3626 if (this._startAt) { 3627 if (time >= 0) { 3628 this._startAt.render(time, suppressEvents, force); 3629 } else if (!callback) { 3630 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. 3631 } 3632 } 3633 if (this.vars.onStart) if (this._time !== 0 || duration === 0) if (!suppressEvents) { 3634 this._callback("onStart"); 3635 } 3636 } 3637 pt = this._firstPT; 3638 while (pt) { 3639 if (pt.f) { 3640 pt.t[pt.p](pt.c * this.ratio + pt.s); 3641 } else { 3642 pt.t[pt.p] = pt.c * this.ratio + pt.s; 3643 } 3644 pt = pt._next; 3645 } 3646 3647 if (this._onUpdate) { 3648 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. 3649 this._startAt.render(time, suppressEvents, 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. 3650 } 3651 if (!suppressEvents) if (this._time !== prevTime || isComplete) { 3652 this._callback("onUpdate"); 3653 } 3654 } 3655 if (callback) if (!this._gc || force) { //check _gc because there's a chance that kill() could be called in an onUpdate 3656 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. 3657 this._startAt.render(time, suppressEvents, force); 3658 } 3659 if (isComplete) { 3660 if (this._timeline.autoRemoveChildren) { 3661 this._enabled(false, false); 3662 } 3663 this._active = false; 3664 } 3665 if (!suppressEvents && this.vars[callback]) { 3666 this._callback(callback); 3667 } 3668 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
3668nario, but possible nonetheless. 3669 this._rawPrevTime = 0; 3670 } 3671 } 3672 }; 3673 3674 p._kill = function(vars, target, overwritingTween) { 3675 if (vars === "all") { 3676 vars = null; 3677 } 3678 if (vars == null) if (target == null || target === this.target) { 3679 this._lazy = false; 3680 return this._enabled(false, false); 3681 } 3682 target = (typeof(target) !== "string") ? (target || this._targets || this.target) : TweenLite.selector(target) || target; 3683 var simultaneousOverwrite = (overwritingTween && this._time && overwritingTween._startTime === this._startTime && this._timeline === overwritingTween._timeline), 3684 i, overwrittenProps, p, pt, propLookup, changed, killProps, record, killed; 3685 if ((_isArray(target) || _isSelector(target)) && typeof(target[0]) !== "number") { 3686 i = target.length; 3687 while (--i > -1) { 3688 if (this._kill(vars, target[i], overwritingTween)) { 3689 changed = true; 3690 } 3691 } 3692 } else { 3693 if (this._targets) { 3694 i = this._targets.length; 3695 while (--i > -1) { 3696 if (target === this._targets[i]) { 3697 propLookup = this._propLookup[i] || {}; 3698 this._overwrittenProps = this._overwrittenProps || []; 3699 overwrittenProps = this._overwrittenProps[i] = vars ? this._overwrittenProps[i] || {} : "all"; 3700 break; 3701 } 3702 } 3703 } else if (target !== this.target) { 3704 return false; 3705 } else { 3706 propLookup = this._propLookup; 3707 overwrittenProps = this._overwrittenProps = vars ? this._overwrittenProps || {} : "all"; 3708 } 3709 3710 if (propLookup) { 3711 killProps = vars || propLookup; 3712 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) 3713 if (overwritingTween && (TweenLite.onOverwrite || this.vars.onOverwrite)) { 3714 for (p in killProps) { 3715 if (propLookup[p]) { 3716 if (!killed) { 3717 killed = []; 3718 } 3719 killed.push(p); 3720 } 3721 } 3722 if ((killed || !vars) && !_onOverwrite(this, overwritingTween, target, killed)) { //if the onOverwrite returned false, that means the user wants to override the overwriting (cancel it). 3723 return false; 3724 } 3725 } 3726 3727 for (p in killProps) { 3728 if ((pt = propLookup[p])) { 3729 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. 3730 if (pt.f) { 3731 pt.t[pt.p](pt.s); 3732 } else { 3733 pt.t[pt.p] = pt.s; 3734 } 3735 changed = true; 3736 } 3737 if (pt.pg && pt.t._kill(killProps)) { 3738 changed = true; //some plugins need to be notified so they can perform cleanup tasks first 3739 } 3740 if (!pt.pg || pt.t._overwriteProps.length === 0) { 3741 if (pt._prev) { 3742 pt._prev._next = pt._next; 3743 } else if (pt === this._firstPT) { 3744 this._firstPT = pt._next; 3745 } 3746 if (pt._next) { 3747 pt._next._prev = pt._prev; 3748 } 3749 pt._next = pt._prev = null; 3750 } 3751 delete propLookup[p]; 3752 } 3753 if (record) { 3754 overwrittenProps[p] = 1; 3755 } 3756 } 3757 if (!this._firstPT && this._initted) { //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. 3758 this._enabled(false, false); 3759 } 3760 } 3761 } 3762 return changed; 3763 }; 3764 3765 p.invalidate = function() { 3766 if (this._notifyPluginsOfEnabled) { 3767 TweenLite._onPluginEvent("_onDisable", this); 3768 } 3769 this._firstPT = this._overwrittenProps = this._startAt = this._onUpdate = null; 3770 this._notifyPluginsOfEnabled = this._active = this._lazy = false; 3771 this._propLookup = (this._targets) ? {} : []; 3772 Animation.prototype.invalidate.call(this); 3773 if (this.vars.immediateRender) { 3774 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) 3775 this.render(-this._delay); 3776 } 3777 return this; 3778 }; 3779 3780 p._enabled = function(enabled, ignoreTimeline) { 3781 if (!_tickerActive) { 3782 _ticker.wake(); 3783 } 3784 if (enabled && this._gc) { 3785 var targets = this._targets, 3786 i; 3787 if (targets) { 3788 i = targets.length; 3789 while (--i > -1) { 3790 this._siblings[i] = _register(targets[i], this, true); 3791 } 3792 } else { 3793 this._siblings = _register(this.target, this, true); 3794 } 3795 } 3796 Animation.prototype._enabled.call(this, enabled, ignoreTimeline); 3797 if (this._notifyPluginsOfEnabled) if (this._firstPT) { 3798 return TweenLite._onPluginEvent((enabled ? "_onEnable" : "_onDisable"), this); 3799 } 3800 return false; 3801 }; 3802 3803 3804//----TweenLite static methods ----------------------------------------------------- 3805 3806 TweenLite.to = function(target, duration, vars) { 3807 return new TweenLite(target, duration, vars); 3808 }; 3809 3810 TweenLite.from = function(target, duration, vars) { 3811 vars.runBackwards = true; 3812 vars.immediateRender = (vars.immediateRender != false); 3813 return new TweenLite(target, duration, vars); 3814 }; 3815 3816 TweenLite.fromTo = function(target, duration, fromVars, toVars) { 3817 toVars.startAt = fromVars; 3818 toVars.immediateRender = (toVars.immediateRender != false && fromVars.immediateRender != false); 3819 return new TweenLite(target, duration, toVars); 3820 }; 3821 3822 TweenLite.delayedCall = function(delay, callback, params, scope, useFrames) { 3823 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}); 3824 }; 3825 3826 TweenLite.set = function(target, vars) { 3827 return new TweenLite(target, 0, vars); 3828 }; 3829 3830 TweenLite.getTweensOf = function(target, onlyActive) { 3831 if (target == null) { return []; } 3832 target = (typeof(target) !== "string") ? target : TweenLite.selector(target) || target; 3833 var i, a, j, t; 3834 if ((_isArray(target) || _isSelector(target)) && typeof(target[0]) !== "number") { 3835 i = target.length; 3836 a = []; 3837 while (--i > -1) { 3838 a = a.concat(TweenLite.getTweensOf(target[i], onlyActive)); 3839 } 3840 i = a.length; 3841 //now get rid of any duplicates (tweens of arrays of objects could cause duplicates) 3842 while (--i > -1) { 3843 t = a[i]; 3844 j = i; 3845 while (--j > -1) { 3846 if (t === a[j]) { 3847 a.splice(i, 1); 3848 } 3849 } 3850 } 3851 } else { 3852 a = _register(target).concat(); 3853 i = a.length; 3854 while (--i > -1) { 3855 if (a[i]._gc || (onlyActive && !a[i].isActive())) { 3856 a.splice(i, 1); 3857 } 3858 } 3859 } 3860 return a; 3861 }; 3862 3863 TweenLite.killTweensOf = TweenLite.killDelayedCallsTo = function(target, onlyActive, vars) { 3864 if (typeof(onlyActive) === "object") { 3865 vars = onlyActive; //for backwards compatibility (before "onlyActive" parameter was inserted) 3866 onlyActive = false; 3867 } 3868 var a = TweenLite.getTweensOf(target, onlyActive), 3869 i = a.length; 3870 while (--i > -1) { 3871 a[i]._kill(vars, target); 3872 } 3873 }; 3874 3875 3876 3877/* 3878 * ---------------------------------------------------------------- 3879 * 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) 3880 * ---------------------------------------------------------------- 3881 */ 3882 var TweenPlugin = _class("plugins.TweenPlugin", function(props, priority) { 3883 this._overwriteProps = (props || "").split(","); 3884 this._propName = this._overwriteProps[0]; 3885 this._priority = priority || 0; 3886 this._super = TweenPlugin.prototype; 3887 }, true); 3888 3889 p = TweenPlugin.prototype; 3890 TweenPlugin.version = "1.18.0"; 3891 TweenPlugin.API = 2; 3892 p._firstPT = null; 3893 p._addTween = _addPropTween; 3894 p.setRatio = _setRatio; 3895 3896 p._kill = function(lookup) { 3897 var a = this._overwriteProps, 3898 pt = this._firstPT, 3899 i; 3900 if (lookup[this._propName] != null) { 3901 this._overwriteProps = []; 3902 } else { 3903 i = a.length; 3904 while (--i > -1) { 3905 if (lookup[a[i]] != null) { 3906 a.splice(i, 1); 3907 } 3908 } 3909 } 3910 while (pt) { 3911 if (lookup[pt.n] != null) { 3912 if (pt._next) { 3913 pt._next._prev = pt._prev; 3914 } 3915 if (pt._prev) { 3916 pt._prev._next = pt._next; 3917 pt._prev = null; 3918 } else if (this._firstPT === pt) { 3919 this._firstPT = pt._next; 3920 } 3921 } 3922 pt = pt._next; 3923 } 3924 return false; 3925 }; 3926 3927 p._roundProps = function(lookup, value) { 3928 var pt = this._firstPT; 3929 while (pt) { 3930 if (lookup[this._propName] || (pt.n != null && lookup[ pt.n.split(this._propName + "_").join("") ])) { //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. 3931 pt.r = value; 3932 } 3933 pt = pt._next; 3934 } 3935 }; 3936 3937 TweenLite._onPluginEvent = function(type, tween) { 3938 var pt = tween._firstPT, 3939 changed, pt2, first, last, next; 3940 if (type === "_onInitAllProps") { 3941 //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. 3942 while (pt) { 3943 next = pt._next; 3944 pt2 = first; 3945 while (pt2 && pt2.pr > pt.pr) { 3946 pt2 = pt2._next; 3947 } 3948 if ((pt._prev = pt2 ? pt2._prev : last)) { 3949 pt._prev._next = pt; 3950 } else { 3951 first = pt; 3952 } 3953 if ((pt._next = pt2)) { 3954 pt2._prev = pt; 3955 } else { 3956 last = pt; 3957 } 3958 pt = next; 3959 } 3960 pt = tween._firstPT = first; 3961 } 3962 while (pt) { 3963 if (pt.pg) if (typeof(pt.t[type]) === "function") if (pt.t[type]()) { 3964 changed = true; 3965 } 3966 pt = pt._next; 3967 } 3968 return changed; 3969 }; 3970 3971 TweenPlugin.activate = function(plugins) { 3972 var i = plugins.length; 3973 while (--i > -1) { 3974 if (plugins[i].API === TweenPlugin.API) { 3975 _plugins[(new plugins[i]())._propName] = plugins[i]; 3976 } 3977 } 3978 return true; 3979 }; 3980 3981 //provides a more concise way to define plugins that have no depen
3981dencies 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. 3982 _gsDefine.plugin = function(config) { 3983 if (!config || !config.propName || !config.init || !config.API) { throw "illegal plugin definition."; } 3984 var propName = config.propName, 3985 priority = config.priority || 0, 3986 overwriteProps = config.overwriteProps, 3987 map = {init:"_onInitTween", set:"setRatio", kill:"_kill", round:"_roundProps", initAll:"_onInitAllProps"}, 3988 Plugin = _class("plugins." + propName.charAt(0).toUpperCase() + propName.substr(1) + "Plugin", 3989 function() { 3990 TweenPlugin.call(this, propName, priority); 3991 this._overwriteProps = overwriteProps || []; 3992 }, (config.global === true)), 3993 p = Plugin.prototype = new TweenPlugin(propName), 3994 prop; 3995 p.constructor = Plugin; 3996 Plugin.API = config.API; 3997 for (prop in map) { 3998 if (typeof(config[prop]) === "function") { 3999 p[map[prop]] = config[prop]; 4000 } 4001 } 4002 Plugin.version = config.version; 4003 TweenPlugin.activate([Plugin]); 4004 return Plugin; 4005 }; 4006 4007 4008 //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. 4009 a = window._gsQueue; 4010 if (a) { 4011 for (i = 0; i < a.length; i++) { 4012 a[i](); 4013 } 4014 for (p in _defLookup) { 4015 if (!_defLookup[p].func) { 4016 window.console.log("GSAP encountered missing dependency: com.greensock." + p); 4017 } 4018 } 4019 } 4020 4021 _tickerActive = false; //ensures that the first official animation forces a ticker.tick() to update the time when it is instantiated 4022 4023})((typeof(module) !== "undefined" && module.exports && typeof(global) !== "undefined") ? global : this || window, "TweenLite"); 4024 4025/*! 4026 * VERSION: 1.18.0 4027 * DATE: 2015-08-29 4028 * UPDATES AND DOCS AT: http://greensock.com 4029 * 4030 * @license Copyright (c) 2008-2015, GreenSock. All rights reserved. 4031 * This work is subject to the terms at http://greensock.com/standard-license or for 4032 * Club GreenSock members, the software agreement that was issued with your membership. 4033 * 4034 * @author: Jack Doyle, [email protected] 4035 */ 4036var _gsScope = (typeof(module) !== "undefined" && module.exports && typeof(global) !== "undefined") ? global : this || window; //helps ensure compatibility with AMD/RequireJS and CommonJS/Node 4037(_gsScope._gsQueue || (_gsScope._gsQueue = [])).push( function() { 4038 4039 "use strict"; 4040 4041 _gsScope._gsDefine("TimelineLite", ["core.Animation","core.SimpleTimeline","TweenLite"], function(Animation, SimpleTimeline, TweenLite) { 4042 4043 var TimelineLite = function(vars) { 4044 SimpleTimeline.call(this, vars); 4045 this._labels = {}; 4046 this.autoRemoveChildren = (this.vars.autoRemoveChildren === true); 4047 this.smoothChildTiming = (this.vars.smoothChildTiming === true); 4048 this._sortChildren = true; 4049 this._onUpdate = this.vars.onUpdate; 4050 var v = this.vars, 4051 val, p; 4052 for (p in v) { 4053 val = v[p]; 4054 if (_isArray(val)) if (val.join("").indexOf("{self}") !== -1) { 4055 v[p] = this._swapSelfInParams(val); 4056 } 4057 } 4058 if (_isArray(v.tweens)) { 4059 this.add(v.tweens, 0, v.align, v.stagger); 4060 } 4061 }, 4062 _tinyNum = 0.0000000001, 4063 TweenLiteInternals = TweenLite._internals, 4064 _internals = TimelineLite._internals = {}, 4065 _isSelector = TweenLiteInternals.isSelector, 4066 _isArray = TweenLiteInternals.isArray, 4067 _lazyTweens = TweenLiteInternals.lazyTweens, 4068 _lazyRender = TweenLiteInternals.lazyRender, 4069 _globals = _gsScope._gsDefine.globals, 4070 _copy = function(vars) { 4071 var copy = {}, p; 4072 for (p in vars) { 4073 copy[p] = vars[p]; 4074 } 4075 return copy; 4076 }, 4077 _applyCycle = function(vars, targets, i) { 4078 var alt = vars.cycle, 4079 p, val; 4080 for (p in alt) { 4081 val = alt[p]; 4082 vars[p] = (typeof(val) === "function") ? val.call(targets[i], i) : val[i % val.length]; 4083 } 4084 delete vars.cycle; 4085 }, 4086 _pauseCallback = _internals.pauseCallback = function() {}, 4087 _slice = function(a) { //don't use [].slice because that doesn't work in IE8 with a NodeList that's returned by querySelectorAll() 4088 var b = [], 4089 l = a.length, 4090 i; 4091 for (i = 0; i !== l; b.push(a[i++])); 4092 return b; 4093 }, 4094 p = TimelineLite.prototype = new SimpleTimeline(); 4095
4096 TimelineLite.version = "1.18.0"; 4097 p.constructor = TimelineLite; 4098 p.kill()._gc = p._forcingPlayhead = p._hasPause = false; 4099 4100 /* might use later... 4101 //translates a local time inside an animation to the corresponding time on the root/global timeline, factoring in all nesting and timeScales. 4102 function localToGlobal(time, animation) { 4103 while (animation) { 4104 time = (time / animation._timeScale) + animation._startTime; 4105 animation = animation.timeline; 4106 } 4107 return time; 4108 } 4109 4110 //translates the supplied time on the root/global timeline into the corresponding local time inside a particular animation, factoring in all nesting and timeScales 4111 function globalToLocal(time, animation) { 4112 var scale = 1; 4113 time -= localToGlobal(0, animation); 4114 while (animation) { 4115 scale *= animation._timeScale; 4116 animation = animation.timeline; 4117 } 4118 return time * scale; 4119 } 4120 */ 4121 4122 p.to = function(target, duration, vars, position) { 4123 var Engine = (vars.repeat && _globals.TweenMax) || TweenLite; 4124 return duration ? this.add( new Engine(target, duration, vars), position) : this.set(target, vars, position); 4125 }; 4126 4127 p.from = function(target, duration, vars, position) { 4128 return this.add( ((vars.repeat && _globals.TweenMax) || TweenLite).from(target, duration, vars), position); 4129 }; 4130 4131 p.fromTo = function(target, duration, fromVars, toVars, position) { 4132 var Engine = (toVars.repeat && _globals.TweenMax) || TweenLite; 4133 return duration ? this.add( Engine.fromTo(target, duration, fromVars, toVars), position) : this.set(target, toVars, position); 4134 }; 4135 4136 p.staggerTo = function(targets, duration, vars, stagger, position, onCompleteAll, onCompleteAllParams, onCompleteAllScope) { 4137 var tl = new TimelineLite({onComplete:onCompleteAll, onCompleteParams:onCompleteAllParams, callbackScope:onCompleteAllScope, smoothChildTiming:this.smoothChildTiming}), 4138 cycle = vars.cycle, 4139 copy, i; 4140 if (typeof(targets) === "string") { 4141 targets = TweenLite.selector(targets) || targets; 4142 } 4143 targets = targets || []; 4144 if (_isSelector(targets)) { //senses if the targets object is a selector. If it is, we should translate it into an array. 4145 targets = _slice(targets); 4146 } 4147 stagger = stagger || 0; 4148 if (stagger < 0) { 4149 targets = _slice(targets); 4150 targets.reverse(); 4151 stagger *= -1; 4152 } 4153 for (i = 0; i < targets.length; i++) { 4154 copy = _copy(vars); 4155 if (copy.startAt) { 4156 copy.startAt = _copy(copy.startAt); 4157 if (copy.startAt.cycle) { 4158 _applyCycle(copy.startAt, targets, i); 4159 } 4160 } 4161 if (cycle) { 4162 _applyCycle(copy, targets, i); 4163 } 4164 tl.to(targets[i], duration, copy, i * stagger); 4165 } 4166 return this.add(tl, position); 4167 }; 4168 4169 p.staggerFrom = function(targets, duration, vars, stagger, position, onCompleteAll, onCompleteAllParams, onCompleteAllScope) { 4170 vars.immediateRender = (vars.immediateRender != false); 4171 vars.runBackwards = true; 4172 return this.staggerTo(targets, duration, vars, stagger, position, onCompleteAll, onCompleteAllParams, onCompleteAllScope); 4173 }; 4174 4175 p.staggerFromTo = function(targets, duration, fromVars, toVars, stagger, position, onCompleteAll, onCompleteAllParams, onCompleteAllScope) { 4176 toVars.startAt = fromVars; 4177 toVars.immediateRender = (toVars.immediateRender != false && fromVars.immediateRender != false); 4178 return this.staggerTo(targets, duration, toVars, stagger, position, onCompleteAll, onCompleteAllParams, onCompleteAllScope); 4179 }; 4180 4181 p.call = function(callback, params, scope, position) { 4182 return this.add( TweenLite.delayedCall(0, callback, params, scope), position); 4183 }; 4184 4185 p.set = function(target, vars, position) { 4186 position = this._parseTimeOrLabel(position, 0, true); 4187 if (vars.immediateRender == null) { 4188 vars.immediateRender = (position === this._time && !this._paused); 4189 } 4190 return this.add( new TweenLite(target, 0, vars), position); 4191 }; 4192 4193 TimelineLite.exportRoot = function(vars, ignoreDelayedCalls) { 4194 vars = vars || {}; 4195 if (vars.smoothChildTiming == null) { 4196 vars.smoothChildTiming = true; 4197 } 4198 var tl = new TimelineLite(vars), 4199 root = tl._timeline, 4200 tween, next; 4201 if (ignoreDelayedCalls == null) { 4202 ignoreDelayedCalls = true; 4203 } 4204 root._remove(tl, true); 4205 tl._startTime = 0; 4206 tl._rawPrevTime = tl._time = tl._totalTime = root._time; 4207 tween = root._first; 4208 while (tween) { 4209 next = tween._next; 4210 if (!ignoreDelayedCalls || !(tween instanceof TweenLite && tween.target === tween.vars.onComplete)) { 4211 tl.add(tween, tween._startTime - tween._delay); 4212 } 4213 tween = next; 4214 } 4215 root.add(tl, 0); 4216 return tl; 4217 }; 4218 4219 p.add = function(value, position, align, stagger) { 4220 var curTime, l, i, child, tl, beforeRawTime; 4221 if (typeof(position) !== "number") { 4222 position = this._parseTimeOrLabel(position, 0, true, value); 4223 } 4224 if (!(value instanceof Animation)) { 4225 if ((value instanceof Array) || (value && value.push && _isArray(value))) {
4226 align = align || "normal"; 4227 stagger = stagger || 0; 4228 curTime = position; 4229 l = value.length; 4230 for (i = 0; i < l; i++) { 4231 if (_isArray(child = value[i])) { 4232 child = new TimelineLite({tweens:child}); 4233 } 4234 this.add(child, curTime); 4235 if (typeof(child) !== "string" && typeof(child) !== "function") { 4236 if (align === "sequence") { 4237 curTime = child._startTime + (child.totalDuration() / child._timeScale); 4238 } else if (align === "start") { 4239 child._startTime -= child.delay(); 4240 } 4241 } 4242 curTime += stagger; 4243 } 4244 return this._uncache(true); 4245 } else if (typeof(value) === "string") { 4246 return this.addLabel(value, position); 4247 } else if (typeof(value) === "function") { 4248 value = TweenLite.delayedCall(0, value); 4249 } else { 4250 throw("Cannot add " + value + " into the timeline; it is not a tween, timeline, function, or string."); 4251 } 4252 } 4253 4254 SimpleTimeline.prototype.add.call(this, value, position); 4255 4256 //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. 4257 if (this._gc || this._time === this._duration) if (!this._paused) if (this._duration < this.duration()) { 4258 //in case any of the ancestors had completed but should now be enabled... 4259 tl = this; 4260 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. 4261 while (tl._timeline) { 4262 if (beforeRawTime && tl._timeline.smoothChildTiming) { 4263 tl.totalTime(tl._totalTime, true); //moves the timeline (shifts its startTime) if necessary, and also enables it. 4264 } else if (tl._gc) { 4265 tl._enabled(true, false); 4266 } 4267 tl = tl._timeline; 4268 } 4269 } 4270 4271 return this; 4272 }; 4273 4274 p.remove = function(value) { 4275 if (value instanceof Animation) { 4276 this._remove(value, false); 4277 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. 4278 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. 4279 return this; 4280 } else if (value instanceof Array || (value && value.push && _isArray(value))) { 4281 var i = value.length; 4282 while (--i > -1) { 4283 this.remove(value[i]); 4284 } 4285 return this; 4286 } else if (typeof(value) === "string") { 4287 return this.removeLabel(value); 4288 } 4289 return this.kill(null, value); 4290 }; 4291 4292 p._remove = function(tween, skipDisable) { 4293 SimpleTimeline.prototype._remove.call(this, tween, skipDisable); 4294 var last = this._last; 4295 if (!last) { 4296 this._time = this._totalTime = this._duration = this._totalDuration = 0; 4297 } else if (this._time > last._startTime + last._totalDuration / last._timeScale) { 4298 this._time = this.duration(); 4299 this._totalTime = this._totalDuration; 4300 } 4301 return this; 4302 }; 4303 4304 p.append = function(value, offsetOrLabel) { 4305 return this.add(value, this._parseTimeOrLabel(null, offsetOrLabel, true, value)); 4306 }; 4307 4308 p.insert = p.insertMultiple = function(value, position, align, stagger) { 4309 return this.add(value, position || 0, align, stagger); 4310 }; 4311 4312 p.appendMultiple = function(tweens, offsetOrLabel, align, stagger) { 4313 return this.add(tweens, this._parseTimeOrLabel(null, offsetOrLabel, true, tweens), align, stagger); 4314 }; 4315 4316 p.addLabel = function(label, position) { 4317 this._labels[label] = this._parseTimeOrLabel(position); 4318 return this; 4319 }; 4320 4321 p.addPause = function(position, callback, params, scope) { 4322 var t = TweenLite.delayedCall(0, _pauseCallback, params, scope || this); 4323 t.vars.onComplete = t.vars.onReverseComplete = callback;
4324 t.data = "isPause"; 4325 this._hasPause = true; 4326 return this.add(t, position); 4327 }; 4328 4329 p.removeLabel = function(label) { 4330 delete this._labels[label]; 4331 return this; 4332 }; 4333 4334 p.getLabelTime = function(label) { 4335 return (this._labels[label] != null) ? this._labels[label] : -1; 4336 }; 4337 4338 p._parseTimeOrLabel = function(timeOrLabel, offsetOrLabel, appendIfAbsent, ignore) { 4339 var i; 4340 //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(). 4341 if (ignore instanceof Animation && ignore.timeline === this) { 4342 this.remove(ignore); 4343 } else if (ignore && ((ignore instanceof Array) || (ignore.push && _isArray(ignore)))) { 4344 i = ignore.length; 4345 while (--i > -1) { 4346 if (ignore[i] instanceof Animation && ignore[i].timeline === this) { 4347 this.remove(ignore[i]); 4348 } 4349 } 4350 } 4351 if (typeof(offsetOrLabel) === "string") { 4352 return this._parseTimeOrLabel(offsetOrLabel, (appendIfAbsent && typeof(timeOrLabel) === "number" && this._labels[offsetOrLabel] == null) ? timeOrLabel - this.duration() : 0, appendIfAbsent); 4353 } 4354 offsetOrLabel = offsetOrLabel || 0; 4355 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). 4356 i = timeOrLabel.indexOf("="); 4357 if (i === -1) { 4358 if (this._labels[timeOrLabel] == null) { 4359 return appendIfAbsent ? (this._labels[timeOrLabel] = this.duration() + offsetOrLabel) : offsetOrLabel; 4360 } 4361 return this._labels[timeOrLabel] + offsetOrLabel; 4362 } 4363 offsetOrLabel = parseInt(timeOrLabel.charAt(i-1) + "1", 10) * Number(timeOrLabel.substr(i+1)); 4364 timeOrLabel = (i > 1) ? this._parseTimeOrLabel(timeOrLabel.substr(0, i-1), 0, appendIfAbsent) : this.duration(); 4365 } else if (timeOrLabel == null) { 4366 timeOrLabel = this.duration(); 4367 } 4368 return Number(timeOrLabel) + offsetOrLabel; 4369 }; 4370 4371 p.seek = function(position, suppressEvents) { 4372 return this.totalTime((typeof(position) === "number") ? position : this._parseTimeOrLabel(position), (suppressEvents !== false)); 4373 }; 4374 4375 p.stop = function() { 4376 return this.paused(true); 4377 }; 4378 4379 p.gotoAndPlay = function(position, suppressEvents) { 4380 return this.play(position, suppressEvents); 4381 }; 4382 4383 p.gotoAndStop = function(position, suppressEvents) { 4384 return this.pause(position, suppressEvents); 4385 }; 4386 4387 p.render = function(time, suppressEvents, force) { 4388 if (this._gc) { 4389 this._enabled(true, false); 4390 } 4391 var totalDur = (!this._dirty) ? this._totalDuration : this.totalDuration(), 4392 prevTime = this._time, 4393 prevStart = this._startTime, 4394 prevTimeScale = this._timeScale, 4395 prevPaused = this._paused, 4396 tween, isComplete, next, callback, internalForce, pauseTween; 4397 if (time >= totalDur) { 4398 this._totalTime = this._time = totalDur; 4399 if (!this._reversed) if (!this._hasPausedChild()) { 4400 isComplete = true; 4401 callback = "onComplete"; 4402 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. 4403 if (this._duration === 0) if (time === 0 || this._rawPrevTime < 0 || this._rawPrevTime === _tinyNum) if (this._rawPrevTime !== time && this._first) { 4404 internalForce = true; 4405 if (this._rawPrevTime > _tinyNum) { 4406 callback = "onReverseComplete"; 4407 } 4408 } 4409 } 4410 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. 4411 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. 4412 4413 } else if (time < 0.0000001) { //to work around occasional floating point math artifacts, round super small values to 0. 4414 this._totalTime = this._time = 0; 4415 if (prevTime !== 0 || (this._duration === 0 && this._rawPrevTime !== _tinyNum && (this._rawPrevTime > 0 || (time < 0 && this._rawPrevTime >= 0)))) { 4416 callback = "onReverseComplete"; 4417 isComplete = this._reversed; 4418 } 4419 if (time < 0) { 4420 this._active = false; 4421 if (this._timeline.autoRemoveChildren && this._reversed) { //ensures proper GC if a timeline is resumed after it's finished reversing. 4422 internalForce = isComplete = true; 4423 callback = "onReverseComplete"; 4424 } 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. 4425 internalForce = true; 4426 } 4427 this._rawPrevTime = time; 4428 } else { 4429 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. 4430 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). 4431 tween = this._first; 4432 while (tween && tween._startTime === 0) { 4433 if (!tween._duration) { 4434 isComplete = false; 4435 } 4436 tween = tween._next; 4437 } 4438 } 4439 time = 0; //to avoid occasional floating point rounding errors (could cause problems especially with zero-duration tweens at the very beginning of the timeline) 4440 if (!this._initted) { 4441 internalForce = true; 4442 } 4443 } 4444 4445 } else { 4446 4447 if (this._hasPause && !this._forcingPlayhead && !suppressEvents) { 4448 if (time >= prevTime) { 4449 tween = this._first; 4450 while (tween && tween._startTime <= time && !pauseTween) { 4451 if (!tween._duration) if (tween.data === "isPause" && !tween.ratio && !(tween._startTime === 0 && this._rawPrevTime === 0)) { 4452 pauseTween = tween; 4453 } 4454 tween = tween._next; 4455 } 4456 } else { 4457 tween = this._last; 4458 while (tween && tween._startTime >= time && !pauseTween) { 4459 if (!tween._duration) if (tween.data === "isPause" && tween._rawPrevTime > 0) { 4460 pauseTween = tween; 4461 } 4462 tween = tween._prev; 4463 } 4464 } 4465 if (pauseTween) { 4466 this._time = time = pauseTween._startTime; 4467 this._totalTime = time + (this._cycle * (this._totalDuration + this._repeatDelay)); 4468 } 4469 } 4470 4471 this._totalTime = this._time = this._rawPrevTime = time; 4472 } 4473 if ((this._time === prevTime || !this._first) && !force && !internalForce && !pauseTween) { 4474 return; 4475 } else if (!this._initted) { 4476 this._initted = true; 4477 } 4478 4479 if (!this._active) if (!this._paused && this._time !== prevTime && time > 0) { 4480 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. 4481 } 4482 4483 if (prevTime === 0) if (this.vars.onStart) if (this._time !== 0) if (!suppressEvents) { 4484 this._callback("onStart"); 4485 } 4486 4487 if (this._time >= prevTime) { 4488 tween = this._first; 4489 while (tween) { 4490 next = tween._next; //record it here because the value could change after rendering... 4491 if (this._paused && !prevPaused) { //in case a tween pauses the timeline when rendering 4492 break; 4493 } else if (tween._active || (tween._startTime <= this._time && !tween._paused && !tween._gc)) { 4494 if (pauseTween === tween) { 4495 this.pause(); 4496 } 4497 if (!tween._reversed) { 4498 tween.render((time - tween._startTime) * tween._timeScale, suppressEvents, force); 4499 } else { 4500 tween.render(((!tween._dirty) ? tween._totalDuration : tween.totalDuration()) - ((time - tween._startTime) * tween._timeScale), suppressEvents, force); 4501 } 4502 } 4503 tween = next; 4504 } 4505 } else { 4506 tween = this._last; 4507 while (tween) { 4508 next = tween._prev; //record it here because the value could change after rendering... 4509 if (this._paused && !prevPaused) { //in case a tween pauses the timeline when rendering 4510 break; 4511 } else if (tween._active || (tween._startTime <= prevTime && !tween._paused && !tween._gc)) { 4512 if (pauseTween === tween) { 4513 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. 4514 while (pauseTween && pauseTween.endTime() > this._time) { 4515 pauseTween.render( (pauseTween._reversed ? pauseTween.totalDuration() - ((time - pauseTween._startTime) * pauseTween._timeScale) : (time - pauseTween._startTime) * pauseTween._timeScale), suppressEvents, force); 4516 pauseTween = pauseTween._prev; 4517 } 4518 pauseTween = null; 4519 this.pause(); 4520 } 4521 if (!tween._reversed) { 4522 tween.render((time - tween._startTime) * tween._timeScale, suppressEvents, force); 4523 } else { 4524 tween.render(((!tween._dirty) ? tween._totalDuration : tween.totalDuration()) - ((time - tween._startTime) * tween._timeScale), suppressEvents, force); 4525 } 4526 } 4527 tween = next; 4528 } 4529 } 4530 4531 if (this._onUpdate) if (!suppressEvents) { 4532 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. 4533 _lazyRender(); 4534 }
4535 this._callback("onUpdate"); 4536 } 4537 4538 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 4539 if (isComplete) { 4540 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. 4541 _lazyRender(); 4542 } 4543 if (this._timeline.autoRemoveChildren) { 4544 this._enabled(false, false); 4545 } 4546 this._active = false; 4547 } 4548 if (!suppressEvents && this.vars[callback]) { 4549 this._callback(callback); 4550 } 4551 } 4552 }; 4553 4554 p._hasPausedChild = function() { 4555 var tween = this._first; 4556 while (tween) { 4557 if (tween._paused || ((tween instanceof TimelineLite) && tween._hasPausedChild())) { 4558 return true; 4559 } 4560 tween = tween._next; 4561 } 4562 return false; 4563 }; 4564 4565 p.getChildren = function(nested, tweens, timelines, ignoreBeforeTime) { 4566 ignoreBeforeTime = ignoreBeforeTime || -9999999999; 4567 var a = [], 4568 tween = this._first, 4569 cnt = 0; 4570 while (tween) { 4571 if (tween._startTime < ignoreBeforeTime) { 4572 //do nothing 4573 } else if (tween instanceof TweenLite) { 4574 if (tweens !== false) { 4575 a[cnt++] = tween; 4576 } 4577 } else { 4578 if (timelines !== false) { 4579 a[cnt++] = tween; 4580 } 4581 if (nested !== false) { 4582 a = a.concat(tween.getChildren(true, tweens, timelines)); 4583 cnt = a.length; 4584 } 4585 } 4586 tween = tween._next; 4587 } 4588 return a; 4589 }; 4590 4591 p.getTweensOf = function(target, nested) { 4592 var disabled = this._gc, 4593 a = [], 4594 cnt = 0, 4595 tweens, i; 4596 if (disabled) { 4597 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. 4598 } 4599 tweens = TweenLite.getTweensOf(target); 4600 i = tweens.length; 4601 while (--i > -1) { 4602 if (tweens[i].timeline === this || (nested && this._contains(tweens[i]))) { 4603 a[cnt++] = tweens[i]; 4604 } 4605 } 4606 if (disabled) { 4607 this._enabled(false, true); 4608 } 4609 return a; 4610 }; 4611 4612 p.recent = function() { 4613 return this._recent; 4614 }; 4615 4616 p._contains = function(tween) { 4617 var tl = tween.timeline; 4618 while (tl) { 4619 if (tl === this) { 4620 return true; 4621 } 4622 tl = tl.timeline; 4623 } 4624 return false; 4625 }; 4626 4627 p.shiftChildren = function(amount, adjustLabels, ignoreBeforeTime) { 4628 ignoreBeforeTime = ignoreBeforeTime || 0; 4629 var tween = this._first, 4630 labels = this._labels, 4631 p; 4632 while (tween) { 4633 if (tween._startTime >= ignoreBeforeTime) { 4634 tween._startTime += amount; 4635 } 4636 tween = tween._next; 4637 } 4638 if (adjustLabels) { 4639 for (p in labels) { 4640 if (labels[p] >= ignoreBeforeTime) { 4641 labels[p] += amount; 4642 } 4643 } 4644 } 4645 return this._uncache(true); 4646 }; 4647 4648 p._kill = function(vars, target) { 4649 if (!vars && !target) { 4650 return this._enabled(false, false); 4651 } 4652 var tweens = (!target) ? this.getChildren(true, true, false) : this.getTweensOf(target), 4653 i = tweens.length, 4654 changed = false; 4655 while (--i > -1) { 4656 if (tweens[i]._kill(vars, target)) { 4657 changed = true; 4658 } 4659 } 4660 return changed; 4661 }; 4662 4663 p.clear = function(labels) { 4664 var tweens = this.getChildren(false, true, true), 4665 i = tweens.length; 4666 this._time = this._totalTime = 0; 4667 while (--i > -1) { 4668 tweens[i]._enabled(false, false); 4669 } 4670 if (labels !== false) { 4671 this._labels = {}; 4672 } 4673 return this._uncache(true); 4674 }; 4675 4676 p.invalidate = function() { 4677 var tween = this._first; 4678 while (tween) { 4679 tween.invalidate(); 4680 tween = tween._next; 4681 } 4682 return Animation.prototype.invalidate.call(this);; 4683 }; 4684 4685 p._enabled = function(enabled, ignoreTimeline) { 4686 if (enabled === this._gc) { 4687 var tween = this._first; 4688 while (tween) { 4689 tween._enabled(enabled, true); 4690 tween = tween._next; 4691 } 4692 } 4693 return SimpleTimeline.prototype._enabled.call(this, enabled, ignoreTimeline); 4694 }; 4695 4696 p.totalTime = function(time, suppressEvents, uncapped) { 4697 this._forcingPlayhead = true; 4698 var val = Animation.prototype.totalTime.apply(this, arguments); 4699 this._forcingPlayhead = false; 4700 return val; 4701 }; 4702 4703 p.duration = function(value) { 4704 if (!arguments.length) { 4705 if (this._dirty) { 4706 this.totalDuration(); //just triggers recalculation 4707 } 4708 return this._duration; 4709 } 4710 if (this.duration() !== 0 && value !== 0) { 4711 this.timeScale(this._duration / value); 4712 } 4713 return this; 4714 }; 4715 4716 p.totalDuration = function(value) { 4717 if (!arguments.length) { 4718 if (this._dirty) { 4719 var max = 0, 4720 tween = this._last, 4721 prevStart = 999999999999, 4722 prev, end; 4723 while (tween) { 4724 prev = tween._prev; //record it here in case the tween changes position in the sequence... 4725 if (tween._dirty) { 4726 tween.totalDuration(); //could change the tween._startTime, so make sure the tween's cache is clean before analyzing it. 4727 } 4728 if (tween._startTime > prevStart && this._sortChildren && !tween._paused) { //in case one of the tweens shifted out of order, it needs to be re-inserted into the correct position in the sequence 4729 this.add(tween, tween._startTime - tween._delay); 4730 } else { 4731 prevStart = tween._startTime; 4732 } 4733 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. 4734 max -= tween._startTime; 4735 if (this._timeline.smoothChildTiming) { 4736 this._startTime += tween._startTime / this._timeScale; 4737 } 4738 this.shiftChildren(-tween._startTime, false, -9999999999); 4739 prevStart = 0; 4740 } 4741 end = tween._startTime + (tween._totalDuration / tween._timeScale); 4742 if (end > max) { 4743 max = end; 4744 } 4745 tween = prev; 4746 } 4747 this._duration = this._totalDuration = max; 4748 this._dirty = false; 4749 } 4750 return this._totalDuration; 4751 } 4752 if (this.totalDuration() !== 0) if (value !== 0) { 4753 this.timeScale(this._totalDuration / value); 4754 } 4755 return this; 4756 }; 4757 4758 p.paused = function(value) { 4759 if (!value) { //if there's a pause directly at the spot from where we're unpausing, skip it. 4760 var tween = this._first, 4761 time = this._time; 4762 while (tween) { 4763 if (tween._startTime === time && tween.data === "isPause") { 4764 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. 4765 } 4766 tween = tween._next; 4767 } 4768 } 4769 return Animation.prototype.paused.apply(this, arguments); 4770 }; 4771 4772 p.usesFrames = function() { 4773 var tl = this._timeline; 4774 while (tl._timeline) { 4775 tl = tl._timeline; 4776 } 4777 return (tl === Animation._rootFramesTimeline); 4778 }; 4779 4780 p.rawTime = function() { 4781 return this._paused ? this._totalTime : (this._timeline.rawTime() - this._startTime) * this._timeScale; 4782 }; 4783 4784 return TimelineLite; 4785 4786 }, true); 4787 4788 4789}); if (_gsScope._gsDefine) { _gsScope._gsQueue.pop()(); } 4790 4791//export to AMD/RequireJS and CommonJS/Node (precursor to full modular build system coming at a later date) 4792(function(name) { 4793 "use strict"; 4794 var getGlobal = function() { 4795 return (_gsScope.GreenSockGlobals || _gsScope)[name]; 4796 }; 4797 if (typeof(define) === "function" && define.amd) { //AMD 4798 define(["TweenLite"], getGlobal); 4799 } else if (typeof(module) !== "undefined" && module.exports) { //node 4800 require("./TweenLite.js"); //dependency 4801 module.exports = getGlobal(); 4802 } 4803}("TimelineLite")); 4804 4805/*! 4806 * VERSION: beta 1.15.2 4807 * DATE: 2015-01-27 4808 * UPDATES AND DOCS AT: http://greensock.com 4809 * 4810 * @license Copyright (c) 2008-2015, GreenSock. All rights reserved. 4811 * This work is subject to the terms at http://greensock.com/standard-license or for
4812 * Club GreenSock members, the software agreement that was issued with your membership. 4813 * 4814 * @author: Jack Doyle, [email protected] 4815 **/ 4816var _gsScope = (typeof(module) !== "undefined" && module.exports && typeof(global) !== "undefined") ? global : this || window; //helps ensure compatibility with AMD/RequireJS and CommonJS/Node 4817(_gsScope._gsQueue || (_gsScope._gsQueue = [])).push( function() { 4818 4819 "use strict"; 4820 4821 _gsScope._gsDefine("easing.Back", ["easing.Ease"], function(Ease) { 4822 4823 var w = (_gsScope.GreenSockGlobals || _gsScope), 4824 gs = w.com.greensock, 4825 _2PI = Math.PI * 2, 4826 _HALF_PI = Math.PI / 2, 4827 _class = gs._class, 4828 _create = function(n, f) { 4829 var C = _class("easing." + n, function(){}, true), 4830 p = C.prototype = new Ease(); 4831 p.constructor = C; 4832 p.getRatio = f; 4833 return C; 4834 }, 4835 _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. 4836 _wrap = function(name, EaseOut, EaseIn, EaseInOut, aliases) { 4837 var C = _class("easing."+name, { 4838 easeOut:new EaseOut(), 4839 easeIn:new EaseIn(), 4840 easeInOut:new EaseInOut() 4841 }, true); 4842 _easeReg(C, name); 4843 return C; 4844 }, 4845 EasePoint = function(time, value, next) { 4846 this.t = time; 4847 this.v = value; 4848 if (next) { 4849 this.next = next; 4850 next.prev = this; 4851 this.c = next.v - value; 4852 this.gap = next.t - time; 4853 } 4854 }, 4855 4856 //Back 4857 _createBack = function(n, f) { 4858 var C = _class("easing." + n, function(overshoot) { 4859 this._p1 = (overshoot || overshoot === 0) ? overshoot : 1.70158; 4860 this._p2 = this._p1 * 1.525; 4861 }, true), 4862 p = C.prototype = new Ease(); 4863 p.constructor = C; 4864 p.getRatio = f; 4865 p.config = function(overshoot) { 4866 return new C(overshoot); 4867 }; 4868 return C; 4869 }, 4870 4871 Back = _wrap("Back", 4872 _createBack("BackOut", function(p) { 4873 return ((p = p - 1) * p * ((this._p1 + 1) * p + this._p1) + 1); 4874 }), 4875 _createBack("BackIn", function(p) { 4876 return p * p * ((this._p1 + 1) * p - this._p1); 4877 }), 4878 _createBack("BackInOut", function(p) { 4879 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); 4880 }) 4881 ), 4882 4883 4884 //SlowMo 4885 SlowMo = _class("easing.SlowMo", function(linearRatio, power, yoyoMode) { 4886 power = (power || power === 0) ? power : 0.7; 4887 if (linearRatio == null) { 4888 linearRatio = 0.7; 4889 } else if (linearRatio > 1) { 4890 linearRatio = 1; 4891 } 4892 this._p = (linearRatio !== 1) ? power : 0; 4893 this._p1 = (1 - linearRatio) / 2; 4894 this._p2 = linearRatio; 4895 this._p3 = this._p1 + this._p2; 4896 this._calcEnd = (yoyoMode === true); 4897 }, true), 4898 p = SlowMo.prototype = new Ease(), 4899 SteppedEase, RoughEase, _createElastic; 4900 4901 p.constructor = SlowMo; 4902 p.getRatio = function(p) { 4903 var r = p + (0.5 - p) * this._p; 4904 if (p < this._p1) { 4905 return this._calcEnd ? 1 - ((p = 1 - (p / this._p1)) * p) : r - ((p = 1 - (p / this._p1)) * p * p * p * r); 4906 } else if (p > this._p3) { 4907 return this._calcEnd ? 1 - (p = (p - this._p3) / this._p1) * p : r + ((p - r) * (p = (p - this._p3) / this._p1) * p * p * p); 4908 } 4909 return this._calcEnd ? 1 : r; 4910 }; 4911 SlowMo.ease = new SlowMo(0.7, 0.7); 4912 4913 p.config = SlowMo.config = function(linearRatio, power, yoyoMode) { 4914 return new SlowMo(linearRatio, power, yoyoMode); 4915 }; 4916 4917 4918 //SteppedEase 4919 SteppedEase = _class("easing.SteppedEase", function(steps) { 4920 steps = steps || 1; 4921 this._p1 = 1 / steps; 4922 this._p2 = steps + 1; 4923 }, true); 4924 p = SteppedEase.prototype = new Ease(); 4925 p.constructor = SteppedEase; 4926 p.getRatio = function(p) { 4927 if (p < 0) { 4928 p = 0; 4929 } else if (p >= 1) { 4930 p = 0.999999999; 4931 } 4932 return ((this._p2 * p) >> 0) * this._p1; 4933 }; 4934 p.config = SteppedEase.config = function(steps) { 4935 return new SteppedEase(steps); 4936 }; 4937 4938 4939 //RoughEase 4940 RoughEase = _class("easing.RoughEase", function(vars) { 4941 vars = vars || {}; 4942 var taper = vars.taper || "none", 4943 a = [], 4944 cnt = 0, 4945 points = (vars.points || 20) | 0, 4946 i = points, 4947 randomize = (vars.randomize !== false), 4948 clamp = (vars.clamp === true), 4949 template = (vars.template instanceof Ease) ? vars.template : null,
4950 strength = (typeof(vars.strength) === "number") ? vars.strength * 0.4 : 0.4, 4951 x, y, bump, invX, obj, pnt; 4952 while (--i > -1) { 4953 x = randomize ? Math.random() : (1 / points) * i; 4954 y = template ? template.getRatio(x) : x; 4955 if (taper === "none") { 4956 bump = strength; 4957 } else if (taper === "out") { 4958 invX = 1 - x; 4959 bump = invX * invX * strength; 4960 } else if (taper === "in") { 4961 bump = x * x * strength; 4962 } else if (x < 0.5) { //"both" (start) 4963 invX = x * 2; 4964 bump = invX * invX * 0.5 * strength; 4965 } else { //"both" (end) 4966 invX = (1 - x) * 2; 4967 bump = invX * invX * 0.5 * strength; 4968 } 4969 if (randomize) { 4970 y += (Math.random() * bump) - (bump * 0.5); 4971 } else if (i % 2) { 4972 y += bump * 0.5; 4973 } else { 4974 y -= bump * 0.5; 4975 } 4976 if (clamp) { 4977 if (y > 1) { 4978 y = 1; 4979 } else if (y < 0) { 4980 y = 0; 4981 } 4982 } 4983 a[cnt++] = {x:x, y:y}; 4984 } 4985 a.sort(function(a, b) { 4986 return a.x - b.x; 4987 }); 4988 4989 pnt = new EasePoint(1, 1, null); 4990 i = points; 4991 while (--i > -1) { 4992 obj = a[i]; 4993 pnt = new EasePoint(obj.x, obj.y, pnt); 4994 } 4995 4996 this._prev = new EasePoint(0, 0, (pnt.t !== 0) ? pnt : pnt.next); 4997 }, true); 4998 p = RoughEase.prototype = new Ease(); 4999 p.constructor = RoughEase; 5000 p.getRatio = function(p) { 5001 var pnt = this._prev; 5002 if (p > pnt.t) { 5003 while (pnt.next && p >= pnt.t) { 5004 pnt = pnt.next; 5005 } 5006 pnt = pnt.prev; 5007 } else { 5008 while (pnt.prev && p <= pnt.t) { 5009 pnt = pnt.prev; 5010 } 5011 } 5012 this._prev = pnt; 5013 return (pnt.v + ((p - pnt.t) / pnt.gap) * pnt.c); 5014 }; 5015 p.config = function(vars) { 5016 return new RoughEase(vars); 5017 }; 5018 RoughEase.ease = new RoughEase(); 5019 5020 5021 //Bounce 5022 _wrap("Bounce", 5023 _create("BounceOut", function(p) { 5024 if (p < 1 / 2.75) { 5025 return 7.5625 * p * p; 5026 } else if (p < 2 / 2.75) { 5027 return 7.5625 * (p -= 1.5 / 2.75) * p + 0.75; 5028 } else if (p < 2.5 / 2.75) { 5029 return 7.5625 * (p -= 2.25 / 2.75) * p + 0.9375; 5030 } 5031 return 7.5625 * (p -= 2.625 / 2.75) * p + 0.984375; 5032 }), 5033 _create("BounceIn", function(p) { 5034 if ((p = 1 - p) < 1 / 2.75) { 5035 return 1 - (7.5625 * p * p); 5036 } else if (p < 2 / 2.75) { 5037 return 1 - (7.5625 * (p -= 1.5 / 2.75) * p + 0.75); 5038 } else if (p < 2.5 / 2.75) { 5039 return 1 - (7.5625 * (p -= 2.25 / 2.75) * p + 0.9375); 5040 } 5041 return 1 - (7.5625 * (p -= 2.625 / 2.75) * p + 0.984375); 5042 }), 5043 _create("BounceInOut", function(p) { 5044 var invert = (p < 0.5); 5045 if (invert) { 5046 p = 1 - (p * 2); 5047 } else { 5048 p = (p * 2) - 1; 5049 } 5050 if (p < 1 / 2.75) { 5051 p = 7.5625 * p * p; 5052 } else if (p < 2 / 2.75) { 5053 p = 7.5625 * (p -= 1.5 / 2.75) * p + 0.75; 5054 } else if (p < 2.5 / 2.75) { 5055 p = 7.5625 * (p -= 2.25 / 2.75) * p + 0.9375; 5056 } else { 5057 p = 7.5625 * (p -= 2.625 / 2.75) * p + 0.984375; 5058 } 5059 return invert ? (1 - p) * 0.5 : p * 0.5 + 0.5; 5060 }) 5061 ); 5062 5063 5064 //CIRC 5065 _wrap("Circ", 5066 _create("CircOut", function(p) { 5067 return Math.sqrt(1 - (p = p - 1) * p); 5068 }), 5069 _create("CircIn", function(p) { 5070 return -(Math.sqrt(1 - (p * p)) - 1); 5071 }), 5072 _create("CircInOut", function(p) { 5073 return ((p*=2) < 1) ? -0.5 * (Math.sqrt(1 - p * p) - 1) : 0.5 * (Math.sqrt(1 - (p -= 2) * p) + 1); 5074 }) 5075 ); 5076 5077 5078 //Elastic 5079 _createElastic = function(n, f, def) { 5080 var C = _class("easing." + n, function(amplitude, period) { 5081 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. 5082 this._p2 = (period || def) / (amplitude < 1 ? amplitude : 1); 5083 this._p3 = this._p2 / _2PI * (Math.asin(1 / this._p1) || 0); 5084 this._p2 = _2PI / this._p2; //precalculate to optimize 5085 }, true), 5086 p = C.prototype = new Ease(); 5087 p.constructor = C; 5088 p.getRatio = f; 5089 p.config = function(amplitude, period) { 5090 return new C(amplitude, period); 5091 }; 5092 return C;
5093 }; 5094 _wrap("Elastic", 5095 _createElastic("ElasticOut", function(p) { 5096 return this._p1 * Math.pow(2, -10 * p) * Math.sin( (p - this._p3) * this._p2 ) + 1; 5097 }, 0.3), 5098 _createElastic("ElasticIn", function(p) { 5099 return -(this._p1 * Math.pow(2, 10 * (p -= 1)) * Math.sin( (p - this._p3) * this._p2 )); 5100 }, 0.3), 5101 _createElastic("ElasticInOut", function(p) { 5102 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; 5103 }, 0.45) 5104 ); 5105 5106 5107 //Expo 5108 _wrap("Expo", 5109 _create("ExpoOut", function(p) { 5110 return 1 - Math.pow(2, -10 * p); 5111 }), 5112 _create("ExpoIn", function(p) { 5113 return Math.pow(2, 10 * (p - 1)) - 0.001; 5114 }), 5115 _create("ExpoInOut", function(p) { 5116 return ((p *= 2) < 1) ? 0.5 * Math.pow(2, 10 * (p - 1)) : 0.5 * (2 - Math.pow(2, -10 * (p - 1))); 5117 }) 5118 ); 5119 5120 5121 //Sine 5122 _wrap("Sine", 5123 _create("SineOut", function(p) { 5124 return Math.sin(p * _HALF_PI); 5125 }), 5126 _create("SineIn", function(p) { 5127 return -Math.cos(p * _HALF_PI) + 1; 5128 }), 5129 _create("SineInOut", function(p) { 5130 return -0.5 * (Math.cos(Math.PI * p) - 1); 5131 }) 5132 ); 5133 5134 _class("easing.EaseLookup", { 5135 find:function(s) { 5136 return Ease.map[s]; 5137 } 5138 }, true); 5139 5140 //register the non-standard eases 5141 _easeReg(w.SlowMo, "SlowMo", "ease,"); 5142 _easeReg(RoughEase, "RoughEase", "ease,"); 5143 _easeReg(SteppedEase, "SteppedEase", "ease,"); 5144 5145 return Back; 5146 5147 }, true); 5148 5149}); if (_gsScope._gsDefine) { _gsScope._gsQueue.pop()(); } 5150 5151 5152/*! 5153 * VERSION: 1.18.0 5154 * DATE: 2015-09-05 5155 * UPDATES AND DOCS AT: http://greensock.com 5156 * 5157 * @license Copyright (c) 2008-2015, GreenSock. All rights reserved. 5158 * This work is subject to the terms at http://greensock.com/standard-license or for 5159 * Club GreenSock members, the software agreement that was issued with your membership. 5160 * 5161 * @author: Jack Doyle, [email protected] 5162 */ 5163var _gsScope = (typeof(module) !== "undefined" && module.exports && typeof(global) !== "undefined") ? global : this || window; //helps ensure compatibility with AMD/RequireJS and CommonJS/Node 5164(_gsScope._gsQueue || (_gsScope._gsQueue = [])).push( function() { 5165 5166 "use strict"; 5167 5168 _gsScope._gsDefine("plugins.CSSPlugin", ["plugins.TweenPlugin","TweenLite"], function(TweenPlugin, TweenLite) { 5169 5170 /** @constructor **/ 5171 var CSSPlugin = function() { 5172 TweenPlugin.call(this, "css"); 5173 this._overwriteProps.length = 0; 5174 this.setRatio = CSSPlugin.prototype.setRatio; //speed optimization (avoid prototype lookup on this "hot" method) 5175 }, 5176 _globals = _gsScope._gsDefine.globals, 5177 _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. 5178 _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 5179 _cs, //computed style (we store this in a shared variable to conserve memory and make minification tighter 5180 _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. 5181 _specialProps = {}, 5182 p = CSSPlugin.prototype = new TweenPlugin("css"); 5183 5184 p.constructor = CSSPlugin; 5185 CSSPlugin.version = "1.18.0"; 5186 CSSPlugin.API = 2; 5187 CSSPlugin.defaultTransformPerspective = 0; 5188 CSSPlugin.defaultSkewType = "compensated"; 5189 CSSPlugin.defaultSmoothOrigin = true; 5190 p = "px"; //we'll reuse the "p" variable to keep file size down 5191 CSSPlugin.suffixMap = {top:p, right:p, bottom:p, left:p, width:p, height:p, fontSize:p, padding:p, margin:p, perspective:p, lineHeight:""}; 5192 5193 5194 var _numExp = /(?:\d|\-\d|\.\d|\-\.\d)+/g, 5195 _relNumExp = /(?:\d|\-\d|\.\d|\-\.\d|\+=\d|\-=\d|\+=.\d|\-=\.\d)+/g,
5196 _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)" 5197 _NaNExp = /(?![+-]?\d*\.?\d+|[+-]|e[+-]\d+)[^0-9]/g, //also allows scientific notation and doesn't kill the leading -/+ in -= and += 5198 _suffixExp = /(?:\d|\-|\+|=|#|\.)*/g, 5199 _opacityExp = /opacity *= *([^)]*)/i, 5200 _opacityValExp = /opacity:([^;]*)/i, 5201 _alphaFilterExp = /alpha\(opacity *=.+?\)/i, 5202 _rgbhslExp = /^(rgb|hsl)/, 5203 _capsExp = /([A-Z])/g, 5204 _camelExp = /-([a-z])/gi, 5205 _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) 5206 _camelFunc = function(s, g) { return g.toUpperCase(); }, 5207 _horizExp = /(?:Left|Right|Width)/i, 5208 _ieGetMatrixExp = /(M11|M12|M21|M22)=[\d\-\.e]+/gi, 5209 _ieSetMatrixExp = /progid\:DXImageTransform\.Microsoft\.Matrix\(.+?\)/i, 5210 _commasOutsideParenExp = /,(?=[^\)]*(?:\(|$))/gi, //finds any commas that are not within parenthesis 5211 _DEG2RAD = Math.PI / 180, 5212 _RAD2DEG = 180 / Math.PI, 5213 _forcePT = {}, 5214 _doc = document, 5215 _createElement = function(type) { 5216 return _doc.createElementNS ? _doc.createElementNS("http://www.w3.org/1999/xhtml", type) : _doc.createElement(type); 5217 }, 5218 _tempDiv = _createElement("div"), 5219 _tempImg = _createElement("img"), 5220 _internals = CSSPlugin._internals = {_specialProps:_specialProps}, //provides a hook to a few internal methods that we need to access from inside other plugins 5221 _agent = navigator.userAgent, 5222 _autoRound, 5223 _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). 5224 5225 _isSafari, 5226 _isFirefox, //Firefox has a bug that causes 3D transformed elements to randomly disappear unless a repaint is forced after each update on each element. 5227 _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!) 5228 _ieVers, 5229 _supportsOpacity = (function() { //we set _isSafari, _ieVers, _isFirefox, and _supportsOpacity all in one function here to reduce file size slightly, especially in the minified version. 5230 var i = _agent.indexOf("Android"), 5231 a = _createElement("a"); 5232 _isSafari = (_agent.indexOf("Safari") !== -1 && _agent.indexOf("Chrome") === -1 && (i === -1 || Number(_agent.substr(i+8, 1)) > 3)); 5233 _isSafariLT6 = (_isSafari && (Number(_agent.substr(_agent.indexOf("Version/")+8, 1)) < 6)); 5234 _isFirefox = (_agent.indexOf("Firefox") !== -1); 5235 if ((/MSIE ([0-9]{1,}[\.0-9]{0,})/).exec(_agent) || (/Trident\/.*rv:([0-9]{1,}[\.0-9]{0,})/).exec(_agent)) { 5236 _ieVers = parseFloat( RegExp.$1 ); 5237 } 5238 if (!a) { 5239 return false; 5240 } 5241 a.style.cssText = "top:1px;opacity:.55;"; 5242 return /^0.55/.test(a.style.opacity); 5243 }()), 5244 _getIEOpacity = function(v) { 5245 return (_opacityExp.test( ((typeof(v) === "string") ? v : (v.currentStyle ? v.currentStyle.filter : v.style.filter) || "") ) ? ( parseFloat( RegExp.$1 ) / 100 ) : 1); 5246 }, 5247 _log = function(s) {//for logging messages, but in a way that won't throw errors in old versions of IE. 5248 if (window.console) { 5249 console.log(s); 5250 } 5251 }, 5252 5253 _prefixCSS = "", //the non-camelCase vendor prefix like "-o-", "-moz-", "-ms-", or "-webkit-" 5254 _prefix = "", //camelCase vendor prefix like "O", "ms", "Webkit", or "Moz". 5255 5256 // @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) 5257 _checkPropPrefix = function(p, e) { 5258 e = e || _tempDiv; 5259 var s = e.style, 5260 a, i; 5261 if (s[p] !== undefined) { 5262 return p; 5263 } 5264 p = p.charAt(0).toUpperCase() + p.substr(1); 5265 a = ["O","Moz","ms","Ms","Webkit"]; 5266 i = 5; 5267 while (--i > -1 && s[a[i]+p] === undefined) { } 5268 if (i >= 0) { 5269 _prefix = (i === 3) ? "ms" : a[i]; 5270 _prefixCSS = "-" + _prefix.toLowerCase() + "-"; 5271 return _prefix + p; 5272 } 5273 return null; 5274 }, 5275 5276 _getComputedStyle = _doc.defaultView ? _doc.defaultView.getComputedStyle : function() {}, 5277 5278 /** 5279 * @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: 5280 * var currentLeft = CSSPlugin.getStyle( document.getElementById("myElement"), "left"); 5281 * 5282 * @param {!Object} t Target element whose style property you want to query 5283 * @param {!string} p Property name (like "left" or "top" or "marginTop", etc.) 5284 * @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. 5285 * @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. 5286 * @param {string=} dflt Default value that should be returned in the place of null, "none", "auto" or "auto auto". 5287 * @return {?string} The current property value 5288 */ 5289 _getStyle = CSSPlugin.getStyle = function(t, p, cs, calc, dflt) { 5290 var rv; 5291 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. 5292 return _getIEOpacity(t); 5293 } 5294 if (!calc && t.style[p]) { 5295 rv = t.style[p]; 5296 } else if ((cs = cs || _getComputedStyle(t))) { 5297 rv = cs[p] || cs.getPropertyValue(p) || cs.getPropertyValue(p.replace(_capsExp, "-$1").toLowerCase()); 5298 } else if (t.currentStyle) { 5299 rv = t.currentStyle[p]; 5300 } 5301 return (dflt != null && (!rv || rv === "none" || rv === "auto" || rv === "auto auto")) ? dflt : rv; 5302 }, 5303 5304 /** 5305 * @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. 5306 * @param {!Object} t Target element 5307 * @param {!string} p Property name (like "left", "top", "marginLeft", etc.) 5308 * @param {!number} v Value 5309 * @param {string=} sfx Suffix (like "px" or "%" or "em") 5310 * @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. 5311 * @return {number} value in pixels 5312 */ 5313 _convertToPixels = _internals.convertToPixels = function(t, p, v, sfx, recurse) { 5314 if (sfx === "px" || !sfx) { return v; } 5315 if (sfx === "auto" || !v) { return 0; } 5316 var horiz = _horizExp.test(p), 5317 node = t, 5318 style = _tempDiv.style, 5319 neg = (v < 0), 5320 pix, cache, time; 5321 if (neg) { 5322 v = -v; 5323 } 5324 if (sfx === "%" && p.indexOf("border") !== -1) { 5325 pix = (v / 100) * (horiz ? t.clientWidth : t.clientHeight); 5326 } else { 5327 style.cssText = "border:0 solid red;position:" + _getStyle(t, "position") + ";line-height:0;"; 5328 if (sfx === "%" || !node.appendChild || sfx.charAt(0) === "v" || sfx === "rem") { 5329 node = t.parentNode || _doc.body; 5330 cache = node._gsCache; 5331 time = TweenLite.ticker.frame; 5332 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) 5333 return cache.width * v / 100; 5334 } 5335 style[(horiz ? "width" : "height")] = v + sfx; 5336 } else { 5337 style[(horiz ? "borderLeftWidth" : "borderTopWidth")] = v + sfx; 5338 } 5339 node.appendChild(_tempDiv); 5340 pix = parseFloat(_tempDiv[(horiz ? "offsetWidth" : "offsetHeight")]); 5341 node.removeChild(_tempDiv); 5342 if (horiz && sfx === "%" && CSSPlugin.cacheWidths !== false) { 5343 cache = node._gsCache = node._gsCache || {}; 5344 cache.time = time; 5345 cache.width = pix / v * 100; 5346 } 5347 if (pix === 0 && !recurse) { 5348 pix = _convertToPixels(t, p, v, sfx, true); 5349 } 5350 } 5351 return neg ? -pix : pix; 5352 }, 5353 _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 5354 if (_getStyle(t, "position", cs) !== "absolute") { return 0; } 5355 var dim = ((p === "left") ? "Left" : "Top"),
5356 v = _getStyle(t, "margin" + dim, cs); 5357 return t["offset" + dim] - (_convertToPixels(t, p, parseFloat(v), v.replace(_suffixExp, "")) || 0); 5358 }, 5359 5360 // @private returns at object containing ALL of the style properties in camelCase and their associated values. 5361 _getAllStyles = function(t, cs) { 5362 var s = {}, 5363 i, tr, p; 5364 if ((cs = cs || _getComputedStyle(t, null))) { 5365 if ((i = cs.length)) { 5366 while (--i > -1) { 5367 p = cs[i]; 5368 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. 5369 s[p.replace(_camelExp, _camelFunc)] = cs.getPropertyValue(p); 5370 } 5371 } 5372 } else { //some browsers behave differently - cs.length is always 0, so we must do a for...in loop. 5373 for (i in cs) { 5374 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. 5375 s[i] = cs[i]; 5376 } 5377 } 5378 } 5379 } else if ((cs = t.currentStyle || t.style)) { 5380 for (i in cs) { 5381 if (typeof(i) === "string" && s[i] === undefined) { 5382 s[i.replace(_camelExp, _camelFunc)] = cs[i]; 5383 } 5384 } 5385 } 5386 if (!_supportsOpacity) { 5387 s.opacity = _getIEOpacity(t); 5388 } 5389 tr = _getTransform(t, cs, false); 5390 s.rotation = tr.rotation; 5391 s.skewX = tr.skewX; 5392 s.scaleX = tr.scaleX; 5393 s.scaleY = tr.scaleY; 5394 s.x = tr.x; 5395 s.y = tr.y; 5396 if (_supports3D) { 5397 s.z = tr.z; 5398 s.rotationX = tr.rotationX; 5399 s.rotationY = tr.rotationY; 5400 s.scaleZ = tr.scaleZ; 5401 } 5402 if (s.filters) { 5403 delete s.filters; 5404 } 5405 return s; 5406 }, 5407 5408 // @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
5408ontaining 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. 5409 _cssDif = function(t, s1, s2, vars, forceLookup) { 5410 var difs = {}, 5411 style = t.style, 5412 val, p, mpt; 5413 for (p in s2) { 5414 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") { 5415 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. 5416 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. 5417 mpt = new MiniPropTween(style, p, style[p], mpt); 5418 } 5419 } 5420 } 5421 if (vars) { 5422 for (p in vars) { //copy properties (except className) 5423 if (p !== "className") { 5424 difs[p] = vars[p]; 5425 } 5426 } 5427 } 5428 return {difs:difs, firstMPT:mpt}; 5429 }, 5430 _dimensions = {width:["Left","Right"], height:["Top","Bottom"]}, 5431 _margins = ["marginLeft","marginRight","marginTop","marginBottom"], 5432 5433 /** 5434 * @private Gets the width or height of an element 5435 * @param {!Object} t Target element 5436 * @param {!string} p Property name ("width" or "height") 5437 * @param {Object=} cs Computed style object (if one exists). Just a speed optimization. 5438 * @return {number} Dimension (in pixels) 5439 */ 5440 _getDimension = function(t, p, cs) { 5441 var v = parseFloat((p === "width") ? t.offsetWidth : t.offsetHeight), 5442 a = _dimensions[p], 5443 i = a.length; 5444 cs = cs || _getComputedStyle(t, null); 5445 while (--i > -1) { 5446 v -= parseFloat( _getStyle(t, "padding" + a[i], cs, true) ) || 0; 5447 v -= parseFloat( _getStyle(t, "border" + a[i] + "Width", cs, true) ) || 0; 5448 } 5449 return v; 5450 }, 5451 5452 // @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) 5453 _parsePosition = function(v, recObj) { 5454 if (v === "contain" || v === "auto" || v === "auto auto") { 5455 return v + " "; 5456 } 5457 if (v == null || v === "") { //note: Firefox uses "auto auto" as default whereas Chrome uses "auto". 5458 v = "0 0"; 5459 } 5460 var a = v.split(" "), 5461 x = (v.indexOf("left") !== -1) ? "0%" : (v.indexOf("right") !== -1) ? "100%" : a[0], 5462 y = (v.indexOf("top") !== -1) ? "0%" : (v.indexOf("bottom") !== -1) ? "100%" : a[1]; 5463 if (y == null) { 5464 y = (x === "center") ? "50%" : "0"; 5465 } else if (y === "center") { 5466 y = "50%"; 5467 } 5468 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
5468ould be used to describe horizontal or vertical, hence the isNaN(). If there's an "=" sign in the value, it's relative. 5469 x = "50%"; 5470 } 5471 v = x + " " + y + ((a.length > 2) ? " " + a[2] : ""); 5472 if (recObj) { 5473 recObj.oxp = (x.indexOf("%") !== -1); 5474 recObj.oyp = (y.indexOf("%") !== -1); 5475 recObj.oxr = (x.charAt(1) === "="); 5476 recObj.oyr = (y.charAt(1) === "="); 5477 recObj.ox = parseFloat(x.replace(_NaNExp, "")); 5478 recObj.oy = parseFloat(y.replace(_NaNExp, "")); 5479 recObj.v = v; 5480 } 5481 return recObj || v; 5482 }, 5483 5484 /** 5485 * @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!) 5486 * @param {(number|string)} e End value which is typically a string, but could be a number 5487 * @param {(number|string)} b Beginning value which is typically a string but could be a number 5488 * @return {number} Amount of change between the beginning and ending values (relative values that have a "+=" or "-=" are recognized) 5489 */ 5490 _parseChange = function(e, b) { 5491 return (typeof(e) === "string" && e.charAt(1) === "=") ? parseInt(e.charAt(0) + "1", 10) * parseFloat(e.substr(2)) : parseFloat(e) - parseFloat(b); 5492 }, 5493 5494 /** 5495 * @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. 5496 * @param {Object} v Value to be parsed 5497 * @param {!number} d Default value (which is also used for relative calculations if "+=" or "-=" is found in the first parameter) 5498 * @return {number} Parsed value 5499 */ 5500 _parseVal = function(v, d) { 5501 return (v == null) ? d : (typeof(v) === "string" && v.charAt(1) === "=") ? parseInt(v.charAt(0) + "1", 10) * parseFloat(v.substr(2)) + d : parseFloat(v); 5502 }, 5503 5504 /** 5505 * @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. 5506 * @param {Object} v Value to be parsed 5507 * @param {!number} d Default value (which is also used for relative calculations if "+=" or "-=" is found in the first parameter) 5508 * @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" 5509 * @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. 5510 * @return {number} parsed angle in radians 5511 */ 5512 _parseAngle = function(v, d, p, directionalEnd) { 5513 var min = 0.000001, 5514 cap, split, dif, result, isRelative; 5515 if (v == null) { 5516 result = d; 5517 } else if (typeof(v) === "number") { 5518 result = v; 5519 } else { 5520 cap = 360; 5521 split = v.split("_"); 5522 isRelative = (v.charAt(1) === "="); 5523 dif = (isRelative ? parseInt(v.charAt(0) + "1", 10) * parseFloat(split[0].substr(2)) : parseFloat(split[0])) * ((v.indexOf("rad") === -1) ? 1 : _RAD2DEG) - (isRelative ? 0 : d); 5524 if (split.length) { 5525 if (directionalEnd) { 5526 directionalEnd[p] = d + dif; 5527 } 5528 if (v.indexOf("short") !== -1) { 5529 dif = dif % cap; 5530 if (dif !== dif % (cap / 2)) { 5531 dif = (dif < 0) ? dif + cap : dif - cap; 5532 } 5533 } 5534 if (v.indexOf("_cw") !== -1 && dif < 0) { 5535 dif = ((dif + cap * 9999999999) % cap) - ((dif / cap) | 0) * cap; 5536 } else if (v.indexOf("ccw") !== -1 && dif > 0) { 5537 dif = ((dif - cap * 9999999999) % cap) - ((dif / cap) | 0) * cap; 5538 } 5539 } 5540 result = d + dif; 5541 } 5542 if (result < min && result > -min) { 5543 result = 0; 5544 } 5545 return result; 5546 }, 5547 5548 _colorLookup = {aqua:[0,255,255], 5549 lime:[0,255,0], 5550 silver:[192,192,192], 5551 black:[0,0,0], 5552 maroon:[128,0,0], 5553 teal:[0,128,128], 5554 blue:[0,0,255], 5555 navy:[0,0,128], 5556 white:[255,255,255], 5557 fuchsia:[255,0,255], 5558 olive:[128,128,0], 5559 yellow:[255,255,0], 5560 orange:[255,165,0], 5561 gray:[128,128,128], 5562 purple:[128,0,128], 5563 green:[0,128,0], 5564 red:[255,0,0], 5565 pink:[255,192,203], 5566 cyan:[0,255,255], 5567 transparent:[255,255,255,0]}, 5568 5569 _hue = function(h, m1, m2) { 5570 h = (h < 0) ? h + 1 : (h > 1) ? h - 1 : h; 5571 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; 5572 }, 5573 5574 /** 5575 * @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). 5576 * @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. 5577 * @param {(boolean)} toHSL If true, an hsl() or hsla() value will be returned instead of rgb() or rgba() 5578 * @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. 5579 */ 5580 _parseColor = CSSPlugin.parseColor = function(v, toHSL) { 5581 var a, r, g, b, h, s, l, max, min, d, wasHSL; 5582 if (!v) { 5583 a = _colorLookup.black; 5584 } else if (typeof(v) === "number") { 5585 a = [v >> 16, (v >> 8) & 255, v & 255]; 5586 } else { 5587 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. 5588 v = v.substr(0, v.length - 1); 5589 } 5590 if (_colorLookup[v]) { 5591 a = _colorLookup[v]; 5592 } else if (v.charAt(0) === "#") { 5593 if (v.length === 4) { //for shorthand like #9F0 5594 r = v.charAt(1); 5595 g = v.charAt(2); 5596 b = v.charAt(3); 5597 v = "#" + r + r + g + g + b + b; 5598 } 5599 v = parseInt(v.substr(1), 16); 5600 a = [v >> 16, (v >> 8) & 255, v & 255]; 5601 } else if (v.substr(0, 3) === "hsl") { 5602 a = wasHSL = v.match(_numExp); 5603 if (!toHSL) { 5604 h = (Number(a[0]) % 360) / 360;
5605 s = Number(a[1]) / 100; 5606 l = Number(a[2]) / 100; 5607 g = (l <= 0.5) ? l * (s + 1) : l + s - l * s; 5608 r = l * 2 - g; 5609 if (a.length > 3) { 5610 a[3] = Number(v[3]); 5611 } 5612 a[0] = _hue(h + 1 / 3, r, g); 5613 a[1] = _hue(h, r, g); 5614 a[2] = _hue(h - 1 / 3, r, g); 5615 } else if (v.indexOf("=") !== -1) { //if relative values are found, just return the raw strings with the relative prefixes in place. 5616 return v.match(_relNumExp); 5617 } 5618 } else { 5619 a = v.match(_numExp) || _colorLookup.transparent; 5620 } 5621 a[0] = Number(a[0]); 5622 a[1] = Number(a[1]); 5623 a[2] = Number(a[2]); 5624 if (a.length > 3) { 5625 a[3] = Number(a[3]); 5626 } 5627 } 5628 if (toHSL && !wasHSL) { 5629 r = a[0] / 255; 5630 g = a[1] / 255; 5631 b = a[2] / 255; 5632 max = Math.max(r, g, b); 5633 min = Math.min(r, g, b); 5634 l = (max + min) / 2; 5635 if (max === min) { 5636 h = s = 0; 5637 } else { 5638 d = max - min; 5639 s = l > 0.5 ? d / (2 - max - min) : d / (max + min); 5640 h = (max === r) ? (g - b) / d + (g < b ? 6 : 0) : (max === g) ? (b - r) / d + 2 : (r - g) / d + 4; 5641 h *= 60; 5642 } 5643 a[0] = (h + 0.5) | 0; 5644 a[1] = (s * 100 + 0.5) | 0; 5645 a[2] = (l * 100 + 0.5) | 0; 5646 } 5647 return a; 5648 }, 5649 _formatColors = function(s, toHSL) { 5650 var colors = s.match(_colorExp) || [], 5651 charIndex = 0, 5652 parsed = colors.length ? "" : s, 5653 i, color, temp; 5654 for (i = 0; i < colors.length; i++) { 5655 color = colors[i]; 5656 temp = s.substr(charIndex, s.indexOf(color, charIndex)-charIndex); 5657 charIndex += temp.length + color.length; 5658 color = _parseColor(color, toHSL); 5659 if (color.length === 3) { 5660 color.push(1); 5661 } 5662 parsed += temp + (toHSL ? "hsla(" + color[0] + "," + color[1] + "%," + color[2] + "%," + color[3] : "rgba(" + color.join(",")) + ")"; 5663 } 5664 return parsed; 5665 }, 5666 _colorExp = "(?:\\b(?:(?:rgb|rgba|hsl|hsla)\\(.+?\\))|\\B#.+?\\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. 5667 5668 for (p in _colorLookup) { 5669 _colorExp += "|" + p + "\\b"; 5670 } 5671 _colorExp = new RegExp(_colorExp+")", "gi"); 5672 5673 CSSPlugin.colorStringFilter = function(a) { 5674 var combined = a[0] + a[1], 5675 toHSL; 5676 _colorExp.lastIndex = 0; 5677 if (_colorExp.test(combined)) { 5678 toHSL = (combined.indexOf("hsl(") !== -1 || combined.indexOf("hsla(") !== -1); 5679 a[0] = _formatColors(a[0], toHSL); 5680 a[1] = _formatColors(a[1], toHSL); 5681 } 5682 }; 5683 5684 if (!TweenLite.defaultStringFilter) { 5685 TweenLite.defaultStringFilter = CSSPlugin.colorStringFilter; 5686 } 5687 5688 /** 5689 * @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. 5690 * @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. 5691 * @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. 5692 * @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. 5693 * @return {Function} formatter function 5694 */ 5695 var _getFormatter = function(dflt, clr, collapsible, multi) { 5696 if (dflt == null) { 5697 return function(v) {return v;}; 5698 } 5699 var dColor = clr ? (dflt.match(_colorExp) || [""])[0] : "", 5700 dVals = dflt.split(dColor).join("").match(_valuesExp) || [], 5701 pfx = dflt.substr(0, dflt.indexOf(dVals[0])), 5702 sfx = (dflt.charAt(dflt.length - 1) === ")") ? ")" : "", 5703 delim = (dflt.indexOf(" ") !== -1) ? " " : ",", 5704 numVals = dVals.length, 5705 dSfx = (numVals > 0) ? dVals[0].replace(_numExp, "") : "", 5706 formatter; 5707 if (!numVals) { 5708 return function(v) {return v;}; 5709 } 5710 if (clr) { 5711 formatter = function(v) { 5712 var color, vals, i, a; 5713 if (typeof(v) === "number") { 5714 v += dSfx; 5715 } else if (multi && _commasOutsideParenExp.test(v)) { 5716 a = v.replace(_commasOutsideParenExp, "|").split("|"); 5717 for (i = 0; i < a.length; i++) { 5718 a[i] = formatter(a[i]); 5719 } 5720 return a.join(","); 5721 } 5722 color = (v.match(_colorExp) || [dColor])[0]; 5723 vals = v.split(color).join("").match(_valuesExp) || []; 5724 i = vals.length; 5725 if (numVals > i--) { 5726 while (++i < numVals) { 5727 vals[i] = collapsible ? vals[(((i - 1) / 2) | 0)] : dVals[i];
5728 } 5729 } 5730 return pfx + vals.join(delim) + delim + color + sfx + (v.indexOf("inset") !== -1 ? " inset" : ""); 5731 }; 5732 return formatter; 5733 5734 } 5735 formatter = function(v) { 5736 var vals, a, i; 5737 if (typeof(v) === "number") { 5738 v += dSfx; 5739 } else if (multi && _commasOutsideParenExp.test(v)) { 5740 a = v.replace(_commasOutsideParenExp, "|").split("|"); 5741 for (i = 0; i < a.length; i++) { 5742 a[i] = formatter(a[i]); 5743 } 5744 return a.join(","); 5745 } 5746 vals = v.match(_valuesExp) || []; 5747 i = vals.length; 5748 if (numVals > i--) { 5749 while (++i < numVals) { 5750 vals[i] = collapsible ? vals[(((i - 1) / 2) | 0)] : dVals[i]; 5751 } 5752 } 5753 return pfx + vals.join(delim) + sfx; 5754 }; 5755 return formatter; 5756 }, 5757 5758 /** 5759 * @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. 5760 * @param {!string} props a comma-delimited list of property names in order from top to left, like "marginTop,marginRight,marginBottom,marginLeft" 5761 * @return {Function} a formatter function 5762 */ 5763 _getEdgeParser = function(props) { 5764 props = props.split(","); 5765 return function(t, e, p, cssp, pt, plugin, vars) { 5766 var a = (e + "").split(" "), 5767 i; 5768 vars = {}; 5769 for (i = 0; i < 4; i++) { 5770 vars[props[i]] = a[i] = a[i] || a[(((i - 1) / 2) >> 0)]; 5771 } 5772 return cssp.parse(t, vars, pt, plugin); 5773 }; 5774 }, 5775 5776 // @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. 5777 _setPluginRatio = _internals._setPluginRatio = function(v) { 5778 this.plugin.setRatio(v); 5779 var d = this.data, 5780 proxy = d.proxy, 5781 mpt = d.firstMPT, 5782 min = 0.000001, 5783 val, pt, i, str; 5784 while (mpt) { 5785 val = proxy[mpt.v]; 5786 if (mpt.r) { 5787 val = Math.round(val); 5788 } else if (val < min && val > -min) { 5789 val = 0; 5790 } 5791 mpt.t[mpt.p] = val; 5792 mpt = mpt._next; 5793 } 5794 if (d.autoRotate) { 5795 d.autoRotate.rotation = proxy.rotation; 5796 } 5797 //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. 5798 if (v === 1) { 5799 mpt = d.firstMPT; 5800 while (mpt) { 5801 pt = mpt.t; 5802 if (!pt.type) { 5803 pt.e = pt.s + pt.xs0; 5804 } else if (pt.type === 1) { 5805 str = pt.xs0 + pt.s + pt.xs1; 5806 for (i = 1; i < pt.l; i++) { 5807 str += pt["xn"+i] + pt["xs"+(i+1)]; 5808 } 5809 pt.e = str; 5810 } 5811 mpt = mpt._next; 5812 } 5813 } 5814 }, 5815 5816 /** 5817 * @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. 5818 * @param {!Object} t target object whose property we're tweening (often a CSSPropTween) 5819 * @param {!string} p property name 5820 * @param {(number|string|object)} v value 5821 * @param {MiniPropTween=} next next MiniPropTween in the linked list 5822 * @param {boolean=} r if true, the tweened value should be rounded to the nearest integer 5823 */ 5824 MiniPropTween = function(t, p, v, next, r) { 5825 this.t = t; 5826 this.p = p; 5827 this.v = v; 5828 this.r = r; 5829 if (next) { 5830 next._prev = this; 5831 this._next = next; 5832 } 5833 }, 5834 5835 /** 5836 * @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 roun
5836ded. 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. 5837 * This method returns an object that has the following properties: 5838 * - 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 5839 * - 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 5840 * - firstMPT: the first MiniPropTween in the linked list 5841 * - 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. 5842 * @param {!Object} t target object to be tweened 5843 * @param {!(Object|string)} vars the object containing the information about the tweening values (typically the end/destination values) that should be parsed 5844 * @param {!CSSPlugin} cssp The CSSPlugin instance 5845 * @param {CSSPropTween=} pt the next CSSPropTween in the linked list 5846 * @param {TweenPlugin=} plugin the external TweenPlugin instance that will be handling tweening the numeric values 5847 * @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. 5848 * @return An object containing the following properties: proxy, end, firstMPT, and pt (see above for descriptions) 5849 */ 5850 _parseToProxy = _internals._parseToProxy = function(t, vars, cssp, pt, plugin, shallow) { 5851 var bpt = pt, 5852 start = {}, 5853 end = {}, 5854 transform = cssp._transform, 5855 oldForce = _forcePT, 5856 i, p, xp, mpt, firstPT; 5857 cssp._transform = null; 5858 _forcePT = vars; 5859 pt = firstPT = cssp.parse(t, vars, pt, plugin); 5860 _forcePT = oldForce; 5861 //break off from the linked list so the new ones are isolated. 5862 if (shallow) { 5863 cssp._transform = transform; 5864 if (bpt) { 5865 bpt._prev = null; 5866 if (bpt._prev) { 5867 bpt._prev._next = null; 5868 } 5869 } 5870 } 5871 while (pt && pt !== bpt) { 5872 if (pt.type <= 1) { 5873 p = pt.p; 5874 end[p] = pt.s + pt.c; 5875 start[p] = pt.s; 5876 if (!shallow) { 5877 mpt = new MiniPropTween(pt, "s", p, mpt, pt.r); 5878 pt.c = 0; 5879 } 5880 if (pt.type === 1) { 5881 i = pt.l; 5882 while (--i > 0) { 5883 xp = "xn" + i; 5884 p = pt.p + "_" + xp; 5885 end[p] = pt.data[xp]; 5886 start[p] = pt[xp]; 5887 if (!shallow) { 5888 mpt = new MiniPropTween(pt, xp, p, mpt, pt.rxp[xp]); 5889 } 5890 } 5891 } 5892 } 5893 pt = pt._next; 5894 } 5895 return {proxy:start, end:end, firstMPT:mpt, pt:firstPT}; 5896 }, 5897 5898 5899 5900 /** 5901 * @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. 5902 * CSSPropTweens have the following optional properties as well (not defined through the constructor): 5903 * - 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. 5904 * - 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) 5905 * - plugin: The TweenPlugin instance that will handle the tweening of any complex values. For example, sometimes we don't want to use normal subt
5905weens (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. 5906 * - 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. 5907 * - 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. 5908 * @param {!Object} t Target object whose property will be tweened. Often a DOM element, but not always. It could be anything. 5909 * @param {string} p Property to tween (name). For example, to tween element.width, p would be "width". 5910 * @param {number} s Starting numeric value 5911 * @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. 5912 * @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. 5913 * @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. 5914 * @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" 5915 * @param {boolean=} r If true, the value(s) should be rounded 5916 * @param {number=} pr Priority in the linked list order. Higher priority CSSPropTweens will be updated before lower priority ones. The default priority is 0. 5917 * @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. 5918 * @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. 5919 */ 5920 CSSPropTween = _internals.CSSPropTween = function(t, p, s, c, next, type, n, r, pr, b, e) { 5921 this.t = t; //target 5922 this.p = p; //property 5923 this.s = s; //starting value 5924 this.c = c; //change value 5925 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) 5926 if (!(t instanceof CSSPropTween)) { 5927 _overwriteProps.push(this.n); 5928 } 5929 this.r = r; //round (boolean) 5930 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 5931 if (pr) { 5932 this.pr = pr; 5933 _hasPriority = true; 5934 } 5935 this.b = (b === undefined) ? s : b; 5936 this.e = (e === undefined) ? s + c : e; 5937 if (next) { 5938 this._next = next; 5939 next._prev = this; 5940 } 5941 }, 5942 5943 _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
5944 var pt = new CSSPropTween(target, prop, start, end - start, next, -1, overwriteProp); 5945 pt.b = start; 5946 pt.e = pt.xs0 = end; 5947 return pt; 5948 }, 5949 5950 /** 5951 * 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: 5952 * 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); 5953 * 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(). 5954 * 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. 5955 * 5956 * @param {!Object} t Target whose property will be tweened 5957 * @param {!string} p Property that will be tweened (its name, like "left" or "backgroundColor" or "boxShadow") 5958 * @param {string} b Beginning value 5959 * @param {string} e Ending value 5960 * @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) 5961 * @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 5962 * @param {?CSSPropTween} pt CSSPropTween instance that is the current head of the linked list (we'll prepend to this). 5963 * @param {number=} pr Priority in the linked list order. Higher priority properties will be updated before lower priority ones. The default priority is 0. 5964 * @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} 5965 * @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. 5966 * @return {CSSPropTween} The first CSSPropTween in the linked list which includes the new one(s) added by the parseComplex() call. 5967 */ 5968 _parseComplex = CSSPlugin.parseComplex = function(t, p, b, e, clrs, dflt, pt, pr, plugin, setRatio) { 5969 //DEBUG: _log("parseComplex: "+p+", b: "+b+", e: "+e); 5970 b = b || dflt || ""; 5971 pt = new CSSPropTween(t, p, 0, 0, pt, (setRatio ? 2 : 1), null, false, pr, b, e); 5972 e += ""; //ensures it's a string 5973 var ba = b.split(", ").join(",").split(" "), //beginning array 5974 ea = e.split(", ").join(",").split(" "), //ending array 5975 l = ba.length, 5976 autoRound = (_autoRound !== false), 5977 i, xi, ni, bv, ev, bnums, enums, bn, hasAlpha, temp, cv, str, useHSL; 5978 if (e.indexOf(",") !== -1 || b.indexOf(",") !== -1) { 5979 ba = ba.join(" ").replace(_commasOutsideParenExp, ", ").split(" "); 5980 ea = ea.join(" ").replace(_commasOutsideParenExp, ", ").split(" "); 5981 l = ba.length; 5982 } 5983 if (l !== ea.length) { 5984 //DEBUG: _log("mismatched formatting detected on " + p + " (" + b + " vs " + e + ")"); 5985 ba = (dflt || "").split(" "); 5986 l = ba.length; 5987 } 5988 pt.plugin = plugin; 5989 pt.setRatio = setRatio; 5990 _colorExp.lastIndex = 0; 5991 for (i = 0; i < l; i++) { 5992 bv = ba[i]; 5993 ev = ea[i]; 5994 bn = parseFloat(bv); 5995 //if the value begins with a number (most common). It's fine if it has a suffix like px 5996 if (bn || bn === 0) { 5997 pt.appendXtra("", bn, _parseChange(ev, bn), ev.replace(_relNumExp, ""), (autoRound && ev.indexOf("px") !== -1), true); 5998 5999 //if the value is a color 6000 } else if (clrs && _colorExp.test(bv)) { 6001 str = ev.charAt(ev.length - 1) === "," ? ")," : ")"; //if there's a comma at the end, retain it. 6002 useHSL = (ev.indexOf("hsl") !== -1 && _supportsOpacity); 6003 bv = _parseColor(bv, useHSL); 6004 ev = _parseColor(ev, useHSL); 6005 hasAlpha = (bv.length + ev.length > 6); 6006 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 6007 pt["xs" + pt.l] += pt.l ? " transparent" : "transparent"; 6008 pt.e = pt.e.split(ea[i]).join("transparent"); 6009 } else { 6010 if (!_supportsOpacity) { //old versions of IE don't support rgba(). 6011 hasAlpha = false; 6012 } 6013 if (useHSL) { 6014 pt.appendXtra((hasAlpha ? "hsla(" : "hsl("), bv[0], _parseChange(ev[0], bv[0]), ",", false, true) 6015 .appendXtra("", bv[1], _parseChange(ev[1], bv[1]), "%,", false) 6016 .appendXtra("", bv[2], _parseChange(ev[2], bv[2]), (hasAlpha ? "%," : "%" + str), false); 6017 } else { 6018 pt.appendXtra((hasAlpha ? "rgba(" : "rgb("), bv[0], ev[0] - bv[0], ",", true, true) 6019 .appendXtra("", bv[1], ev[1] - bv[1], ",", true) 6020 .appendXtra("", bv[2], ev[2] - bv[2], (hasAlpha ? "," : str), true); 6021 } 6022 6023 if (hasAlpha) { 6024 bv = (bv.length < 4) ? 1 : bv[3]; 6025 pt.appendXtra("", bv, ((ev.length < 4) ? 1 : ev[3]) - bv, str, false); 6026 } 6027 } 6028 _colorExp.lastIndex = 0; //otherwise the test() on the RegExp could move the lastIndex and taint future results. 6029 6030 } else { 6031 bnums = bv.match(_numExp); //gets each group of numbers in the beginning value string and drops them into an array 6032 6033 //if no number is found, treat it as a non-tweening value and just append the string to the current xs. 6034 if (!bnums) { 6035 pt["xs" + pt.l] += pt.l ? " " + bv : bv; 6036 6037 //loop through all the numbers that are found and construct the extra values on the pt. 6038 } else { 6039 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 6040 if (!enums || enums.length !== bnums.length) { 6041 //DEBUG: _log("mismatched formatting detected on " + p + " (" + b + " vs " + e + ")"); 6042 return pt; 6043 } 6044 ni = 0; 6045 for (xi = 0; xi < bnums.length; xi++) { 6046 cv = bnums[xi]; 6047 temp = bv.indexOf(cv, ni); 6048 pt.appendXtra(bv.substr(ni, temp - ni), Number(cv), _parseChange(enums[xi], cv), "", (autoRound && bv.substr(temp + cv.length, 2) === "px"), (xi === 0)); 6049 ni = temp + cv.length; 6050 } 6051 pt["xs" + pt.l] += bv.substr(ni); 6052 } 6053 } 6054 } 6055 //if there are relative values ("+=" or "-=" prefix), we need to adjust the ending value to eliminate the prefixes and combine the values properly. 6056 if (e.indexOf("=") !== -1) if (pt.data) { 6057 str = pt.xs0 + pt.data.s; 6058 for (i = 1; i < pt.l; i++) { 6059 str += pt["xs" + i] + pt.data["xn" + i]; 6060 } 6061 pt.e = str + pt["xs" + i]; 6062 } 6063 if (!pt.l) { 6064 pt.type = -1; 6065 pt.xs0 = pt.e; 6066 } 6067 return pt.xfirst || pt; 6068 }, 6069 i = 9; 6070 6071 6072 p = CSSPropTween.prototype; 6073 p.l = p.pr = 0; //length (number of extra properties like xn1, xn2, xn3, etc. 6074 while (--i > 0) { 6075 p["xn" + i] = 0; 6076 p["xs" + i] = ""; 6077 } 6078 p.xs0 = ""; 6079 p._next = p._prev = p.xfirst = p.data = p.plugin = p.setRatio = p.rxp = null; 6080 6081 6082 /** 6083 * 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: 6084 * xs0:"rect(", s:10, xs1:"px, ", xn1:5, xs2:"px, ", xn2:0, xs3:"px, ", xn3:20, xn4:"px)" 6085 * And they'd all get joined together when the CSSPlugin renders (in the setRatio() method). 6086 * @param {string=} pfx Prefix (if any) 6087 * @param {!number} s Starting value 6088 * @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. 6089 * @param {string=} sfx Suffix (if any) 6090 * @param {boolean=} r Round (if true). 6091 * @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. 6092 * @return {CSSPropTween} returns itself so that multiple methods can be chained together. 6093 */ 6094 p.appendXtra = function(pfx, s, c, sfx, r, pad) { 6095 var pt = this, 6096 l = pt.l; 6097 pt["xs" + l] += (pad && l) ? " " + pfx : pfx || ""; 6098 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! 6099 pt["xs" + l] += s + (sfx || ""); 6100 return pt; 6101 } 6102 pt.l++; 6103 pt.type = pt.setRatio ? 2 : 1; 6104 pt["xs" + pt.l] = sfx || ""; 6105 if (l > 0) { 6106 pt.data["xn" + l] = s + c; 6107 pt.rxp["xn" + l] = r; //round extra property (we need to tap into this in the _parseToProxy() method) 6108 pt["xn" + l] = s; 6109 if (!pt.plugin) { 6110 pt.xfirst = new CSSPropTween(pt, "xn" + l, s, c, pt.xfirst || pt, 0, pt.n, r, pt.pr); 6111 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. 6112 } 6113 return pt; 6114 } 6115 pt.data = {s:s + c}; 6116 pt.rxp = {}; 6117 pt.s = s; 6118 pt.c = c; 6119 pt.r = r; 6120 return pt; 6121 }; 6122 6123 /** 6124 * @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. 6125 * @param {!string} p Property name (like "boxShadow" or "throwProps") 6126 * @param {Object=}
6126 options An object containing any of the following configuration options: 6127 * - defaultValue: the default value 6128 * - 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) 6129 * - 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.) 6130 * - prefix: if true, we'll determine whether or not this property requires a vendor prefix (like Webkit or Moz or ms or O) 6131 * - color: set this to true if the value for this SpecialProp may contain color-related values like rgb(), rgba(), etc. 6132 * - priority: priority in the linked list order. Higher priority SpecialProps will be updated before lower priority ones. The default priority is 0. 6133 * - multi: if true, the formatter should accommodate a comma-delimited list of values, like boxShadow could have multiple boxShadows listed out. 6134 * - 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. 6135 * - 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). 6136 */ 6137 var SpecialProp = function(p, options) { 6138 options = options || {}; 6139 this.p = options.prefix ? _checkPropPrefix(p) || p : p; 6140 _specialProps[p] = _specialProps[this.p] = this; 6141 this.format = options.formatter || _getFormatter(options.defaultValue, options.color, options.collapsible, options.multi); 6142 if (options.parser) { 6143 this.parse = options.parser; 6144 } 6145 this.clrs = options.color; 6146 this.multi = options.multi; 6147 this.keyword = options.keyword; 6148 this.dflt = options.defaultValue; 6149 this.pr = options.priority || 0; 6150 }, 6151 6152 //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. 6153 _registerComplexSpecialProp = _internals._registerComplexSpecialProp = function(p, options, defaults) { 6154 if (typeof(options) !== "object") { 6155 options = {parser:defaults}; //to make backwards compatible with older versions of BezierPlugin and ThrowPropsPlugin 6156 } 6157 var a = p.split(","), 6158 d = options.defaultValue, 6159 i, temp; 6160 defaults = defaults || [d]; 6161 for (i = 0; i < a.length; i++) { 6162 options.prefix = (i === 0 && options.prefix); 6163 options.defaultValue = defaults[i] || d; 6164 temp = new SpecialProp(a[i], options); 6165 } 6166 }, 6167 6168 //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. 6169 _registerPluginProp = function(p) { 6170 if (!_specialProps[p]) { 6171 var pluginName = p.charAt(0).toUpperCase() + p.substr(1) + "Plugin"; 6172 _registerComplexSpecialProp(p, {parser:function(t, e, p, cssp, pt, plugin, vars) { 6173 var pluginClass = _globals.com.greensock.plugins[pluginName]; 6174 if (!pluginClass) { 6175 _log("Error: " + pluginName + " js file not loaded."); 6176 return pt; 6177 } 6178 pluginClass._cssRegister(); 6179 return _specialProps[p].parse(t, e, p, cssp, pt, plugin, vars); 6180 }}); 6181 } 6182 }; 6183 6184 6185 p = SpecialProp.prototype; 6186 6187 /** 6188 * 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
6188adow) 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) 6189 * @param {!Object} t target element 6190 * @param {(string|number|object)} b beginning value 6191 * @param {(string|number|object)} e ending (destination) value 6192 * @param {CSSPropTween=} pt next CSSPropTween in the linked list 6193 * @param {TweenPlugin=} plugin If another plugin will be tweening the complex value, that TweenPlugin instance goes here. 6194 * @param {function=} setRatio If a custom setRatio() method should be used to handle this complex value, that goes here. 6195 * @return {CSSPropTween=} First CSSPropTween in the linked list 6196 */ 6197 p.parseComplex = function(t, b, e, pt, plugin, setRatio) { 6198 var kwd = this.keyword, 6199 i, ba, ea, l, bi, ei; 6200 //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) 6201 if (this.multi) if (_commasOutsideParenExp.test(e) || _commasOutsideParenExp.test(b)) { 6202 ba = b.replace(_commasOutsideParenExp, "|").split("|"); 6203 ea = e.replace(_commasOutsideParenExp, "|").split("|"); 6204 } else if (kwd) { 6205 ba = [b]; 6206 ea = [e]; 6207 } 6208 if (ea) { 6209 l = (ea.length > ba.length) ? ea.length : ba.length; 6210 for (i = 0; i < l; i++) { 6211 b = ba[i] = ba[i] || this.dflt; 6212 e = ea[i] = ea[i] || this.dflt; 6213 if (kwd) { 6214 bi = b.indexOf(kwd); 6215 ei = e.indexOf(kwd); 6216 if (bi !== ei) { 6217 if (ei === -1) { //if the keyword isn't in the end value, remove it from the beginning one. 6218 ba[i] = ba[i].split(kwd).join(""); 6219 } else if (bi === -1) { //if the keyword isn't in the beginning, add it. 6220 ba[i] += " " + kwd; 6221 } 6222 } 6223 } 6224 } 6225 b = ba.join(", "); 6226 e = ea.join(", "); 6227 } 6228 return _parseComplex(t, this.p, b, e, this.clrs, this.dflt, pt, this.pr, plugin, setRatio); 6229 }; 6230 6231 /** 6232 * 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: 6233 * this._firstPT = sp.parse(element, "5px 10px 20px rgb(2550,102,51)", "boxShadow", this); 6234 * 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). 6235 * @param {!Object} t Target object whose property is being tweened 6236 * @param {Object} e End value as provided in the vars object (typically a string, but not always - like a throwProps would be an object). 6237 * @param {!string} p Property name 6238 * @param {!CSSPlugin} cssp The CSSPlugin instance that should be associated with this tween. 6239 * @param {?CSSPropTween} pt The CSSPropTween that is the current head of the linked list (we'll prepend to it) 6240 * @param {TweenPlugin=} plugin If a plugin will be used to tween the parsed value, this is the plugin instance. 6241 * @param {Object=} vars Original vars object that contains the data for parsing. 6242 * @return {CSSPropTween} The first CSSPropTween in the linked list which includes the new one(s) added by the parse() call. 6243 */ 6244 p.parse = function(t, e, p, cssp, pt, plugin, vars) { 6245 return this.parseComplex(t.style, this.format(_getStyle(t, this.p, _cs, false, this.dflt)), this.format(e), pt, plugin); 6246 }; 6247 6248 /** 6249 * 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: 6250 * 1) Target object whose property should be tweened (typically a DOM element) 6251 * 2) The end/destination value (could be a string, number, object, or whatever you want) 6252 * 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) 6253 * 6254 * 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: 6255 * 6256 * CSSPlugin.registerSpecialProp("myCustomProp", function(target, value, tween) { 6257 * var start = target.style.width;
6258 * return function(ratio) { 6259 * target.style.width = (start + value * ratio) + "px"; 6260 * console.log("set width to " + target.style.width); 6261 * } 6262 * }, 0); 6263 * 6264 * Then, when I do this tween, it will trigger my special property: 6265 * 6266 * TweenLite.to(element, 1, {css:{myCustomProp:100}}); 6267 * 6268 * In the example, of course, we're just changing the width, but you can do anything you want. 6269 * 6270 * @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}}) 6271 * @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. 6272 * @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. 6273 */ 6274 CSSPlugin.registerSpecialProp = function(name, onInitTween, priority) { 6275 _registerComplexSpecialProp(name, {parser:function(t, e, p, cssp, pt, plugin, vars) { 6276 var rv = new CSSPropTween(t, p, 0, 0, pt, 2, p, false, priority); 6277 rv.plugin = plugin; 6278 rv.setRatio = onInitTween(t, e, cssp._tween, p); 6279 return rv; 6280 }, priority:priority}); 6281 }; 6282 6283 6284 6285 6286 6287 6288 //transform-related methods and properties 6289 CSSPlugin.useSVGTransformAttr = _isSafari || _isFirefox; //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). 6290 var _transformProps = ("scaleX,scaleY,scaleZ,x,y,z,skewX,skewY,rotation,rotationX,rotationY,perspective,xPercent,yPercent").split(","), 6291 _transformProp = _checkPropPrefix("transform"), //the Javascript (camelCase) transform property, like msTransform, WebkitTransform, MozTransform, or OTransform. 6292 _transformPropCSS = _prefixCSS + "transform", 6293 _transformOriginProp = _checkPropPrefix("transformOrigin"), 6294 _supports3D = (_checkPropPrefix("perspective") !== null), 6295 Transform = _internals.Transform = function() { 6296 this.perspective = parseFloat(CSSPlugin.defaultTransformPerspective) || 0; 6297 this.force3D = (CSSPlugin.defaultForce3D === false || !_supports3D) ? false : CSSPlugin.defaultForce3D || "auto"; 6298 }, 6299 _SVGElement = window.SVGElement, 6300 _useSVGTransformAttr, 6301 //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. 6302 6303 _createSVG = function(type, container, attributes) { 6304 var element = _doc.createElementNS("http://www.w3.org/2000/svg", type), 6305 reg = /([a-z])([A-Z])/g, 6306 p; 6307 for (p in attributes) { 6308 element.setAttributeNS(null, p.replace(reg, "$1-$2").toLowerCase(), attributes[p]); 6309 } 6310 container.appendChild(element); 6311 return element; 6312 }, 6313 _docElement = _doc.documentElement, 6314 _forceSVGTransformAttr = (function() { 6315 //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 6316 var force = _ieVers || (/Android/i.test(_agent) && !window.chrome), 6317 svg, rect, width; 6318 if (_doc.createElementNS && !force) { //IE8 and earlier doesn't support SVG anyway 6319 svg = _createSVG("svg", _docElement); 6320 rect = _createSVG("rect", svg, {width:100, height:50, x:100}); 6321 width = rect.getBoundingClientRect().width; 6322 rect.style[_transformOriginProp] = "50% 50%"; 6323 rect.style[_transformProp] = "scaleX(0.5)"; 6324 force = (width === rect.getBoundingClientRect().width && !(_is
6324Firefox && _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). 6325 _docElement.removeChild(svg); 6326 } 6327 return force; 6328 })(), 6329 _parseSVGOrigin = function(e, local, decoratee, absolute, smoothOrigin) { 6330 var tm = e._gsTransform, 6331 m = _getMatrix(e, true), 6332 v, x, y, xOrigin, yOrigin, a, b, c, d, tx, ty, determinant, xOriginOld, yOriginOld; 6333 if (tm) { 6334 xOriginOld = tm.xOrigin; //record the original values before we alter them. 6335 yOriginOld = tm.yOrigin; 6336 } 6337 if (!absolute || (v = absolute.split(" ")).length < 2) { 6338 b = e.getBBox(); 6339 local = _parsePosition(local).split(" "); 6340 v = [(local[0].indexOf("%") !== -1 ? parseFloat(local[0]) / 100 * b.width : parseFloat(local[0])) + b.x, 6341 (local[1].indexOf("%") !== -1 ? parseFloat(local[1]) / 100 * b.height : parseFloat(local[1])) + b.y]; 6342 } 6343 decoratee.xOrigin = xOrigin = parseFloat(v[0]); 6344 decoratee.yOrigin = yOrigin = parseFloat(v[1]); 6345 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. 6346 a = m[0]; 6347 b = m[1]; 6348 c = m[2]; 6349 d = m[3]; 6350 tx = m[4]; 6351 ty = m[5]; 6352 determinant = (a * d - b * c); 6353 x = xOrigin * (d / determinant) + yOrigin * (-c / determinant) + ((c * ty - d * tx) / determinant); 6354 y = xOrigin * (-b / determinant) + yOrigin * (a / determinant) - ((a * ty - b * tx) / determinant); 6355 xOrigin = decoratee.xOrigin = v[0] = x; 6356 yOrigin = decoratee.yOrigin = v[1] = y; 6357 } 6358 if (tm) { //avoid jump when transformOrigin is changed - adjust the x/y values accordingly 6359 if (smoothOrigin || (smoothOrigin !== false && CSSPlugin.defaultSmoothOrigin !== false)) { 6360 x = xOrigin - xOriginOld; 6361 y = yOrigin - yOriginOld; 6362 //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. 6363 //tm.x -= x - (x * m[0] + y * m[2]); 6364 //tm.y -= y - (x * m[1] + y * m[3]); 6365 tm.xOffset += (x * m[0] + y * m[2]) - x; 6366 tm.yOffset += (x * m[1] + y * m[3]) - y; 6367 } else { 6368 tm.xOffset = tm.yOffset = 0; 6369 } 6370 } 6371 e.setAttribute("data-svg-origin", v.join(" ")); 6372 }, 6373 _isSVG = function(e) { 6374 return !!(_SVGElement && typeof(e.getBBox) === "function" && e.getCTM && (!e.parentNode || (e.parentNode.getBBox && e.parentNode.getCTM))); 6375 }, 6376 _identity2DMatrix = [1,0,0,1,0,0], 6377 _getMatrix = function(e, force2D) { 6378 var tm = e._gsTransform || new Transform(), 6379 rnd = 100000, 6380 isDefault, s, m, n, dec; 6381 if (_transformProp) { 6382 s = _getStyle(e, _transformPropCSS, null, true); 6383 } else if (e.currentStyle) { 6384 //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. 6385 s = e.currentStyle.filter.match(_ieGetMatrixExp); 6386 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(",") : ""; 6387 }
6388 isDefault = (!s || s === "none" || s === "matrix(1, 0, 0, 1, 0, 0)"); 6389 if (tm.svg || (e.getBBox && _isSVG(e))) { 6390 if (isDefault && (e.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 6391 s = e.style[_transformProp]; 6392 isDefault = 0; 6393 } 6394 m = e.getAttribute("transform"); 6395 if (isDefault && m) { 6396 if (m.indexOf("matrix") !== -1) { //just in case there's a "transform" value specified as an attribute instead of CSS style. Accept either a matrix() or simple translate() value though. 6397 s = m; 6398 isDefault = 0; 6399 } else if (m.indexOf("translate") !== -1) { 6400 s = "matrix(1,0,0,1," + m.match(/(?:\-|\b)[\d\-\.e]+\b/gi).join(",") + ")"; 6401 isDefault = 0; 6402 } 6403 } 6404 } 6405 if (isDefault) { 6406 return _identity2DMatrix; 6407 } 6408 //split the matrix values out into an array (m for matrix) 6409 m = (s || "").match(/(?:\-|\b)[\d\-\.e]+\b/gi) || []; 6410 i = m.length; 6411 while (--i > -1) { 6412 n = Number(m[i]); 6413 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). 6414 } 6415 return (force2D && m.length > 6) ? [m[0], m[1], m[4], m[5], m[12], m[13]] : m; 6416 }, 6417 6418 /** 6419 * 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. 6420 * @param {!Object} t target element 6421 * @param {Object=} cs computed style object (optional) 6422 * @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...} 6423 * @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) 6424 * @return {object}
6424 object containing all of the transform properties/values like {x:0, y:0, z:0, scaleX:1...} 6425 */ 6426 _getTransform = _internals.getTransform = function(t, cs, rec, parse) { 6427 if (t._gsTransform && rec && !parse) { 6428 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. 6429 } 6430 var tm = rec ? t._gsTransform || new Transform() : new Transform(), 6431 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. 6432 min = 0.00002, 6433 rnd = 100000, 6434 zOrigin = _supports3D ? parseFloat(_getStyle(t, _transformOriginProp, cs, false, "0 0 0").split(" ")[2]) || tm.zOrigin || 0 : 0, 6435 defaultTransformPerspective = parseFloat(CSSPlugin.defaultTransformPerspective) || 0, 6436 m, i, scaleX, scaleY, rotation, skewX; 6437 6438 tm.svg = !!(t.getBBox && _isSVG(t)); 6439 if (tm.svg) { 6440 _parseSVGOrigin(t, _getStyle(t, _transformOriginProp, _cs, false, "50% 50%") + "", tm, t.getAttribute("data-svg-origin")); 6441 _useSVGTransformAttr = CSSPlugin.useSVGTransformAttr || _forceSVGTransformAttr; 6442 } 6443 m = _getMatrix(t); 6444 if (m !== _identity2DMatrix) { 6445 6446 if (m.length === 16) { 6447 //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) 6448 var a11 = m[0], a21 = m[1], a31 = m[2], a41 = m[3], 6449 a12 = m[4], a22 = m[5], a32 = m[6], a42 = m[7], 6450 a13 = m[8], a23 = m[9], a33 = m[10], 6451 a14 = m[12], a24 = m[13], a34 = m[14], 6452 a43 = m[11], 6453 angle = Math.atan2(a32, a33), 6454 t1, t2, t3, t4, cos, sin; 6455 6456 //we manually compensate for non-zero z component of transformOrigin to work around bugs in Safari 6457 if (tm.zOrigin) { 6458 a34 = -tm.zOrigin; 6459 a14 = a13*a34-m[12]; 6460 a24 = a23*a34-m[13]; 6461 a34 = a33*a34+tm.zOrigin-m[14]; 6462 } 6463 tm.rotationX = angle * _RAD2DEG; 6464 //rotationX 6465 if (angle) { 6466 cos = Math.cos(-angle); 6467 sin = Math.sin(-angle); 6468 t1 = a12*cos+a13*sin; 6469 t2 = a22*cos+a23*sin; 6470 t3 = a32*cos+a33*sin; 6471 a13 = a12*-sin+a13*cos; 6472 a23 = a22*-sin+a23*cos; 6473 a33 = a32*-sin+a33*cos; 6474 a43 = a42*-sin+a43*cos; 6475 a12 = t1; 6476 a22 = t2; 6477 a32 = t3; 6478 } 6479 //rotationY 6480 angle = Math.atan2(a13, a33); 6481 tm.rotationY = angle * _RAD2DEG; 6482 if (angle) { 6483 cos = Math.cos(-angle); 6484 sin = Math.sin(-angle); 6485 t1 = a11*cos-a13*sin; 6486 t2 = a21*cos-a23*sin; 6487 t3 = a31*cos-a33*sin; 6488 a23 = a21*sin+a23*cos; 6489 a33 = a31*sin+a33*cos; 6490 a43 = a41*sin+a43*cos; 6491 a11 = t1; 6492 a21 = t2; 6493 a31 = t3; 6494 } 6495 //rotationZ 6496 angle = Math.atan2(a21, a11); 6497 tm.rotation = angle * _RAD2DEG; 6498 if (angle) { 6499 cos = Math.cos(-angle); 6500 sin = Math.sin(-angle); 6501 a11 = a11*cos+a12*sin; 6502 t2 = a21*cos+a22*sin; 6503 a22 = a21*-sin+a22*cos; 6504 a32 = a31*-sin+a32*cos; 6505 a21 = t2; 6506 } 6507 6508 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. 6509 tm.rotationX = tm.rotation = 0; 6510 tm.rotationY += 180; 6511 } 6512 6513 tm.scaleX = ((Math.sqrt(a11 * a11 + a21 * a21) * rnd + 0.5) | 0) / rnd; 6514 tm.scaleY = ((Math.sqrt(a22 * a22 + a23 * a23) * rnd + 0.5) | 0) / rnd; 6515 tm.scaleZ = ((Math.sqrt(a32 * a32 + a33 * a33) * rnd + 0.5) | 0) / rnd; 6516 tm.skewX = 0; 6517 tm.perspective = a43 ? 1 / ((a43 < 0) ? -a43 : a43) : 0; 6518 tm.x = a14; 6519 tm.y = a24; 6520 tm.z = a34; 6521 if (tm.svg) { 6522 tm.x -= tm.xOrigin - (tm.xOrigin * a11 - tm.yOrigin * a12); 6523 tm.y -= tm.yOrigin - (tm.yOrigin * a21 - tm.xOrigin * a22); 6524 } 6525 6526 } else if ((!_supports3D || parse || !m.length || tm.x !== m[4] || tm.y !== m[5] || (!tm.rotationX && !tm.rotationY)) && !(tm.x !== undefined && _getStyle(t, "display", cs) === "
6526none")) { //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. 6527 var k = (m.length >= 6), 6528 a = k ? m[0] : 1, 6529 b = m[1] || 0, 6530 c = m[2] || 0, 6531 d = k ? m[3] : 1; 6532 tm.x = m[4] || 0; 6533 tm.y = m[5] || 0; 6534 scaleX = Math.sqrt(a * a + b * b); 6535 scaleY = Math.sqrt(d * d + c * c); 6536 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). 6537 skewX = (c || d) ? Math.atan2(c, d) * _RAD2DEG + rotation : tm.skewX || 0; 6538 if (Math.abs(skewX) > 90 && Math.abs(skewX) < 270) { 6539 if (invX) { 6540 scaleX *= -1; 6541 skewX += (rotation <= 0) ? 180 : -180; 6542 rotation += (rotation <= 0) ? 180 : -180; 6543 } else { 6544 scaleY *= -1; 6545 skewX += (skewX <= 0) ? 180 : -180; 6546 } 6547 } 6548 tm.scaleX = scaleX; 6549 tm.scaleY = scaleY; 6550 tm.rotation = rotation; 6551 tm.skewX = skewX; 6552 if (_supports3D) { 6553 tm.rotationX = tm.rotationY = tm.z = 0; 6554 tm.perspective = defaultTransformPerspective; 6555 tm.scaleZ = 1; 6556 } 6557 if (tm.svg) { 6558 tm.x -= tm.xOrigin - (tm.xOrigin * a + tm.yOrigin * c); 6559 tm.y -= tm.yOrigin - (tm.xOrigin * b + tm.yOrigin * d); 6560 } 6561 } 6562 tm.zOrigin = zOrigin; 6563 //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. 6564 for (i in tm) { 6565 if (tm[i] < min) if (tm[i] > -min) { 6566 tm[i] = 0; 6567 } 6568 } 6569 } 6570 //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); 6571 if (rec) { 6572 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) 6573 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. 6574 if (_useSVGTransformAttr && t.style[_transformProp]) { 6575 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). 6576 _removeProp(t.style, _transformProp); 6577 });
6578 } else if (!_useSVGTransformAttr && t.getAttribute("transform")) { 6579 TweenLite.delayedCall(0.001, function(){ 6580 t.removeAttribute("transform"); 6581 }); 6582 } 6583 } 6584 } 6585 return tm; 6586 }, 6587 6588 //for setting 2D transforms in IE6, IE7, and IE8 (must use a "filter" to emulate the behavior of modern day browser transforms) 6589 _setIETransformRatio = function(v) { 6590 var t = this.data, //refers to the element's _gsTransform object 6591 ang = -t.rotation * _DEG2RAD, 6592 skew = ang + t.skewX * _DEG2RAD, 6593 rnd = 100000, 6594 a = ((Math.cos(ang) * t.scaleX * rnd) | 0) / rnd, 6595 b = ((Math.sin(ang) * t.scaleX * rnd) | 0) / rnd, 6596 c = ((Math.sin(skew) * -t.scaleY * rnd) | 0) / rnd, 6597 d = ((Math.cos(skew) * t.scaleY * rnd) | 0) / rnd, 6598 style = this.t.style, 6599 cs = this.t.currentStyle, 6600 filters, val; 6601 if (!cs) { 6602 return; 6603 } 6604 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) 6605 b = -c; 6606 c = -val; 6607 filters = cs.filter; 6608 style.filter = ""; //remove filters so that we can accurately measure offsetWidth/offsetHeight 6609 var w = this.t.offsetWidth, 6610 h = this.t.offsetHeight, 6611 clip = (cs.position !== "absolute"), 6612 m = "progid:DXImageTransform.Microsoft.Matrix(M11=" + a + ", M12=" + b + ", M21=" + c + ", M22=" + d, 6613 ox = t.x + (w * t.xPercent / 100), 6614 oy = t.y + (h * t.yPercent / 100), 6615 dx, dy; 6616 6617 //if transformOrigin is being used, adjust the offset x and y 6618 if (t.ox != null) { 6619 dx = ((t.oxp) ? w * t.ox * 0.01 : t.ox) - w / 2; 6620 dy = ((t.oyp) ? h * t.oy * 0.01 : t.oy) - h / 2; 6621 ox += dx - (dx * a + dy * b); 6622 oy += dy - (dx * c + dy * d); 6623 } 6624 6625 if (!clip) { 6626 m += ", sizingMethod='auto expand')"; 6627 } else { 6628 dx = (w / 2); 6629 dy = (h / 2); 6630 //translate to ensure that transformations occur around the correct origin (default is center). 6631 m += ", Dx=" + (dx - (dx * a + dy * b) + ox) + ", Dy=" + (dy - (dx * c + dy * d) + oy) + ")"; 6632 } 6633 if (filters.indexOf("DXImageTransform.Microsoft.Matrix(") !== -1) { 6634 style.filter = filters.replace(_ieSetMatrixExp, m); 6635 } else { 6636 style.filter = m + " " + filters; //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. 6637 } 6638 6639 //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. 6640 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) { 6641 style.removeAttribute("filter"); 6642 } 6643 6644 //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). 6645 if (!clip) { 6646 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 6647 marg, prop, dif; 6648 dx = t.ieOffsetX || 0; 6649 dy = t.ieOffsetY || 0; 6650 t.ieOffsetX = Math.round((w - ((a < 0 ? -a : a) * w + (b < 0 ? -b : b) * h)) / 2 + ox); 6651 t.ieOffsetY = Math.round((h - ((d < 0 ? -d : d) * h + (c < 0 ? -c : c) * w)) / 2 + oy); 6652 for (i = 0; i < 4; i++) { 6653 prop = _margins[i]; 6654 marg = cs[prop]; 6655 //we need to get the current margin in case it is being tweened separately (we want to respect that tween's changes) 6656 val = (marg.indexOf("px") !== -1) ? parseFloat(marg) : _convertToPixels(this.t, prop, parseFloat(marg), marg.replace(_suffixExp, "")) || 0; 6657 if (val !== t[prop]) { 6658 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. 6659 } else { 6660 dif = (i < 2) ? dx - t.ieOffsetX : dy - t.ieOffsetY; 6661 } 6662 style[prop] = (t[prop] = Math.round( val - dif * ((i === 0 || i === 2) ? 1 : mult) )) + "px"; 6663 } 6664 } 6665 }, 6666 6667 /* translates a super small decimal to a string WITHOUT scientific notation 6668 _safeDecimal = function(n) { 6669 var s = (n < 0 ? -n : n) + "", 6670 a = s.split("e-"); 6671 return (n < 0 ? "-0." : "0.") + new Array(parseInt(a[1], 10) || 0).join("0") + a[0].split(".").join(""); 6672 }, 6673 */ 6674 6675 _setTransformRatio = _internals.set3DTransformRatio = _internals.setTransformRatio = function(v) { 6676 var t = this.data, //refers to the element's _gsTransform object 6677 style = this.t.style, 6678 angle = t.rotation, 6679 rotationX = t.rotationX, 6680 rotationY = t.rotationY, 6681 sx = t.scaleX, 6682 sy = t.scaleY, 6683 sz = t.scaleZ, 6684 x = t.x, 6685 y = t.y, 6686 z = t.z, 6687 isSVG = t.svg, 6688 perspective = t.perspective, 6689 force3D = t.force3D, 6690 a11, a12, a13, a21, a22, a23, a31, a32, a33, a41, a42, a43, 6691 zOrigin, min, cos, sin, t1, t2, transform, comma, zero, skew, rnd; 6692 //check to see if we should render as 2D (and SVGs must use 2D when _useSVGTransformAttr is true) 6693 if (((((v === 1 || v === 0) && force3D === "auto" && (this.tween._totalTime === this.tween._totalDuration || !this.tween._totalTime)) || !force3D) && !z && !perspective && !rotationY && !rotationX) || (_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. 6694 6695 //2D 6696 if (angle || t.skewX || isSVG) { 6697 angle *= _DEG2RAD; 6698 skew = t.skewX * _DEG2RAD; 6699 rnd = 100000; 6700 a11 = Math.cos(angle) * sx; 6701 a21 = Math.sin(angle) * sx; 6702 a12 = Math.sin(angle - skew) * -sy;
6703 a22 = Math.cos(angle - skew) * sy; 6704 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 6705 t1 = Math.tan(skew); 6706 t1 = Math.sqrt(1 + t1 * t1); 6707 a12 *= t1; 6708 a22 *= t1; 6709 if (t.skewY) { 6710 a11 *= t1; 6711 a21 *= t1; 6712 } 6713 } 6714 if (isSVG) { 6715 x += t.xOrigin - (t.xOrigin * a11 + t.yOrigin * a12) + t.xOffset; 6716 y += t.yOrigin - (t.xOrigin * a21 + t.yOrigin * a22) + t.yOffset; 6717 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. 6718 min = this.t.getBBox(); 6719 x += t.xPercent * 0.01 * min.width; 6720 y += t.yPercent * 0.01 * min.height; 6721 } 6722 min = 0.000001; 6723 if (x < min) if (x > -min) { 6724 x = 0; 6725 } 6726 if (y < min) if (y > -min) { 6727 y = 0; 6728 } 6729 } 6730 transform = (((a11 * rnd) | 0) / rnd) + "," + (((a21 * rnd) | 0) / rnd) + "," + (((a12 * rnd) | 0) / rnd) + "," + (((a22 * rnd) | 0) / rnd) + "," + x + "," + y + ")"; 6731 if (isSVG && _useSVGTransformAttr) {
6732 this.t.setAttribute("transform", "matrix(" + transform); 6733 } else { 6734 //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. 6735 style[_transformProp] = ((t.xPercent || t.yPercent) ? "translate(" + t.xPercent + "%," + t.yPercent + "%) matrix(" : "matrix(") + transform; 6736 } 6737 } else { 6738 style[_transformProp] = ((t.xPercent || t.yPercent) ? "translate(" + t.xPercent + "%," + t.yPercent + "%) matrix(" : "matrix(") + sx + ",0,0," + sy + "," + x + "," + y + ")"; 6739 } 6740 return; 6741 6742 } 6743 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. 6744 min = 0.0001; 6745 if (sx < min && sx > -min) { 6746 sx = sz = 0.00002; 6747 } 6748 if (sy < min && sy > -min) { 6749 sy = sz = 0.00002; 6750 } 6751 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). 6752 perspective = 0; 6753 } 6754 } 6755 if (angle || t.skewX) { 6756 angle *= _DEG2RAD; 6757 cos = a11 = Math.cos(angle); 6758 sin = a21 = Math.sin(angle); 6759 if (t.skewX) { 6760 angle -= t.skewX * _DEG2RAD; 6761 cos = Math.cos(angle); 6762 sin = Math.sin(angle); 6763 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 6764 t1 = Math.tan(t.skewX * _DEG2RAD); 6765 t1 = Math.sqrt(1 + t1 * t1); 6766 cos *= t1; 6767 sin *= t1; 6768 if (t.skewY) { 6769 a11 *= t1; 6770 a21 *= t1; 6771 } 6772 } 6773 } 6774 a12 = -sin; 6775 a22 = cos; 6776 6777 } else if (!rotationY && !rotationX && sz === 1 && !perspective && !isSVG) { //if we're only translating and/or 2D scaling, this is faster... 6778 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 + ")" : ""); 6779 return; 6780 } else { 6781 a11 = a22 = 1; 6782 a12 = a21 = 0; 6783 } 6784 // KEY INDEX AFFECTS 6785 // a11 0 rotation, rotationY, scaleX 6786 // a21 1 rotation, rotationY, scaleX 6787 // a31 2 rotationY, scaleX 6788 // a41 3 rotationY, scaleX 6789 // a12 4 rotation, skewX, rotationX, scaleY 6790 // a22 5 rotation, skewX, rotationX, scaleY 6791 // a32 6 rotationX, scaleY 6792 // a42 7 rotationX, scaleY 6793 // a13 8 rotationY, rotationX, scaleZ 6794 // a23 9 rotationY, rotationX, scaleZ 6795 // a33 10 rotationY, rotationX, scaleZ 6796 // a43 11 rotationY, rotationX, perspective, scaleZ 6797 // a14 12 x, zOrigin, svgOrigin 6798 // a24 13 y, zOrigin, svgOrigin 6799 // a34 14 z, zOrigin 6800 // a44 15 6801 // rotation: Math.atan2(a21, a11) 6802 // rotationY: Math.atan2(a13, a33) (or Math.atan2(a13, a11)) 6803 // rotationX: Math.atan2(a32, a33) 6804 a33 = 1; 6805 a13 = a23 = a31 = a32 = a41 = a42 = 0; 6806 a43 = (perspective) ? -1 / perspective : 0; 6807 zOrigin = t.zOrigin; 6808 min = 0.000001; //threshold below which browsers use scientific notation which won't work. 6809 comma = ","; 6810 zero = "0"; 6811 angle = rotationY * _DEG2RAD; 6812 if (angle) { 6813 cos = Math.cos(angle); 6814 sin = Math.sin(angle); 6815 a31 = -sin; 6816 a41 = a43*-sin; 6817 a13 = a11*sin; 6818 a23 = a21*sin; 6819 a33 = cos; 6820 a43 *= cos; 6821 a11 *= cos; 6822 a21 *= cos; 6823 } 6824 angle = rotationX * _DEG2RAD; 6825 if (angle) { 6826 cos = Math.cos(angle); 6827 sin = Math.sin(angle); 6828 t1 = a12*cos+a13*sin; 6829 t2 = a22*cos+a23*sin; 6830 a32 = a33*sin; 6831 a42 = a43*sin; 6832 a13 = a12*-sin+a13*cos; 6833 a23 = a22*-sin+a23*cos; 6834 a33 = a33*cos; 6835 a43 = a43*cos; 6836 a12 = t1; 6837 a22 = t2; 6838 } 6839 if (sz !== 1) { 6840 a13*=sz; 6841 a23*=sz; 6842 a33*=sz; 6843 a43*=sz; 6844 } 6845 if (sy !== 1) { 6846 a12*=sy; 6847 a22*=sy; 6848 a32*=sy; 6849 a42*=sy; 6850 } 6851 if (sx !== 1) { 6852 a11*=sx; 6853 a21*=sx; 6854 a31*=sx; 6855 a41*=sx; 6856 } 6857 6858 if (zOrigin || isSVG) { 6859 if (zOrigin) { 6860 x += a13*-zOrigin; 6861 y += a23*-zOrigin; 6862 z += a33*-zOrigin+zOrigin; 6863 } 6864 if (isSVG) { //due to bugs in some browsers, we need to manage the transform-origin of SVG manually 6865 x += t.xOrigin - (t.xOrigin * a11 + t.yOrigin * a12) + t.xOffset; 6866 y += t.yOrigin - (t.xOrigin * a21 + t.yOrigin * a22) + t.yOffset; 6867 } 6868 if (x < min && x > -min) { 6869 x = zero; 6870 } 6871 if (y < min && y > -min) { 6872 y = zero; 6873 } 6874 if (z < min && z > -min) { 6875 z = 0; //don't use string because we calculate perspective later and need the number. 6876 } 6877 } 6878 6879 //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: 6880 transform = ((t.xPercent || t.yPercent) ? "translate(" + t.xPercent + "%," + t.yPercent + "%) matrix3d(" : "matrix3d("); 6881 transform += ((a11 < min && a11 > -min) ? zero : a11) + comma + ((a21 < min && a21 > -min) ? zero : a21) + comma + ((a31 < min && a31 > -min) ? zero : a31); 6882 transform += comma + ((a41 < min && a41 > -min) ? zero : a41) + comma + ((a12 < min && a12 > -min) ? zero : a12) + comma + ((a22 < min && a22 > -min) ? zero : a22); 6883 if (rotationX || rotationY) { //performance optimization (often there's no rotationX or rotationY, so we can skip these calculations) 6884 transform += comma + ((a32 < min && a32 > -min) ? zero : a32) + comma + ((a42 < min && a42 > -min) ? zero : a42) + comma + ((a13 < min && a13 > -min) ? zero : a13); 6885 transform += comma + ((a23 < min && a23 > -min) ? zero : a23) + comma + ((a33 < min && a33 > -min) ? zero : a33) + comma + ((a43 < min && a43 > -min) ? zero : a43) + comma; 6886 } else { 6887 transform += ",0,0,0,0,1,0,"; 6888 } 6889 transform += x + comma + y + comma + z + comma + (perspective ? (1 + (-z / perspective)) : 1) + ")"; 6890 6891 style[_transformProp] = transform; 6892 }; 6893 6894 p = Transform.prototype; 6895 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; 6896 p.scaleX = p.scaleY = p.scaleZ = 1; 6897 6898 _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, p, cssp, pt, plugin, vars) { 6899 if (cssp._lastParsedTransform === vars) { return pt; } //only need to parse the transform once, and only if the browser supports it. 6900 cssp._lastParsedTransform = vars; 6901 var originalGSTransform = t._gsTransform, 6902 style = t.style, 6903 min = 0.000001, 6904 i = _transformProps.length, 6905 v = vars, 6906 endRotations = {}, 6907 transformOriginString = "transformOrigin", 6908 m1, m2, skewY, copy, orig, has3D, hasChange, dr, x, y; 6909 if (vars.display) { //if the user is setting display during this tween, it may not be instantiated yet but we must force it here in order to get accurate readings. If display is "none", some browsers refuse to report the transform properties correctly. 6910 copy = _getStyle(t, "display"); 6911 style.display = "block"; 6912 m1 = _getTransform(t, _cs, true, vars.parseTransform); 6913 style.display = copy; 6914 } else { 6915 m1 = _getTransform(t, _cs, true, vars.parseTransform); 6916 } 6917 cssp._transform = m1; 6918 if (typeof(v.transform) === "string" && _transformProp) { //for values like transform:"rotate(60deg) scale(0.5, 0.8)" 6919 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. 6920 copy[_transformProp] = v.transform; 6921 copy.display = "block"; //if display is "none", the browser often refuses to report the transform properties correctly. 6922 copy.position = "absolute"; 6923 _doc.body.appendChild(_tempDiv); 6924 m2 = _getTransform(_tempDiv, null, false); 6925 _doc.body.removeChild(_tempDiv); 6926 if (!m2.perspective) { 6927 m2.perspective = m1.perspective; //tweening to no perspective gives very unintuitive results - just keep the same perspective in that case. 6928 } 6929 if (v.xPercent != null) { 6930 m2.xPercent = _parseVal(v.xPercent, m1.xPercent); 6931 } 6932 if (v.yPercent != null) { 6933 m2.yPercent = _parseVal(v.yPercent, m1.yPercent); 6934 } 6935 } else if (typeof(v) === "object") { //for values like scaleX, scaleY, rotation, x, y, skewX, and skewY or transform:{...} (object) 6936 m2 = {scaleX:_parseVal((v.scaleX != null) ? v.scaleX : v.scale, m1.scaleX), 6937 scaleY:_parseVal((v.scaleY != null) ? v.scaleY : v.scale, m1.scaleY), 6938 scaleZ:_parseVal(v.scaleZ, m1.scaleZ), 6939 x:_parseVal(v.x, m1.x), 6940 y:_parseVal(v.y, m1.y), 6941 z:_parseVal(v.z, m1.z), 6942 xPercent:_parseVal(v.xPercent, m1.xPercent), 6943 yPercent:_parseVal(v.yPercent, m1.yPercent), 6944 perspective:_parseVal(v.transformPerspective, m1.perspective)}; 6945 dr = v.directionalRotation; 6946 if (dr != null) { 6947 if (typeof(dr) === "object") { 6948 for (copy in dr) { 6949 v[copy] = dr[copy]; 6950 } 6951 } else { 6952 v.rotation = dr; 6953 } 6954 } 6955 if (typeof(v.x) === "string" && v.x.indexOf("%") !== -1) { 6956 m2.x = 0; 6957 m2.xPercent = _parseVal(v.x, m1.xPercent); 6958 } 6959 if (typeof(v.y) === "string" && v.y.indexOf("%") !== -1) { 6960 m2.y = 0; 6961 m2.yPercent = _parseVal(v.y, m1.yPercent); 6962 } 6963 6964 m2.rotation = _parseAngle(("rotation" in v) ? v.rotation : ("shortRotation" in v) ? v.shortRotation + "_short" : ("rotationZ" in v) ? v.rotationZ : m1.rotation, m1.rotation, "rotation", endRotations); 6965 if (_supports3D) { 6966 m2.rotationX = _parseAngle(("rotationX" in v) ? v.rotationX : ("shortRotationX" in v) ? v.shortRotationX + "_short" : m1.rotationX || 0, m1.rotationX, "rotationX", endRotations); 6967 m2.rotationY = _parseAngle(("rotationY" in v) ? v.rotationY : ("shortRotationY" in v) ? v.shortRotationY + "_short" : m1.rotationY || 0, m1.rotationY, "rotationY", endRotations); 6968 }
6969 m2.skewX = (v.skewX == null) ? m1.skewX : _parseAngle(v.skewX, m1.skewX); 6970 6971 //note: for performance reasons, we combine all skewing into the skewX and rotation values, ignoring skewY but we must still record it so that we can discern how much of the overall skew is attributed to skewX vs. skewY. Otherwise, if the skewY would always act relative (tween skewY to 10deg, for example, multiple times and if we always combine things into skewX, we can't remember that skewY was 10 from last time). Remember, a skewY of 10 degrees looks the same as a rotation of 10 degrees plus a skewX of -10 degrees. 6972 m2.skewY = (v.skewY == null) ? m1.skewY : _parseAngle(v.skewY, m1.skewY); 6973 if ((skewY = m2.skewY - m1.skewY)) { 6974 m2.skewX += skewY; 6975 m2.rotation += skewY; 6976 } 6977 } 6978 if (_supports3D && v.force3D != null) { 6979 m1.force3D = v.force3D; 6980 hasChange = true; 6981 } 6982 6983 m1.skewType = v.skewType || m1.skewType || CSSPlugin.defaultSkewType; 6984 6985 has3D = (m1.force3D || m1.z || m1.rotationX || m1.rotationY || m2.z || m2.rotationX || m2.rotationY || m2.perspective); 6986 if (!has3D && v.scale != null) { 6987 m2.scaleZ = 1; //no need to tween scaleZ. 6988 } 6989 6990 while (--i > -1) { 6991 p = _transformProps[i]; 6992 orig = m2[p] - m1[p]; 6993 if (orig > min || orig < -min || v[p] != null || _forcePT[p] != null) { 6994 hasChange = true; 6995 pt = new CSSPropTween(m1, p, m1[p], orig, pt); 6996 if (p in endRotations) { 6997 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 6998 } 6999 pt.xs0 = 0; //ensures the value stays numeric in setRatio() 7000 pt.plugin = plugin; 7001 cssp._overwriteProps.push(pt.n); 7002 } 7003 } 7004 7005 orig = v.transformOrigin; 7006 if (m1.svg && (orig || v.svgOrigin)) { 7007 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 7008 y = m1.yOffset; 7009 _parseSVGOrigin(t, _parsePosition(orig), m2, v.svgOrigin, v.smoothOrigin); 7010 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. 7011 pt = _addNonTweeningNumericPT(m1, "yOrigin", (originalGSTransform ? m1 : m2).yOrigin, m2.yOrigin, pt, transformOriginString); 7012 if (x !== m1.xOffset || y !== m1.yOffset) { 7013 pt = _addNonTweeningNumericPT(m1, "xOffset", (originalGSTransform ? x : m1.xOffset), m1.xOffset, pt, transformOriginString); 7014 pt = _addNonTweeningNumericPT(m1, "yOffset", (originalGSTransform ? y : m1.yOffset), m1.yOffset, pt, transformOriginString); 7015 } 7016 orig = _useSVGTransformAttr ? null : "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 7017 } 7018 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). 7019 if (_transformProp) { 7020 hasChange = true; 7021 p = _transformOriginProp; 7022 orig = (orig || _getStyle(t, p, _cs, false, "50% 50%")) + ""; //cast as string to avoid errors 7023 pt = new CSSPropTween(style, p, 0, 0, pt, -1, transformOriginString); 7024 pt.b = style[p]; 7025 pt.plugin = plugin; 7026 if (_supports3D) { 7027 copy = m1.zOrigin; 7028 orig = orig.split(" "); 7029 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. 7030 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)! 7031 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) 7032 pt.b = copy; 7033 pt.xs0 = pt.e = m1.zOrigin; 7034 } else { 7035 pt.xs0 = pt.e = orig; 7036 } 7037 7038 //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). 7039 } else { 7040 _parsePosition(orig + "", m1); 7041 } 7042 } 7043 if (hasChange) { 7044 cssp._transformType = (!(m1.svg && _useSVGTransformAttr) && (has3D || this._transformType === 3)) ? 3 : 2; //quicker than calling cssp._enableTransforms(); 7045 } 7046 return pt; 7047 }, prefix:true}); 7048 7049 _registerComplexSpecialProp("boxShadow", {defaultValue:"0px 0px 0px 0px #999", prefix:true, color:true, multi:true, keyword:"inset"}); 7050 7051 _registerComplexSpecialProp("borderRadius", {defaultValue:"0px", parser:function(t, e, p, cssp, pt, plugin) { 7052 e = this.format(e); 7053 var props = ["borderTopLeftRadius","borderTopRightRadius","borderBottomRightRadius","borderBottomLeftRadius"], 7054 style = t.style, 7055 ea1, i, es2, bs2, bs, es, bn, en, w, h, esfx, bsfx, rel, hn, vn, em; 7056 w = parseFloat(t.offsetWidth);
7057 h = parseFloat(t.offsetHeight); 7058 ea1 = e.split(" "); 7059 for (i = 0; i < props.length; i++) { //if we're dealing with percentages, we must convert things separately for the horizontal and vertical axis! 7060 if (this.p.indexOf("border")) { //older browsers used a prefix 7061 props[i] = _checkPropPrefix(props[i]); 7062 } 7063 bs = bs2 = _getStyle(t, props[i], _cs, false, "0px"); 7064 if (bs.indexOf(" ") !== -1) { 7065 bs2 = bs.split(" "); 7066 bs = bs2[0]; 7067 bs2 = bs2[1]; 7068 } 7069 es = es2 = ea1[i]; 7070 bn = parseFloat(bs); 7071 bsfx = bs.substr((bn + "").length); 7072 rel = (es.charAt(1) === "="); 7073 if (rel) { 7074 en = parseInt(es.charAt(0)+"1", 10); 7075 es = es.substr(2); 7076 en *= parseFloat(es); 7077 esfx = es.substr((en + "").length - (en < 0 ? 1 : 0)) || ""; 7078 } else { 7079 en = parseFloat(es); 7080 esfx = es.substr((en + "").length); 7081 } 7082 if (esfx === "") { 7083 esfx = _suffixMap[p] || bsfx; 7084 } 7085 if (esfx !== bsfx) { 7086 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. 7087 vn = _convertToPixels(t, "borderTop", bn, bsfx); //vertical number 7088 if (esfx === "%") { 7089 bs = (hn / w * 100) + "%"; 7090 bs2 = (vn / h * 100) + "%"; 7091 } else if (esfx === "em") { 7092 em = _convertToPixels(t, "borderLeft", 1, "em"); 7093 bs = (hn / em) + "em"; 7094 bs2 = (vn / em) + "em"; 7095 } else { 7096 bs = hn + "px"; 7097 bs2 = vn + "px"; 7098 } 7099 if (rel) { 7100 es = (parseFloat(bs) + en) + esfx; 7101 es2 = (parseFloat(bs2) + en) + esfx; 7102 } 7103 } 7104 pt = _parseComplex(style, props[i], bs + " " + bs2, es + " " + es2, false, "0px", pt); 7105 } 7106 return pt; 7107 }, prefix:true, formatter:_getFormatter("0px 0px 0px 0px", false, true)}); 7108 _registerComplexSpecialProp("backgroundPosition", {defaultValue:"0 0", parser:function(t, e, p, cssp, pt, plugin) { 7109 var bp = "background-position", 7110 cs = (_cs || _getComputedStyle(t, null)), 7111 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 7112 es = this.format(e), 7113 ba, ea, i, pct, overlap, src; 7114 if ((bs.indexOf("%") !== -1) !== (es.indexOf("%") !== -1)) { 7115 src = _getStyle(t, "backgroundImage").replace(_urlExp, ""); 7116 if (src && src !== "none") { 7117 ba = bs.split(" "); 7118 ea = es.split(" "); 7119 _tempImg.setAttribute("src", src); //set the temp IMG's src to the background-image so that we can measure its width/height 7120 i = 2; 7121 while (--i > -1) { 7122 bs = ba[i]; 7123 pct = (bs.indexOf("%") !== -1); 7124 if (pct !== (ea[i].indexOf("%") !== -1)) { 7125 overlap = (i === 0) ? t.offsetWidth - _tempImg.width : t.offsetHeight - _tempImg.height; 7126 ba[i] = pct ? (parseFloat(bs) / 100 * overlap) + "px" : (parseFloat(bs) / overlap * 100) + "%"; 7127 } 7128 } 7129 bs = ba.join(" "); 7130 } 7131 } 7132 return this.parseComplex(t.style, bs, es, pt, plugin); 7133 }, formatter:_parsePosition}); 7134 _registerComplexSpecialProp("backgroundSize", {defaultValue:"0 0", formatter:_parsePosition}); 7135 _registerComplexSpecialProp("perspective", {defaultValue:"0px", prefix:true}); 7136 _registerComplexSpecialProp("perspectiveOrigin", {defaultValue:"50% 50%", prefix:true}); 7137 _registerComplexSpecialProp("transformStyle", {prefix:true}); 7138 _registerComplexSpecialProp("backfaceVisibility", {prefix:true}); 7139 _registerComplexSpecialProp("userSelect", {prefix:true}); 7140 _registerComplexSpecialProp("margin", {parser:_getEdgeParser("marginTop,marginRight,marginBottom,marginLeft")}); 7141 _registerComplexSpecialProp("padding", {parser:_getEdgeParser("paddingTop,paddingRight,paddingBottom,paddingLeft")}); 7142 _registerComplexSpecialProp("clip", {defaultValue:"rect(0px,0px,0px,0px)", parser:function(t, e, p, cssp, pt, plugin){ 7143 var b, cs, delim; 7144 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. 7145 cs = t.currentStyle; 7146 delim = _ieVers < 8 ? " " : ","; 7147 b = "rect(" + cs.clipTop + delim + cs.clipRight + delim + cs.clipBottom + delim + cs.clipLeft + ")"; 7148 e = this.format(e).split(",").join(delim); 7149 } else { 7150 b = this.format(_getStyle(t, this.p, _cs, false, this.dflt)); 7151 e = this.format(e); 7152 } 7153 return this.parseComplex(t.style, b, e, pt, plugin); 7154 }}); 7155 _registerComplexSpecialProp("textShadow", {defaultValue:"0px 0px 0px #999", color:true, multi:true}); 7156 _registerComplexSpecialProp("autoRound,strictUnits", {parser:function(t, e, p, cssp, pt) {return pt;}}); //just so that we can ignore these properties (not tween them) 7157 _registerComplexSpecialProp("border", {defaultValue:"0px solid #000", parser:function(t, e, p, cssp, pt, plugin) { 7158 return this.parseComplex(t.style, this.format(_getStyle(t, "borderTopWidth", _cs, false, "0px") + " " + _getStyle(t, "borderTopStyle", _cs, false, "solid") + " " + _getStyle(t, "borderTopColor", _cs, false, "#000")), this.format(e), pt, plugin); 7159 }, color:true, formatter:function(v) { 7160 var a = v.split(" "); 7161 return a[0] + " " + (a[1] || "solid") + " " + (v.match(_colorExp) || ["#000"])[0]; 7162 }}); 7163 _registerComplexSpecialProp("borderWidth", {parser:_getEdgeParser("borderTopWidth,borderRightWidth,borderBottomWidth,borderLeftWidth")}); //Firefox doesn't pick up on borderWidth set in style sheets (only inline). 7164 _registerComplexSpecialProp("float,cssFloat,styleFloat", {parser:function(t, e, p, cssp, pt, plugin) { 7165 var s = t.style, 7166 prop = ("cssFloat" in s) ? "cssFloat" : "styleFloat"; 7167 return new CSSPropTween(s, prop, 0, 0, pt, -1, p, false, 0, s[prop], e); 7168 }}); 7169 7170 //opacity-related 7171 var _setIEOpacityRatio = function(v) { 7172 var t = this.t, //refers to the element's style property 7173 filters = t.filter || _getStyle(this.data, "filter") || "", 7174 val = (this.s + this.c * v) | 0, 7175 skip; 7176 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. 7177 if (filters.indexOf("atrix(") === -1 && filters.indexOf("radient(") === -1 && filters.indexOf("oader(") === -1) { 7178 t.removeAttribute("filter"); 7179 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. 7180 } else {
7181 t.filter = filters.replace(_alphaFilterExp, ""); 7182 skip = true; 7183 } 7184 } 7185 if (!skip) { 7186 if (this.xn1) { 7187 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. 7188 } 7189 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 7190 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) 7191 t.filter = filters + " alpha(opacity=" + val + ")"; //we round the value because otherwise, bugs in IE7/8 can prevent "visibility" changes from being applied properly. 7192 } 7193 } else { 7194 t.filter = filters.replace(_opacityExp, "opacity=" + val); 7195 } 7196 } 7197 }; 7198 _registerComplexSpecialProp("opacity,alpha,autoAlpha", {defaultValue:"1", parser:function(t, e, p, cssp, pt, plugin) { 7199 var b = parseFloat(_getStyle(t, "opacity", _cs, false, "1")), 7200 style = t.style, 7201 isAutoAlpha = (p === "autoAlpha"); 7202 if (typeof(e) === "string" && e.charAt(1) === "=") { 7203 e = ((e.charAt(0) === "-") ? -1 : 1) * parseFloat(e.substr(2)) + b; 7204 } 7205 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) 7206 b = 0; 7207 } 7208 if (_supportsOpacity) { 7209 pt = new CSSPropTween(style, "opacity", b, e - b, pt); 7210 } else { 7211 pt = new CSSPropTween(style, "opacity", b * 100, (e - b) * 100, pt); 7212 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. 7213 style.zoom = 1; //helps correct an IE issue. 7214 pt.type = 2; 7215 pt.b = "alpha(opacity=" + pt.s + ")"; 7216 pt.e = "alpha(opacity=" + (pt.s + pt.c) + ")"; 7217 pt.data = t; 7218 pt.plugin = plugin; 7219 pt.setRatio = _setIEOpacityRatio; 7220 } 7221 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 7222 pt = new CSSPropTween(style, "visibility", 0, 0, pt, -1, null, false, 0, ((b !== 0) ? "inherit" : "hidden"), ((e === 0) ? "hidden" : "inherit")); 7223 pt.xs0 = "inherit"; 7224 cssp._overwriteProps.push(pt.n); 7225 cssp._overwriteProps.push(p); 7226 } 7227 return pt; 7228 }}); 7229 7230 7231 var _removeProp = function(s, p) { 7232 if (p) { 7233 if (s.removeProperty) { 7234 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) 7235 p = "-" + p; 7236 } 7237 s.removeProperty(p.replace(_capsExp, "-$1").toLowerCase()); 7238 } else { //note: old versions of IE use "removeAttribute()" instead of "removeProperty()" 7239 s.removeAttribute(p); 7240 } 7241 } 7242 }, 7243 _setClassNameRatio = function(v) { 7244 this.t._gsClassPT = this; 7245 if (v === 1 || v === 0) { 7246 this.t.setAttribute("class", (v === 0) ? this.b : this.e); 7247 var mpt = this.data, //first MiniPropTween 7248 s = this.t.style; 7249 while (mpt) { 7250 if (!mpt.v) { 7251 _removeProp(s, mpt.p); 7252 } else { 7253 s[mpt.p] = mpt.v; 7254 } 7255 mpt = mpt._next; 7256 } 7257 if (v === 1 && this.t._gsClassPT === this) { 7258 this.t._gsClassPT = null; 7259 } 7260 } else if (this.t.getAttribute("class") !== this.e) { 7261 this.t.setAttribute("class", this.e); 7262 } 7263 }; 7264 _registerComplexSpecialProp("className", {parser:function(t, e, p, cssp, pt, plugin, vars) { 7265 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. 7266 cssText = t.style.cssText, 7267 difData, bs, cnpt, cnptLookup, mpt; 7268 pt = cssp._classNamePT = new CSSPropTween(t, p, 0, 0, pt, 2); 7269 pt.setRatio = _setClassNameRatio; 7270 pt.pr = -11; 7271 _hasPriority = true; 7272 pt.b = b; 7273 bs = _getAllStyles(t, _cs); 7274 //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
7274ing that were there just for class-specific values) 7275 cnpt = t._gsClassPT; 7276 if (cnpt) { 7277 cnptLookup = {}; 7278 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. 7279 while (mpt) { 7280 cnptLookup[mpt.p] = 1; 7281 mpt = mpt._next; 7282 } 7283 cnpt.setRatio(1); 7284 } 7285 t._gsClassPT = pt; 7286 pt.e = (e.charAt(1) !== "=") ? e : b.replace(new RegExp("\\s*\\b" + e.substr(2) + "\\b"), "") + ((e.charAt(0) === "+") ? " " + e.substr(2) : ""); 7287 t.setAttribute("class", pt.e); 7288 difData = _cssDif(t, bs, _getAllStyles(t), vars, cnptLookup); 7289 t.setAttribute("class", b); 7290 pt.data = difData.firstMPT; 7291 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). 7292 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) 7293 return pt; 7294 }}); 7295 7296 7297 var _setClearPropsRatio = function(v) { 7298 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). 7299 var s = this.t.style, 7300 transformParse = _specialProps.transform.parse, 7301 a, p, i, clearTransform, transform; 7302 if (this.e === "all") { 7303 s.cssText = ""; 7304 clearTransform = true; 7305 } else { 7306 a = this.e.split(" ").join("").split(","); 7307 i = a.length; 7308 while (--i > -1) { 7309 p = a[i]; 7310 if (_specialProps[p]) { 7311 if (_specialProps[p].parse === transformParse) { 7312 clearTransform = true; 7313 } else { 7314 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" 7315 } 7316 } 7317 _removeProp(s, p); 7318 } 7319 } 7320 if (clearTransform) { 7321 _removeProp(s, _transformProp); 7322 transform = this.t._gsTransform; 7323 if (transform) { 7324 if (transform.svg) { 7325 this.t.removeAttribute("data-svg-origin"); 7326 } 7327 delete this.t._gsTransform; 7328 } 7329 } 7330 7331 } 7332 }; 7333 _registerComplexSpecialProp("clearProps", {parser:function(t, e, p, cssp, pt) { 7334 pt = new CSSPropTween(t, p, 0, 0, pt, 2); 7335 pt.setRatio = _setClearPropsRatio; 7336 pt.e = e; 7337 pt.pr = -10; 7338 pt.data = cssp._tween; 7339 _hasPriority = true; 7340 return pt; 7341 }}); 7342 7343 p = "bezier,throwProps,physicsProps,physics2D".split(","); 7344 i = p.length; 7345 while (i--) { 7346 _registerPluginProp(p[i]); 7347 } 7348 7349 7350 7351 7352 7353 7354 7355 7356 p = CSSPlugin.prototype; 7357 p._firstPT = p._lastParsedTransform = p._transform = null; 7358 7359 //gets called when the tween renders for the first time. This kicks everything off, recording start/end values, etc. 7360 p._onInitTween = function(target, vars, tween) { 7361 if (!target.nodeType) { //css is only for dom elements 7362 return false; 7363 } 7364 this._target = target; 7365 this._tween = tween; 7366 this._vars = vars; 7367 _autoRound = vars.autoRound; 7368 _hasPriority = false; 7369 _suffixMap = vars.suffixMap || CSSPlugin.suffixMap; 7370 _cs = _getComputedStyle(target, ""); 7371 _overwriteProps = this._overwriteProps; 7372 var style = target.style, 7373 v, pt, pt2, first, last, next, zIndex, tpt, threeD; 7374 if (_reqSafariFix) if (style.zIndex === "") {
7375 v = _getStyle(target, "zIndex", _cs); 7376 if (v === "auto" || v === "") { 7377 //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. 7378 this._addLazySet(style, "zIndex", 0); 7379 } 7380 } 7381 7382 if (typeof(vars) === "string") { 7383 first = style.cssText; 7384 v = _getAllStyles(target, _cs); 7385 style.cssText = first + ";" + vars; 7386 v = _cssDif(target, v, _getAllStyles(target)).difs; 7387 if (!_supportsOpacity && _opacityValExp.test(vars)) { 7388 v.opacity = parseFloat( RegExp.$1 ); 7389 } 7390 vars = v; 7391 style.cssText = first; 7392 } 7393 7394 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. 7395 this._firstPT = pt = _specialProps.className.parse(target, vars.className, "className", this, null, null, vars); 7396 } else { 7397 this._firstPT = pt = this.parse(target, vars, null); 7398 } 7399 7400 if (this._transformType) { 7401 threeD = (this._transformType === 3); 7402 if (!_transformProp) { 7403 style.zoom = 1; //helps correct an IE issue. 7404 } else if (_isSafari) { 7405 _reqSafariFix = true; 7406 //if zIndex isn't set, iOS Safari doesn't repaint things correctly sometimes (seemingly at random). 7407 if (style.zIndex === "") { 7408 zIndex = _getStyle(target, "zIndex", _cs); 7409 if (zIndex === "auto" || zIndex === "") { 7410 this._addLazySet(style, "zIndex", 0); 7411 } 7412 } 7413 //Setting WebkitBackfaceVisibility corrects 3 bugs: 7414 // 1) [non-Android] Safari skips rendering changes to "top" and "left" that are made on the same frame/render as a transform update. 7415 // 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. 7416 // 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. 7417 //Note: we allow the user to override the auto-setting by defining WebkitBackfaceVisibility in the vars of the tween. 7418 if (_isSafariLT6) { 7419 this._addLazySet(style, "WebkitBackfaceVisibility", this._vars.WebkitBackfaceVisibility || (threeD ? "visible" : "hidden")); 7420 } 7421 } 7422 pt2 = pt; 7423 while (pt2 && pt2._next) { 7424 pt2 = pt2._next; 7425 } 7426 tpt = new CSSPropTween(target, "transform", 0, 0, null, 2); 7427 this._linkCSSP(tpt, null, pt2); 7428 tpt.setRatio = _transformProp ? _setTransformRatio : _setIETransformRatio; 7429 tpt.data = this._transform || _getTransform(target, _cs, true); 7430 tpt.tween = tween; 7431 tpt.pr = -1; //ensures that the transforms get applied after the components are updated. 7432 _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. 7433 } 7434 7435 if (_hasPriority) { 7436 //reorders the linked list in order of pr (priority) 7437 while (pt) { 7438 next = pt._next; 7439 pt2 = first; 7440 while (pt2 && pt2.pr > pt.pr) { 7441 pt2 = pt2._next; 7442 } 7443 if ((pt._prev = pt2 ? pt2._prev : last)) { 7444 pt._prev._next = pt; 7445 } else { 7446 first = pt; 7447 } 7448 if ((pt._next = pt2)) { 7449 pt2._prev = pt; 7450 } else { 7451 last = pt; 7452 } 7453 pt = next; 7454 } 7455 this._firstPT = first; 7456 } 7457 return true; 7458 }; 7459 7460 7461 p.parse = function(target, vars, pt, plugin) { 7462 var style = target.style, 7463 p, sp, bn, en, bs, es, bsfx, esfx, isStr, rel; 7464 for (p in vars) { 7465 es = vars[p]; //ending value string 7466 sp = _specialProps[p]; //SpecialProp lookup. 7467 if (sp) { 7468 pt = sp.parse(target, es, p, this, pt, plugin, vars); 7469 7470 } else { 7471 bs = _getStyle(target, p, _cs) + ""; 7472 isStr = (typeof(es) === "string"); 7473 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: 7474 if (!isStr) { 7475 es = _parseColor(es); 7476 es = ((es.length > 3) ? "rgba(" : "rgb(") + es.join(",") + ")"; 7477 } 7478 pt = _parseComplex(style, p, bs, es, true, "transparent", pt, 0, plugin); 7479 7480 } else if (isStr && (es.indexOf(" ") !== -1 || es.indexOf(",") !== -1)) { 7481 pt = _parseComplex(style, p, bs, es, true, null, pt, 0, plugin); 7482 7483 } else { 7484 bn = parseFloat(bs); 7485 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. 7486 7487 if (bs === "" || bs === "auto") { 7488 if (p === "width" || p === "height") { 7489 bn = _getDimension(target, p, _cs); 7490 bsfx = "px"; 7491 } else if (p === "left" || p === "top") {
7492 bn = _calculateOffset(target, p, _cs); 7493 bsfx = "px"; 7494 } else { 7495 bn = (p !== "opacity") ? 0 : 1; 7496 bsfx = ""; 7497 } 7498 } 7499 7500 rel = (isStr && es.charAt(1) === "="); 7501 if (rel) { 7502 en = parseInt(es.charAt(0) + "1", 10); 7503 es = es.substr(2); 7504 en *= parseFloat(es); 7505 esfx = es.replace(_suffixExp, ""); 7506 } else { 7507 en = parseFloat(es); 7508 esfx = isStr ? es.replace(_suffixExp, "") : ""; 7509 } 7510 7511 if (esfx === "") { 7512 esfx = (p in _suffixMap) ? _suffixMap[p] : bsfx; //populate the end suffix, prioritizing the map, then if none is found, use the beginning suffix. 7513 } 7514 7515 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. 7516 7517 //if the beginning/ending suffixes don't match, normalize them... 7518 if (bsfx !== esfx) if (esfx !== "") if (en || en === 0) if (bn) { //note: if the beginning value (bn) is 0, we don't need to convert units! 7519 bn = _convertToPixels(target, p, bn, bsfx); 7520 if (esfx === "%") { 7521 bn /= _convertToPixels(target, p, 100, "%") / 100; 7522 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. 7523 bs = bn + "%"; 7524 } 7525 7526 } else if (esfx === "em" || esfx === "rem") { 7527 bn /= _convertToPixels(target, p, 1, esfx); 7528 7529 //otherwise convert to pixels. 7530 } else if (esfx !== "px") { 7531 en = _convertToPixels(target, p, en, esfx); 7532 esfx = "px"; //we don't use bsfx after this, so we don't need to set it to px too. 7533 } 7534 if (rel) if (en || en === 0) { 7535 es = (en + bn) + esfx; //the changes we made affect relative calculations, so adjust the end value here. 7536 } 7537 } 7538 7539 if (rel) { 7540 en += bn; 7541 } 7542 7543 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. 7544 pt = new CSSPropTween(style, p, bn, en - bn, pt, 0, p, (_autoRound !== false && (esfx === "px" || p === "zIndex")), 0, bs, es); 7545 pt.xs0 = esfx; 7546 //DEBUG: _log("tween "+p+" from "+pt.b+" ("+bn+esfx+") to "+pt.e+" with suffix: "+pt.xs0); 7547 } else if (style[p] === undefined || !es && (es + "" === "NaN" || es == null)) { 7548 _log("invalid " + p + " tween value: " + vars[p]); 7549 } else { 7550 pt = new CSSPropTween(style, p, en || bn || 0, 0, pt, -1, p, false, 0, bs, es); 7551 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. 7552 //DEBUG: _log("non-tweening value "+p+": "+pt.xs0); 7553 } 7554 } 7555 } 7556 if (plugin) if (pt && !pt.plugin) { 7557 pt.plugin = plugin; 7558 } 7559 } 7560 return pt; 7561 }; 7562 7563 7564 //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. 7565 p.setRatio = function(v) { 7566 var pt = this._firstPT, 7567 min = 0.000001, 7568 val, str, i; 7569 //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). 7570 if (v === 1 && (this._tween._time === this._tween._duration || this._tween._time === 0)) { 7571 while (pt) { 7572 if (pt.type !== 2) { 7573 if (pt.r && pt.type !== -1) { 7574 val = Math.round(pt.s + pt.c); 7575 if (!pt.type) { 7576 pt.t[pt.p] = val + pt.xs0; 7577 } else if (pt.type === 1) { //complex value (one that typically has multiple numbers inside a string, like "rect(5px,10px,20px,25px)" 7578 i = pt.l; 7579 str = pt.xs0 + val + pt.xs1; 7580 for (i = 1; i < pt.l; i++) {
7581 str += pt["xn"+i] + pt["xs"+(i+1)]; 7582 } 7583 pt.t[pt.p] = str; 7584 } 7585 } else { 7586 pt.t[pt.p] = pt.e; 7587 } 7588 } else { 7589 pt.setRatio(v); 7590 } 7591 pt = pt._next; 7592 } 7593 7594 } else if (v || !(this._tween._time === this._tween._duration || this._tween._time === 0) || this._tween._rawPrevTime === -0.000001) { 7595 while (pt) { 7596 val = pt.c * v + pt.s; 7597 if (pt.r) { 7598 val = Math.round(val); 7599 } else if (val < min) if (val > -min) { 7600 val = 0; 7601 } 7602 if (!pt.type) { 7603 pt.t[pt.p] = val + pt.xs0; 7604 } else if (pt.type === 1) { //complex value (one that typically has multiple numbers inside a string, like "rect(5px,10px,20px,25px)" 7605 i = pt.l; 7606 if (i === 2) { 7607 pt.t[pt.p] = pt.xs0 + val + pt.xs1 + pt.xn1 + pt.xs2; 7608 } else if (i === 3) { 7609 pt.t[pt.p] = pt.xs0 + val + pt.xs1 + pt.xn1 + pt.xs2 + pt.xn2 + pt.xs3; 7610 } else if (i === 4) { 7611 pt.t[pt.p] = pt.xs0 + val + pt.xs1 + pt.xn1 + pt.xs2 + pt.xn2 + pt.xs3 + pt.xn3 + pt.xs4; 7612 } else if (i === 5) { 7613 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; 7614 } else { 7615 str = pt.xs0 + val + pt.xs1; 7616 for (i = 1; i < pt.l; i++) { 7617 str += pt["xn"+i] + pt["xs"+(i+1)]; 7618 } 7619 pt.t[pt.p] = str; 7620 } 7621 7622 } else if (pt.type === -1) { //non-tweening value 7623 pt.t[pt.p] = pt.xs0; 7624 7625 } else if (pt.setRatio) { //custom setRatio() for things like SpecialProps, external plugins, etc. 7626 pt.setRatio(v); 7627 } 7628 pt = pt._next; 7629 } 7630 7631 //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). 7632 } else { 7633 while (pt) { 7634 if (pt.type !== 2) { 7635 pt.t[pt.p] = pt.b; 7636 } else { 7637 pt.setRatio(v); 7638 } 7639 pt = pt._next; 7640 } 7641 } 7642 }; 7643 7644 /** 7645 * @private 7646 * Forces rendering of the target's transforms (rotation, scale, etc.) whenever the CSSPlugin's setRatio() is called. 7647 * Basically, this tells the CSSPlugin to create a CSSPropTween (type 2) after instantiation that runs last in the linked 7648 * list and calls the appropriate (3D or 2D) rendering function. We separate this into its own method so that we can call 7649 * it from other plugins like BezierPlugin if, for example, it needs to apply an autoRotation and this CSSPlugin 7650 * doesn't have any transform-related properties of its own. You can call this method as many times as you 7651 * want and it won't create duplicate CSSPropTweens. 7652 * 7653 * @param {boolean} threeD if true, it should apply 3D tweens (otherwise, just 2D ones are fine and typically faster) 7654 */ 7655 p._enableTransforms = function(threeD) { 7656 this._transform = this._transform || _getTransform(this._target, _cs, true); //ensures that the element has a _gsTransform property with the appropriate values. 7657 this._transformType = (!(this._transform.svg && _useSVGTransformAttr) && (threeD || this._transformType === 3)) ? 3 : 2; 7658 }; 7659 7660 var lazySet = function(v) { 7661 this.t[this.p] = this.e; 7662 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. 7663 }; 7664 /** @private Gives us a way to set a value on the first render (and only the first render). **/ 7665 p._addLazySet = function(t, p, v) { 7666 var pt = this._firstPT = new CSSPropTween(t, p, 0, 0, this._firstPT, 2); 7667 pt.e = v; 7668 pt.setRatio = lazySet; 7669 pt.data = this; 7670 }; 7671 7672 /** @private **/ 7673 p._linkCSSP = function(pt, next, prev, remove) { 7674 if (pt) { 7675 if (next) { 7676 next._prev = pt; 7677 } 7678 if (pt._next) { 7679 pt._next._prev = pt._prev; 7680 } 7681 if (pt._prev) { 7682 pt._prev._next = pt._next; 7683 } else if (this._firstPT === pt) { 7684 this._firstPT = pt._next; 7685 remove = true; //just to prevent resetting this._firstPT 5 lines down in case pt._next is null. (optimized for speed) 7686 } 7687 if (prev) { 7688 prev._next = pt; 7689 } else if (!remove && this._firstPT === null) { 7690 this._firstPT = pt; 7691 } 7692 pt._next = next; 7693 pt._prev = prev; 7694 } 7695 return pt; 7696 }; 7697 7698 //we need to make sure that if alpha or autoAlpha is killed, opacity is too. And autoAlpha affects the "visibility" property. 7699 p._kill = function(lookup) { 7700 var copy = lookup, 7701 pt, p, xfirst; 7702 if (lookup.autoAlpha || lookup.alpha) { 7703 copy = {}; 7704 for (p in lookup) { //copy the lookup so that we're not changing the original which may be passed elsewhere. 7705 copy[p] = lookup[p]; 7706 } 7707 copy.opacity = 1; 7708 if (copy.autoAlpha) { 7709 copy.visibility = 1; 7710 } 7711 } 7712 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". 7713 xfirst = pt.xfirst; 7714 if (xfirst && xfirst._prev) { 7715 this._linkCSSP(xfirst._prev, pt._next, xfirst._prev._prev); //break off the prev 7716 } else if (xfirst === this._firstPT) { 7717 this._firstPT = pt._next; 7718 } 7719 if (pt._next) { 7720 this._linkCSSP(pt._next, pt._next._next, xfirst._prev); 7721 } 7722 this._classNamePT = null; 7723 } 7724 return TweenPlugin.prototype._kill.call(this, copy); 7725 }; 7726 7727 7728 7729 //used by cascadeTo() for gathering all the style properties of each child element into an array for c
7729omparison. 7730 var _getChildStyles = function(e, props, targets) { 7731 var children, i, child, type; 7732 if (e.slice) { 7733 i = e.length; 7734 while (--i > -1) { 7735 _getChildStyles(e[i], props, targets); 7736 } 7737 return; 7738 } 7739 children = e.childNodes; 7740 i = children.length; 7741 while (--i > -1) { 7742 child = children[i]; 7743 type = child.type; 7744 if (child.style) { 7745 props.push(_getAllStyles(child)); 7746 if (targets) { 7747 targets.push(child); 7748 } 7749 } 7750 if ((type === 1 || type === 9 || type === 11) && child.childNodes.length) { 7751 _getChildStyles(child, props, targets); 7752 } 7753 } 7754 }; 7755 7756 /** 7757 * Typically only useful for className tweens that may affect child elements, this method creates a TweenLite 7758 * and then compares the style properties of all the target's child elements at the tween's start and end, and 7759 * if any are different, it also creates tweens for those and returns an array containing ALL of the resulting 7760 * tweens (so that you can easily add() them to a TimelineLite, for example). The reason this functionality is 7761 * wrapped into a separate static method of CSSPlugin instead of being integrated into all regular className tweens 7762 * is because it creates entirely new tweens that may have completely different targets than the original tween, 7763 * so if they were all lumped into the original tween instance, it would be inconsistent with the rest of the API 7764 * and it would create other problems. For example: 7765 * - 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) 7766 * - 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. 7767 * - 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. 7768 * 7769 * @param {Object} target object to be tweened 7770 * @param {number} Duration in seconds (or frames for frames-based tweens) 7771 * @param {Object} Object containing the end values, like {className:"newClass", ease:Linear.easeNone} 7772 * @return {Array} An array of TweenLite instances 7773 */ 7774 CSSPlugin.cascadeTo = function(target, duration, vars) { 7775 var tween = TweenLite.to(target, duration, vars), 7776 results = [tween], 7777 b = [], 7778 e = [], 7779 targets = [], 7780 _reservedProps = TweenLite._internals.reservedProps, 7781 i, difs, p, from; 7782 target = tween._targets || tween.target; 7783 _getChildStyles(target, b, targets); 7784 tween.render(duration, true, true); 7785 _getChildStyles(target, e); 7786 tween.render(0, true, true); 7787 tween._enabled(true); 7788 i = targets.length; 7789 while (--i > -1) { 7790 difs = _cssDif(targets[i], b[i], e[i]); 7791 if (difs.firstMPT) { 7792 difs = difs.difs;
7793 for (p in vars) { 7794 if (_reservedProps[p]) { 7795 difs[p] = vars[p]; 7796 } 7797 } 7798 from = {}; 7799 for (p in difs) { 7800 from[p] = b[i][p]; 7801 } 7802 results.push(TweenLite.fromTo(targets[i], duration, from, difs)); 7803 } 7804 } 7805 return results; 7806 }; 7807 7808 TweenPlugin.activate([CSSPlugin]); 7809 return CSSPlugin; 7810 7811 }, true); 7812 7813}); if (_gsScope._gsDefine) { _gsScope._gsQueue.pop()(); } 7814 7815//export to AMD/RequireJS and CommonJS/Node (precursor to full modular build system coming at a later date) 7816(function(name) { 7817 "use strict"; 7818 var getGlobal = function() { 7819 return (_gsScope.GreenSockGlobals || _gsScope)[name]; 7820 }; 7821 if (typeof(define) === "function" && define.amd) { //AMD 7822 define(["TweenLite"], getGlobal); 7823 } else if (typeof(module) !== "undefined" && module.exports) { //node 7824 require("../TweenLite.js"); 7825 module.exports = getGlobal(); 7826 } 7827}("CSSPlugin")); 7828 7829 7830 7831/*! 7832 * VERSION: beta 0.3.4 7833 * DATE: 2015-08-15 7834 * UPDATES AND DOCS AT: http://greensock.com 7835 * 7836 * @license Copyright (c) 2008-2015, GreenSock. All rights reserved. 7837 * SplitText is a Club GreenSock membership benefit; You must have a valid membership to use 7838 * this code without violating the terms of use. Visit http://www.greensock.com/club/ to sign up or get more details. 7839 * This work is subject to the software agreement that was issued with your membership. 7840 * 7841 * @author: Jack Doyle, [email protected] 7842 */ 7843var _gsScope = (typeof(module) !== "undefined" && module.exports && typeof(global) !== "undefined") ? global : this || window; //helps ensure compatibility with AMD/RequireJS and CommonJS/Node 7844(function(window) { 7845 7846 "use strict"; 7847 var _globals = window.GreenSockGlobals || window, 7848 _namespace = function(ns) { 7849 var a = ns.split("."), 7850 p = _globals, i; 7851 for (i = 0; i < a.length; i++) { 7852 p[a[i]] = p = p[a[i]] || {}; 7853 } 7854 return p; 7855 }, 7856 pkg = _namespace("com.greensock.utils"), 7857 _getText = function(e) { 7858 var type = e.nodeType, 7859 result = ""; 7860 if (type === 1 || type === 9 || type === 11) { 7861 if (typeof(e.textContent) === "string") { 7862 return e.textContent; 7863 } else { 7864 for ( e = e.firstChild; e; e = e.nextSibling ) { 7865 result += _getText(e); 7866 } 7867 } 7868 } else if (type === 3 || type === 4) { 7869 return e.nodeValue; 7870 } 7871 return result; 7872 }, 7873 _doc = document, 7874 _getComputedStyle = _doc.defaultView ? _doc.defaultView.getComputedStyle : function() {}, 7875 _capsExp = /([A-Z])/g, 7876 _getStyle = function(t, p, cs, str) { 7877 var result; 7878 if ((cs = cs || _getComputedStyle(t, null))) { 7879 t = cs.getPropertyValue(p.replace(_capsExp, "-$1").toLowerCase()); 7880 result = (t || cs.length) ? t : cs[p]; //Opera behaves VERY strangely - length is usually 0 and cs[p] is the only way to get accurate results EXCEPT when checking for -o-transform which only works with cs.getPropertyValue()! 7881 } else if (t.currentStyle) { 7882 cs = t.currentStyle; 7883 result = cs[p]; 7884 } 7885 return str ? result : parseInt(result, 10) || 0; 7886 }, 7887 _isArrayLike = function(e) { 7888 return (e.length && e[0] && ((e[0].nodeType && e[0].style && !e.nodeType) || (e[0].length && e[0][0]))) ? true : false; //could be an array of jQuery objects too, so accommodate that. 7889 }, 7890 _flattenArray = function(a) { 7891 var result = [], 7892 l = a.length, 7893 i, e, j; 7894 for (i = 0; i < l; i++) { 7895 e = a[i]; 7896 if (_isArrayLike(e)) { 7897 j = e.length; 7898 for (j = 0; j < e.length; j++) { 7899 result.push(e[j]); 7900 } 7901 } else { 7902 result.push(e); 7903 } 7904 } 7905 return result; 7906 }, 7907 _brSwap = ")eefec303079ad17405c", 7908 _brExp = /(?:<br>|<br\/>|<br \/>)/gi, 7909 _isOldIE = (_doc.all && !_doc.addEventListener), 7910 _divStart = "<div style='position:relative;display:inline-block;" + (_isOldIE ? "*display:inline;*zoom:1;'" : "'"), //note: we must use both display:inline-block and *display:inline for IE8 and earlier, otherwise it won't flow correctly (and if we only use display:inline, IE won't render most of the property tweens - very odd). 7911 _cssClassFunc = function(cssClass) { 7912 cssClass = cssClass || ""; 7913 var iterate = (cssClass.indexOf("++") !== -1), 7914 num = 1; 7915 if (iterate) { 7916 cssClass = cssClass.split("++").join(""); 7917 } 7918 return function() { 7919 return _divStart + (cssClass ? " class='" + cssClass + (iterate ? num++ : "") + "'>" : ">"); 7920 }; 7921 }, 7922 SplitText = pkg.SplitText = _globals.SplitText = function(element, vars) { 7923 if (typeof(element) === "string") { 7924 element = SplitText.selector(element); 7925 } 7926 if (!element) { 7927 throw("cannot split a null element."); 7928 } 7929 this.elements = _isArrayLike(element) ? _flattenArray(element) : [element]; 7930 this.chars = []; 7931 this.words = []; 7932 this.lines = []; 7933 this._originals = []; 7934 this.vars = vars || {}; 7935 this.split(vars); 7936 }, 7937 _swapText = function(element, oldText, newText) { 7938 var type = element.nodeType; 7939 if (type === 1 || type === 9 || type === 11) { 7940 for ( element = element.firstChild; element; element = element.nextSibling ) { 7941 _swapText(element, oldText, newText); 7942 } 7943 } else if (type === 3 || type === 4) { 7944 element.nodeValue = element.nodeValue.split(oldText).join(newText); 7945 } 7946 }, 7947 _pushReversed = function(a, merge) { 7948 var i = merge.length; 7949 while (--i > -1) { 7950 a.push(merge[i]); 7951 } 7952 }, 7953 _split = function(element, vars, allChars, allWords, allLines) { 7954 if (_brExp.test(element.innerHTML)) { 7955 element.innerHTML = element.innerHTML.replace(_brExp, _brSwap); //swap in a unique string for <br/> tags so that we can identify it when we loop through later, and replace it appropriately 7956 } 7957 var text = _getText(element), 7958 types = vars.type || vars.split || "chars,words,lines", 7959 lines = (types.indexOf("lines") !== -1) ? [] : null, 7960 words = (types.indexOf("words") !== -1), 7961 chars = (types.indexOf("chars") !== -1), 7962 absolute = (vars.position === "absolute" || vars.absolute === true), 7963 space = absolute ? "­ " : " ", 7964 lineOffsetY = -999, 7965 cs = _getComputedStyle(element), 7966 paddingLeft = _getStyle(element, "paddingLeft", cs), 7967 borderTopAndBottom = _getStyle(element, "borderBottomWidth", cs) + _get
7967Style(element, "borderTopWidth", cs), 7968 borderLeftAndRight = _getStyle(element, "borderLeftWidth", cs) + _getStyle(element, "borderRightWidth", cs), 7969 padTopAndBottom = _getStyle(element, "paddingTop", cs) + _getStyle(element, "paddingBottom", cs), 7970 padLeftAndRight = _getStyle(element, "paddingLeft", cs) + _getStyle(element, "paddingRight", cs), 7971 textAlign = _getStyle(element, "textAlign", cs, true), 7972 origHeight = element.clientHeight, 7973 origWidth = element.clientWidth, 7974 wordEnd = "</div>", 7975 wordStart = _cssClassFunc(vars.wordsClass), 7976 charStart = _cssClassFunc(vars.charsClass), 7977 iterateLine = ((vars.linesClass || "").indexOf("++") !== -1), 7978 linesClass = vars.linesClass, 7979 hasTagStart = text.indexOf("<") !== -1, 7980 wordIsOpen = true, 7981 charArray = [], 7982 wordArray = [], 7983 lineArray = [], 7984 l, curLine, isChild, splitText, i, j, character, nodes, node, offset, lineNode, style, lineWidth, addWordSpaces; 7985 7986 if (iterateLine) { 7987 linesClass = linesClass.split("++").join(""); 7988 } 7989 if (hasTagStart) { 7990 text = text.split("<").join("{{LT}}"); //we can't leave "<" in the string, or when we set the innerHTML, it can be interpreted as 7991 } 7992 l = text.length; 7993 7994 splitText = wordStart(); 7995 for (i = 0; i < l; i++) { 7996 character = text.charAt(i); 7997 if (character === ")" && text.substr(i, 20) === _brSwap) { 7998 splitText += (wordIsOpen ? wordEnd : "") + "<BR/>"; 7999 wordIsOpen = false; 8000 if (i !== l - 20 && text.substr(i+20, 20) !== _brSwap) { 8001 splitText += " " + wordStart(); 8002 wordIsOpen = true; 8003 } 8004 i += 19; 8005 8006 } else if (character === " " && text.charAt(i-1) !== " " && i !== l-1 && text.substr(i-20, 20) !== _brSwap) { 8007 splitText += wordIsOpen ? wordEnd : ""; 8008 wordIsOpen = false; 8009 while (text.charAt(i + 1) === " ") { //skip over empty spaces (to avoid making them words) 8010 splitText += space; 8011 i++; 8012 } 8013 if (text.charAt(i + 1) !== ")" || text.substr(i + 1, 20) !== _brSwap) { 8014 splitText += space + wordStart(); 8015 wordIsOpen = true; 8016 } 8017 8018 } else if (character === "{" && text.substr(i, 6) === "{{LT}}") { 8019 splitText += chars ? charStart() + "{{LT}}" + "</div>" : "{{LT}}"; 8020 i += 5; 8021 } else { 8022 splitText += (chars && character !== " ") ? charStart() + character + "</div>" : character; 8023 } 8024 } 8025 element.innerHTML = splitText + (wordIsOpen ? wordEnd : ""); 8026 8027 if (hasTagStart) { 8028 _swapText(element, "{{LT}}", "<"); 8029 } 8030 //copy all the descendant nodes into an array (we can't use a regular nodeList because it's live and we may need to renest things) 8031 j = element.getElementsByTagName("*"); 8032 l = j.length; 8033 nodes = []; 8034 for (i = 0; i < l; i++) { 8035 nodes[i] = j[i]; 8036 } 8037 8038 //for absolute positioning, we need to record the x/y offsets and width/height for every <div>. And even if we're not positioning things absolutely, in order to accommodate lines, we must figure out where the y offset changes so that we can sense where the lines break, and we populate the lines array. 8039 if (lines || absolute) { 8040 for (i = 0; i < l; i++) { 8041 node = nodes[i]; 8042 isChild = (node.parentNode === element); 8043 if (isChild || absolute || (chars && !words)) { 8044 offset = node.offsetTop; 8045 if (lines && isChild && offset !== lineOffsetY && node.nodeName !== "BR") { 8046 curLine = []; 8047 lines.push(curLine); 8048 lineOffsetY = offset; 8049 } 8050 if (absolute) { //record offset x and y, as well as width and height so that we can access them later for positioning. Grabbing them at once ensures we don't trigger a browser paint & we maximize performance. 8051 node._x = node.offsetLeft; 8052 node._y = offset; 8053 node._w = node.offsetWidth; 8054 node._h = node.offsetHeight; 8055 } 8056 if (lines) { 8057 if (words === isChild || !chars) { 8058 curLine.push(node); 8059 node._x -= paddingLeft; 8060 } 8061 if (isChild && i) { 8062 nodes[i-1]._wordEnd = true; 8063 } 8064 if (node.nodeName === "BR" && node.nextSibling && node.nextSibling.nodeName === "BR") { //two consecutive <br> tags signify a new [empty] line. 8065 lines.push([]); 8066 } 8067 } 8068 } 8069 } 8070 } 8071 8072 for (i = 0; i < l; i++) { 8073 node = nodes[i];
8074 isChild = (node.parentNode === element); 8075 if (node.nodeName === "BR") { 8076 if (lines || absolute) { 8077 element.removeChild(node); 8078 nodes.splice(i--, 1); 8079 l--; 8080 } else if (!words) { 8081 element.appendChild(node); 8082 } 8083 continue; 8084 } 8085 8086 if (absolute) { 8087 style = node.style; 8088 if (!words && !isChild) { 8089 node._x += node.parentNode._x; 8090 node._y += node.parentNode._y; 8091 } 8092 style.left = node._x + "px"; 8093 style.top = node._y + "px"; 8094 style.position = "absolute"; 8095 style.display = "block"; 8096 //if we don't set the width/height, things collapse in older versions of IE and the origin for transforms is thrown off in all browsers. 8097 style.width = (node._w + 1) + "px"; //IE is 1px short sometimes. Avoid wrapping 8098 style.height = node._h + "px"; 8099 } 8100 8101 if (!words) { 8102 //we always start out wrapping words in their own <div> so that line breaks happen correctly, but here we'll remove those <div> tags if necessary and renest the characters directly into the element rather than inside the word <div> 8103 if (isChild) { 8104 element.removeChild(node); 8105 nodes.splice(i--, 1); 8106 l--; 8107 } else if (!isChild && chars) { 8108 offset = (!lines && !absolute && node.nextSibling); //if this is the last letter in the word (and we're not breaking by lines and not positioning things absolutely), we need to add a space afterwards so that the characters don't just mash together 8109 element.appendChild(node); 8110 if (!offset) { 8111 element.appendChild(_doc.createTextNode(" ")); 8112 } 8113 charArray.push(node); //TODO: push() 8114 } 8115 } else if (isChild && node.innerHTML !== "") { 8116 wordArray.push(node); //TODO: push() 8117 } else if (chars) { 8118 charArray.push(node); //TODO: push() 8119 } 8120 } 8121 8122 if (lines) { 8123 //the next 7 lines just give us the line width in the most reliable way and figure out the left offset (if position isn't relative or absolute). We must set the width along with text-align to ensure everything works properly for various alignments. 8124 if (absolute) { 8125 lineNode = _doc.createElement("div"); 8126 element.appendChild(lineNode); 8127 lineWidth = lineNode.offsetWidth + "px"; 8128 offset = (lineNode.offsetParent === element) ? 0 : element.offsetLeft; 8129 element.removeChild(lineNode); 8130 } 8131 style = element.style.cssText; 8132 element.style.cssText = "display:none;"; //to improve performance, set display:none on the element so that the browser doesn't have to worry about reflowing or rendering while we're renesting things. We'll revert the cssText later. 8133 //we can't use element.innerHTML = "" because that causes IE to literally delete all the nodes and their content even though we've stored them in an array! So we must loop through the children and remove them. 8134 while (element.firstChild) { 8135 element.removeChild(element.firstChild); 8136 } 8137 addWordSpaces = (!absolute || (!words && !chars)); 8138 for (i = 0; i < lines.length; i++) { 8139 curLine = lines[i]; 8140 lineNode = _doc.createElement("div"); 8141 lineNode.style.cssText = "display:block;text-align:" + textAlign + ";position:" + (absolute ? "absolute;" : "relative;"); 8142 if (linesClass) { 8143 lineNode.className = linesClass + (iterateLine ? i+1 : ""); 8144 } 8145 lineArray.push(lineNode); //TODO: push() 8146 l = curLine.length; 8147 for (j = 0; j < l; j++) { 8148 if (curLine[j].nodeName !== "BR") { 8149 node = curLine[j]; 8150 lineNode.appendChild(node); 8151 if (addWordSpaces && (node._wordEnd || words)) { 8152 lineNode.appendChild(_doc.createTextNode(" ")); 8153 } 8154 if (absolute) { 8155 if (j === 0) { 8156 lineNode.style.top = node._y + "px"; 8157 lineNode.style.left = (paddingLeft + offset) + "px"; 8158 } 8159 node.style.top = "0px"; 8160 if (offset) { 8161 node.style.left = (node._x - offset) + "px"; 8162 } 8163 } 8164 } 8165 } 8166 if (l === 0) { //if there are no nodes in the line (typically meaning there were tw
8166o consecutive <br> tags, just add a non-breaking space so that things display properly. 8167 lineNode.innerHTML = " "; 8168 } 8169 if (!words && !chars) { 8170 lineNode.innerHTML = _getText(lineNode).split(String.fromCharCode(160)).join(" "); 8171 } 8172 if (absolute) { 8173 lineNode.style.width = lineWidth; 8174 lineNode.style.height = node._h + "px"; 8175 } 8176 element.appendChild(lineNode); 8177 } 8178 element.style.cssText = style; 8179 } 8180 8181 //if everything shifts to being position:absolute, the container can collapse in terms of height or width, so fix that here. 8182 if (absolute) { 8183 if (origHeight > element.clientHeight) { 8184 element.style.height = (origHeight - padTopAndBottom) + "px"; 8185 if (element.clientHeight < origHeight) { //IE8 and earlier use a different box model - we must include padding and borders 8186 element.style.height = (origHeight + borderTopAndBottom)+ "px"; 8187 } 8188 } 8189 if (origWidth > element.clientWidth) { 8190 element.style.width = (origWidth - padLeftAndRight) + "px"; 8191 if (element.clientWidth < origWidth) { //IE8 and earlier use a different box model - we must include padding and borders 8192 element.style.width = (origWidth + borderLeftAndRight)+ "px"; 8193 } 8194 } 8195 } 8196 _pushReversed(allChars, charArray); 8197 _pushReversed(allWords, wordArray); 8198 _pushReversed(allLines, lineArray); 8199 }, 8200 p = SplitText.prototype; 8201 8202 p.split = function(vars) { 8203 if (this.isSplit) { 8204 this.revert(); 8205 } 8206 this.vars = vars || this.vars; 8207 this._originals.length = this.chars.length = this.words.length = this.lines.length = 0; 8208 var i = this.elements.length; 8209 //we split in reversed order so that if/when we position:absolute elements, they don't affect the position of the ones after them in the document flow (shifting them up as they're taken out of the document flow). 8210 while (--i > -1) { 8211 this._originals[i] = this.elements[i].innerHTML; 8212 _split(this.elements[i], this.vars, this.chars, this.words, this.lines); 8213 } 8214 this.chars.reverse(); 8215 this.words.reverse(); 8216 this.lines.reverse(); 8217 this.isSplit = true; 8218 return this; 8219 }; 8220 8221 p.revert = function() { 8222 if (!this._originals) { 8223 throw("revert() call wasn't scoped properly."); 8224 } 8225 var i = this._originals.length; 8226 while (--i > -1) { 8227 this.elements[i].innerHTML = this._originals[i]; 8228 } 8229 this.chars = []; 8230 this.words = []; 8231 this.lines = []; 8232 this.isSplit = false; 8233 return this; 8234 }; 8235 8236 SplitText.selector = window.$ || window.jQuery || function(e) { 8237 var selector = window.$ || window.jQuery; 8238 if (selector) { 8239 SplitText.selector = selector; 8240 return selector(e); 8241 } 8242 return (typeof(document) === "undefined") ? e : (document.querySelectorAll ? document.querySelectorAll(e) : document.getElementById((e.charAt(0) === "#") ? e.substr(1) : e)); 8243 }; 8244 SplitText.version = "0.3.4"; 8245 8246})(_gsScope); 8247 8248//export to AMD/RequireJS and CommonJS/Node (precursor to full modular build system coming at a later date) 8249(function(name) { 8250 "use strict"; 8251 var getGlobal = function() { 8252 return (_gsScope.GreenSockGlobals || _gsScope)[name]; 8253 }; 8254 if (typeof(define) === "function" && define.amd) { //AMD 8255 define(["TweenLite"], getGlobal); 8256 } else if (typeof(module) !== "undefined" && module.exports) { //node 8257 module.exports = getGlobal(); 8258 } 8259}("SplitText")); 8260 8261 8262// Sandboxing Greensock 8263try{ 8264 window.GreenSockGlobals = null; 8265 window._gsQueue = null; 8266 window._gsDefine = null; 8267 8268 delete(window.GreenSockGlobals); 8269 delete(window._gsQueue); 8270 delete(window._gsDefine); 8271 } catch(e) {} 8272 8273try{ 8274 window.GreenSockGlobals = oldgs; 8275 window._gsQueue = oldgs_queue; 8276 } catch(e) {} 8277 8278if (window.tplogs==true) 8279 try { 8280 console.groupEnd(); 8281 } catch(e) {} 8282 8283 8284;(function (factory) { 8285 if (typeof define === 'function' && define.amd) { 8286 // AMD. Register as an anonymous module. 8287 define(['jquery'], factory); 8288 } else if (typeof exports === 'object') { 8289 // CommonJS / nodejs module 8290 module.exports = factory(require('jquery')); 8291 } else { 8292 // Browser globals 8293 factory(jQuery); 8294 } 8295}(function ($) { 8296 // Namespace all events. 8297 var eventNamespace = 'waitForImages'; 8298 8299 // CSS properties which contain references to images. 8300 $.waitForImages = { 8301 hasImageProperties: [ 8302 'backgroundImage', 8303 'listStyleImage', 8304 'borderImage', 8305 'borderCornerImage', 8306 'cursor' 8307 ], 8308 hasImageAttributes: ['srcset'] 8309 }; 8310 8311 // Custom selector to find all `img` elements with a valid `src` attribute. 8312 //theme 8313 // $.expr[':']['has-src'] = function (obj) { 8314 // // Ensure we are dealing with an `img` element with a valid 8315 // // `src` attribute. 8316 // return $(obj).is('img[src][src!=""]'); 8317 // }; 8318 8319 // Custom selector to find images which are not already cached by the 8320 // browser. 8321 //theme 8322 // $.expr[':'].uncached = function (obj) { 8323 // // Ensure we are dealing with an `img` element with a valid 8324 // // `src` attribute. 8325 // if (!$(obj).is(':has-src')) { 8326 // return false; 8327 // } 8328 8329 // return !obj.complete; 8330 // }; 8331 8332 $.fn.waitForImages = function () { 8333 8334 var allImgsLength = 0; 8335 var allImgsLoaded = 0; 8336 var deferred = $.Deferred(); 8337 8338 var finishedCallback; 8339 var eachCallback; 8340 var waitForAll; 8341 8342 // Handle options object (if passed). 8343 if ($.isPlainObject(arguments[0])) { 8344 8345 waitForAll = arguments[0].waitForAll; 8346 eachCallback = arguments[0].each; 8347 finishedCallback = arguments[0].finished; 8348 8349 } else { 8350 8351 // Handle if using deferred object and only one param was passed in. 8352 if (arguments.length === 1 && $.type(arguments[0]) === 'boolean') { 8353 waitForAll = arguments[0]; 8354 } else { 8355 finishedCallback = arguments[0]; 8356 eachCallback = arguments[1]; 8357 waitForAll = arguments[2]; 8358 } 8359 8360 } 8361 8362 // Handle missing callbacks. 8363 finishedCallback = finishedCallback || $.noop; 8364 eachCallback = eachCallback || $.noop; 8365 8366 // Convert waitForAll to Boolean 8367 waitForAll = !! waitForAll; 8368 8369 // Ensure callbacks are functions. 8370 if (!$.isFunction(finishedCallback) || !$.isFunction(eachCallback)) { 8371 throw new TypeError('An invalid callback was supplied.'); 8372 } 8373 8374 this.each(function () { 8375 // Build a list of all imgs, dependent on what images will 8376 // be considered. 8377 var obj = $(this); 8378 var allImgs = []; 8379 // CSS properties which may contain an image. 8380 var hasImgProperties = $.waitForImages.hasImageProperties || []; 8381 // Element attributes which may contain an image. 8382 var hasImageAttributes = $.waitForImages.hasImageAttributes || []; 8383 // To match `url()` references. 8384 // Spec: http://www.w3.org/TR/CSS2/syndata.html#value-def-uri 8385 var matchUrl = /url\(\s*(['"]?)(.*?)\1\s*\)/g; 8386 8387 if (waitForAll) { 8388 8389 // Get all elements (including the original), as any one of 8390 // them could have a background image. 8391 obj.find('*').addBack().each(function () { 8392 var element = $(this); 8393 8394 // If an `img` element, add it. But keep iterating in 8395 // case it has a background image too. 8396 if (element.is('img:has-src')) { 8397 allImgs.push({ 8398 src: element.attr('src'), 8399 element: element[0] 8400 }); 8401 } 8402 8403 $.each(hasImgProperties, function (i, property) { 8404 var propertyValue = element.css(property); 8405 var match; 8406 8407 // If it doesn't contain this property, skip. 8408 if (!propertyValue) { 8409 return true; 8410 } 8411 8412 // Get all url() of this element. 8413 while (match = matchUrl.exec(propertyValue)) { 8414 allImgs.push({ 8415 src: match[2], 8416 element: element[0] 8417 }); 8418 } 8419 }); 8420 8421 $.each(hasImageAttributes, function (i, attribute) { 8422 var attributeValue = element.attr(attribute); 8423 var attributeValues; 8424 8425 // If it doesn't contain this property, skip. 8426 if (!attributeValue) { 8427 return true; 8428 } 8429 8430 // Check for multiple comma separated images 8431 attributeValues = attributeValue.split(','); 8432 8433 $.each(attributeValues, function(i, value) { 8434 // Trim value and get string before first 8435 // whitespace (for use with srcset). 8436 value = $.trim(value).split(' ')[0]; 8437 allImgs.push({ 8438 src: value, 8439 element: element[0] 8440 }); 8441 }); 8442 }); 8443 }); 8444 } else { 8445 // For images only, the task is simpler. 8446 obj.find('img:has-src') 8447 .each(function () { 8448 allImgs.push({ 8449 src: this.src, 8450 element: this 8451 }); 8452 }); 8453 } 8454 8455 allImgsLength = allImgs.length; 8456 allImgsLoaded = 0; 8457 8458 // If no images found, don't bother. 8459 if (allImgsLength === 0) { 8460 finishedCallback.call(obj[0]); 8461 deferred.resolveWith(obj[0]); 8462 } 8463 8464 $.each(allImgs, function (i, img) { 8465 8466 var image = new Image(); 8467 var events = 8468 'load.' + eventNamespace + ' error.' + eventNamespace; 8469 8470 // Handle the image loading and error with the same callback. 8471 $(image).one(events, function me (event) { 8472 // If an error occurred with loading the image, set the 8473 // third argument accordingly. 8474 var eachArguments = [ 8475 allImgsLoaded, 8476 allImgsLength, 8477 event.type == 'load' 8478 ]; 8479 allImgsLoaded++; 8480 8481 eachCallback.apply(img.element, eachArguments); 8482 deferred.notifyWith(img.element, eachArguments); 8483 8484 // Unbind the event listeners. I use this in addition to 8485 // `one` as one of those events won't be called (either 8486 // 'load' or 'error' will be called). 8487 $(this).off(events, me); 8488 8489 if (allImgsLoaded == allImgsLength) { 8490 finishedCallback.call(obj[0]); 8491 deferred.resolveWith(obj[0]); 8492 return false; 8493 } 8494 8495 }); 8496 8497 image.src = img.src; 8498 }); 8499 }); 8500 8501 return deferred.promise(); 8502 8503 }; 8504})); 8505
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.