1/* eslint-disable */ 2 3/*! 4 * VERSION: 0.1.7 5 * DATE: 2018-02-15 6 * UPDATES AND DOCS AT: http://greensock.com 7 * 8 * @license Copyright (c) 2008-2018, GreenSock. All rights reserved. 9 * GSDevTools is a Club GreenSock membership benefit; You must have a valid membership to use 10 * this code without violating the terms of use. Visit http://greensock.com/club/ to sign up or get more details. 11 * This work is subject to the software agreement that was issued with your membership. 12 * 13 * @author: Jack Doyle, [email protected] 14 **/ 15 16var _gsScope = (typeof(module) !== "undefined" && module.exports && typeof(global) !== "undefined") ? global : this || window; //helps ensure compatibility with AMD/RequireJS and CommonJS/Node 17(_gsScope._gsQueue || (_gsScope._gsQueue = [])).push( function() { 18 19 "use strict"; 20 21 _gsScope._gsDefine("GSDevTools", ["TweenLite", "core.Animation", "core.SimpleTimeline", "TimelineLite", "utils.Draggable", "plugins.CSSPlugin"], function(TweenLite, Animation, SimpleTimeline, TimelineLite, Draggable) { 22 23 var _doc = document, 24 _docElement = _doc.documentElement, 25 _svgNS = "http://www.w3.org/2000/svg", 26 _domNS = "http://www.w3.org/1999/xhtml", 27 _idSeed = 0, //we assign an ID to each GSDevTools instance so that we can segregate the sessionStorage data accordingly. 28 _lookup = {}, 29 _createElement = function(type, container, cssText) { 30 var element = _doc.createElementNS ? _doc.createElementNS(type === "svg" ? _svgNS : _domNS, type) : _doc.createElement(type); 31 if (container) { 32 if (typeof(container) === "string") { 33 container = _doc.querySelector(container); 34 } 35 container.appendChild(element); 36 } 37 if (type === "svg") { 38 element.setAttribute("xmlns", _svgNS); 39 element.setAttribute("xmlns:xlink", _domNS); 40 } 41 if (cssText) { 42 element.style.cssText = cssText; 43 } 44 return element; 45 }, 46 _clearSelection = function() { 47 if (_doc.selection) { 48 _doc.selection.empty() 49 } else if (window.getSelection) { 50 window.getSelection().removeAllRanges(); 51 } 52 }, 53 _globalTimeline = Animation._rootTimeline, 54 _getChildrenOf = function(timeline, includeTimelines) { 55 var a = [], 56 cnt = 0, 57 tween = timeline._first; 58 while (tween) { 59 if (tween instanceof TweenLite) { 60 if (tween.vars.id) { 61 a[cnt++] = tween; 62 } 63 } else { 64 if (includeTimelines && tween.vars.id) { 65 a[cnt++] = tween; 66 } 67 a = a.concat(_getChildrenOf(tween, includeTimelines)); 68 cnt = a.length; 69 } 70 tween = tween._next; 71 } 72 return a; 73 }, 74 //eliminates any very long repeating children (direct children, not deeply nested) and returns the duration accordingly. 75 _getClippedDuration = function(animation) { 76 var max = 0, 77 t = animation._first; 78 if (!t) { 79 return animation.duration(); 80 } 81 while (t) { 82 max = Math.max(max, t.totalDuration() > 999 ? t.endTime(false) : t._startTime + t._totalDuration / t._timeScale); 83 t = t._next; 84 } 85 return max; 86 }, 87 _getAnimationById = function(id) { 88 if (!id) { 89 return null; 90 } 91 if (id instanceof Animation) { 92 return id; 93 } 94 var a = _getChildrenOf(_globalTimeline, true), 95 i = a.length; 96 while (--i > -1) { 97 if (a[i].vars.id === id) { 98 return a[i]; 99 } 100 } 101 }, 102 _timeToProgress = function(time, animation, defaultValue, relativeProgress) { 103 var add, i, a; 104 if (typeof(time) === "string") { 105 if (time.charAt(1) === "=") { 106 add = parseInt(time.charAt(0) + "1", 10) * parseFloat(time.substr(2)); 107 if (add < 0 && relativeProgress === 0) { //if something like inTime:"-=2", we measure it from the END, not the beginning 108 relativeProgress = 100; 109 } 110 time = (relativeProgress / 100 * animation.duration()) + add; 111 } else if (isNaN(time) && animation.getLabelTime && animation.getLabelTime(time) !== -1) { 112 time = animation.getLabelTime(time); 113 } else if (animation === _recordedRoot) { //perhaps they defined an id of an animation, like "myAnimation+=2" 114 i = time.indexOf("="); 115 if (i > 0) { 116 add = parseInt(time.charAt(i-1) + "1", 10) * parseFloat(time.substr(i+1)); 117 time = time.substr(0, i-1); 118 } else { 119 add = 0; 120 } 121 a = _getAnimationById(time); 122 if (a) { 123 time = _getGlobalTime(a, defaultValue / 100 * a.duration()) + add; 124 } 125 } 126 } 127 128 time = isNaN(time) ? defaultValue : parseFloat(time); 129 return Math.min(100, Math.max(0, time / animation.duration() * 100)); 130 }, 131 132 _getGlobalTime = function(animation, time) { 133 var tl = animation; 134 time = time || 0; 135 if (tl.timeline) { 136 while (tl.timeline.timeline) { 137 time = (time / tl._timeScale) + tl._startTime; 138 tl = tl.timeline; 139 } 140 } 141 return time; 142 }, 143 144 145 _addedCSS, 146 _createRootElement = function(element, minimal, css) { 147 if (!_addedCSS) { 148 _createElement("style", _docElement).innerHTML = '.gs-dev-tools{height:51px;bottom:0;left:0;right:0;display:block;position:fixed;overflow:visible;padding:0}.gs-dev-tools *{box-sizing:content-box;visibility:visible}.gs-dev-tools .gs-top{position:relative;z-index:499}.gs-dev-tools .gs-bottom{display:flex;align-items:center;justify-content:space-between;background-color:rgba(0,0,0,.6);height:42px;border-top:1px solid #999;position:relative}.gs-dev-tools .timeline{position:relative;height:8px;margin-left:15px;margin-right:15px;overflow:visible}.gs-dev-tools .progress-bar,.gs-dev-tools .timeline-track{height:8px;width:100%;position:absolute;top:0;left:0}.gs-dev-tools .timeline-track{background-color:#999;opacity:.6}.gs-dev-tools .progress-bar{background-color:#91e600;height:8px;top:0;width:0;pointer-events:none}.gs-dev-tools .seek-bar{width:100%;position:absolute;height:24px;top:-12px;left:0;background-color:transparent}
148.gs-dev-tools .in-point,.gs-dev-tools .out-point{width:15px;height:26px;position:absolute;top:-18px}.gs-dev-tools .in-point-shape{fill:#6d9900;stroke:rgba(0,0,0,.5);stroke-width:1}.gs-dev-tools .out-point-shape{fill:#994242;stroke:rgba(0,0,0,.5);stroke-width:1}.gs-dev-tools .in-point{transform:translateX(-100%)}.gs-dev-tools .out-point{left:100%}.gs-dev-tools .grab{stroke:rgba(255,255,255,.3);stroke-width:1}.gs-dev-tools .playhead{position:absolute;top:-5px;transform:translate(-50%,0);left:0;border-radius:50%;width:16px;height:16px;border:1px solid #6d9900;background-color:#91e600}.gs-dev-tools .gs-btn-white{fill:#fff}.gs-dev-tools .pause{opacity:0}.gs-dev-tools .select-animation{vertical-align:middle;position:relative;padding:6px 10px}.gs-dev-tools .select-animation-container{flex-grow:4;width:40%}.gs-dev-tools .select-arrow{display:inline-block;width:12px;height:7px;margin:0 7px;transform:translate(0,-2px)}.gs-dev-tools .select-arrow-shape{stroke:rgba(255,255,255,.6);stroke-width:2px;fill:none}.gs-dev-tools .rewind{height:16px;width:19px;padding:10px 4px;min-width:24px}.gs-dev-tools .rewind-path{opacity:.6}.gs-dev-tools .play-pause{width:24px;height:24px;padding:6px 10px;min-width:24px}.gs-dev-tools .ease{width:30px;height:30px;padding:10px;min-width:30px;display:none}.gs-dev-tools .ease-path{fill:none;stroke:rgba(255,255,255,.6);stroke-width:2px}.gs-dev-tools .ease-border{fill:rgba(255,255,255,.25)}.gs-dev-tools .time-scale{font-family:monospace;font-size:18px;text-align:center;color:rgba(255,255,255,.6);padding:4px 4px 4px 0;min-width:30px;margin-left:7px}.gs-dev-tools .loop{width:20px;padding:5px;min-width:20px}.gs-dev-tools .loop-path{fill:rgba(255,255,255,.6)}.gs-dev-tools label span{color:#fff;font-family:monospace;text-decoration:none;font-size:16px;line-height:18px}.gs-dev-tools .time-scale span{color:rgba(255,255,255,.6)}.gs-dev-tools button:focus,.gs-dev-tools select:focus{outline:0}.gs-dev-tools label{position:relative;cursor:pointer}.gs-dev-tools label.locked{text-decoration:none;cursor:auto}.gs-dev-tools label input,.gs-dev-tools label select{position:absolute;left:0;top:0;z-index:1;font:inherit;font-size:inherit;line-height:inherit;height:100%;width:100%;color:#000!important;opacity:0;background:0 0;border:none;padding:0;margin:0;-webkit-appearance:none;-moz-appearance:none;appearance:none;cursor:pointer}.gs-dev-tools label input+.display{position:relative;z-index:2}.gs-dev-tools .gs-bottom-right{vertical-align:middle;display:flex;align-items:center;flex-grow:4;width:40%;justify-content:flex-end}.gs-dev-tools .time-container{font-size:18px;font-family:monospace;color:rgba(255,255,255,.6);margin:0 5px}.gs-dev-tools .logo{width:32px;height:32px;position:relative;top:2px;margin:0 12px}.gs-dev-tools .gs-hit-area{background-color:transparent;width:100%;height:100%;top:0;position:absolute}.gs-dev-tools.minimal{height:auto;display:flex;align-items:stretch}.gs-dev-tools.minimal .gs-top{order:2;flex-grow:4;background-color:rgba(0,0,0,1)}.gs-dev-tools.minimal .gs-bottom{background-color:rgba(0,0,0,1);border-top:none}.gs-dev-tools.minimal .timeline{top:50%;transform:translate(0,-50%)}.gs-dev-tools.minimal .in-point,.gs-dev-tools.minimal .out-point{display:none}.gs-dev-tools.minimal .select-animation-container{display:none}.gs-dev-tools.minimal .rewind{display:none}.gs-dev-tools.minimal .play-pause{width:20px;height:20px;padding:4px 6px;margin-left:14px}.gs-dev-tools.minimal .time-scale{min-width:26px}.gs-dev-tools.minimal .loop{width:18px;min-width:18px;display:none}.gs-dev-tools.minimal .gs-bottom-right{display:none}@media only screen and (max-width:600px){.gs-dev-tools{height:auto;display:flex;align-items:stretch}.gs-dev-tools .gs-top{order:2;flex-grow:4;background-color:rgba(0,0,0,1);height:42px}.gs-dev-tools .gs-bottom{background-color:rgba(0,0,0,1);border-top:none}.gs-dev-tools .timeline{top:50%;transform:translate(0,-50%)}.gs-dev-tools .in-point,.gs-dev-tools .out-point{display:none}.gs-dev-tools .select-animation-container{display:none}.gs-dev-tools .rewind{display:none}.gs-dev-tools .play-pause{width:20px;height:20px;padding:4px 6px;margin-left:14px}.gs-dev-tools .time-scale{min-width:26px}.gs-dev-tools .loop{width:18px;min-width:18px;display:none}.gs-dev-tools .gs-bottom-right{display:none}}'; 149 _addedCSS = true; 150 } 151 if (typeof(element) === "string") { 152 element = document.querySelector(element); 153 } 154 var root = _createElement("div", element || _docElement.getElementsByTagName("body")[0] || _docElement); 155 root.setAttribute("class", "gs-dev-tools" + (minimal ? " minimal" : "")); 156 root.innerHTML = '<div class=gs-hit-area></div><div class=gs-top><div class=timeline><div class=timeline-track></div><div class=progress-bar></div><div class=seek-bar></div>
156<svg class=in-point viewBox="0 0 15 26"xmlns=http://www.w3.org/2000/svg><polygon class=in-point-shape points=".5 .5 14.5 .5 14.5 25.5 .5 17.5"/><polyline class=grab points="5.5 4 5.5 15"/><polyline class=grab points="9.5 4 9.5 17"/></svg> <svg class=out-point viewBox="0 0 15 26"xmlns=http://www.w3.org/2000/svg><polygon class=out-point-shape points=".5 .5 14.5 .5 14.5 17.5 .5 25.5"/><polyline class=grab points="5.5 4 5.5 17"/><polyline class=grab points="9.5 4 9.5 15"/></svg><div class=playhead></div></div></div><div class=gs-bottom><div class=select-animation-container><label class=select-animation><select class=animation-list><option>Global Timeline<option>myTimeline</select><nobr><span class="display animation-label">Global Timeline</span> <svg class=select-arrow viewBox="0 0 12.05 6.73"xmlns=http://www.w3.org/2000/svg><polyline class=select-arrow-shape points="0.35 0.35 6.03 6.03 11.7 0.35"/></svg></nobr></label></div><svg class=rewind viewBox="0 0 12 15.38"xmlns=http://www.w3.org/2000/svg><path d=M0,.38H2v15H0Zm2,7,10,7.36V0Z class="gs-btn-white rewind-path"/></svg> <svg class=play-pause viewBox="0 0 20.97 25.67"xmlns=http://www.w3.org/2000/svg><g class=play><path d="M8,4.88 C8,10.18 8,15.48 8,20.79 5.33,22.41 2.66,24.04 0,25.67 0,17.11 0,8.55 0,0 2.66,1.62 5.33,3.25 8,4.88"class="gs-btn-white play-1"style=stroke:#fff;stroke-width:.6px /><path d="M14.485,8.855 C16.64,10.18 18.8,11.5 20.97,12.83 16.64,15.48 12.32,18.13 8,20.79 8,15.48 8,10.18 8,4.88 10.16,6.2 12.32,7.53 14.48,8.85"class="gs-btn-white play-2"style=stroke:#fff;stroke-width:.6px /></g></svg> <svg class=loop viewBox="0 0 29 25.38"xmlns=http://www.w3.org/2000/svg><path d=M27.44,5.44,20.19,0V3.06H9.06A9.31,9.31,0,0,0,0,12.41,9.74,9.74,0,0,0,.69,16l3.06-2.23a6,6,0,0,1-.12-1.22,5.49,5.49,0,0,1,5.43-5.5H20.19v3.81Z class=loop-path /><path d=M25.25,11.54a5.18,5.18,0,0,1,.12,1.12,5.41,5.41,0,0,1-5.43,5.41H9.19V14.5L1.94,19.94l7.25,5.44V22.06H19.94A9.2,9.2,0,0,0,29,12.84a9.42,9.42,0,0,0-.68-3.53Z class=loop-path /></svg> <svg class=ease viewBox="0 0 25.67 25.67"xmlns=http://www.w3.org/2000/svg><path d=M.48,25.12c1.74-3.57,4.28-12.6,8.8-10.7s4.75,1.43,6.5-1.11S19.89,1.19,25.2.55 class=ease-path /><path d=M24.67,1V24.67H1V1H24.67m1-1H0V25.67H25.67V0Z class=ease-border /></svg><label class=time-scale><select><option value=10>10x<option value=5>5x<option value=2>2x<option value=1 selected>1x<option value=0.5>0.5x<option value=0.25>0.25x<option value=0.1>0.1x</select><span class="display time-scale-label">1x</span></label><div class=gs-bottom-right><div class=time-container><span class=time>0.00</span> / <span class=duration>0.00</span></div><a href="https://greensock.com/docs/Utilities/GSDevTools?source=GSDevTools"target=_blank title=Docs><svg class=logo viewBox="0 0 100 100"xmlns=http://www.w3.org/2000/svg><path d="M60 15.4c-.3-.4-.5-.6-.5-.7.1-.6.2-1 .2-1.7v-.4c.6.6 1.3 1.3 1.8 1.7.2.2.5.3.8.3.2 0 .3 0 .5.1h1.6c.8 0 1.6.1 2 0 .1 0 .2 0 .3-.1.6-.3 1.4-1 2.1-1.6 0 .6.1 1.2.1 1.7v1.5c0 .3 0 .5.1.7-.1.1-.2.1-.4.2-.7.4-1.7 1-2.3.9-.5-.1-1.5-.3-2.6-.7-1.2-.3-2.4-.8-3.2-1.2 0 0-.1 0-.1-.1s-.2-.4-.4-.6zm24.6 21.9c-.5-1.7-1.9-2-4.2-.7.9-1.5 2.1-1.5 2.3-2.1.9-2.5-.6-4.6-1.2-5.3.7-1.8 1.4-4.5-1-6.8-1-1-2.4-1.2-3.6-1.1 1.8 1.7 3.4 4.4 2.5 7.2-.1.3-.9.7-1.7 1 0 0 .4 2-.3 3.5-.3.6-.8 1.5-1.3 2.6 1 .9 1.6 1 3 1.3-.9.1-1.2.4-1.2.5-.7 3 1 3.4 1.4 4.8 0 .1 0 .2.1.3v.4c-.3.3-1.4.5-2.5.5s-1.8 1-1.8 1c-.2.1-.3.3-.4.4v1c0 .1 0 .4.1.6.1.5.3 1.3.4 1.8.9.6 1.4.9 2.2 1.1.5.1 1 .2 1.5.1.3-.1.7-.3 1-.7 1.5-1.7 1.9-3.2 2.2-4.1 0-.1 0-.2.1-.2 0 .1.1.1.1.2 0 0 .1-.1.1-.2l.1-.1c1.3-1.6 2.9-4.5 2.1-7zM74.3 49.9c-.1-.3-.1-.7-.2-1.1v-.2c-.1-.2-.1-.4-.2-.6 0-.1-.1-.3-.1-.5s-.1-.5-.1-.7v-.1c0-.2-.1-.5-.1-.7-.1-.3-.1-.7-.2-1.1v-.1c0-.2 0-.3-.1-.5v-.9c0-.1 0-.2.1-.3V43h-.3c-1.1.1-3.8.4-6.7.2-1.2-.1-2.4-.3-3.6-.6-1-.3-1.8-.5-2.3-.7-1.2-.4-1.6-.6-1.8-.7 0 .2-.1.4-.1.7 0 .3-.1.5-.1.8-.1.2-.1.4-.2.6l.1.1c.5.5 1.5 1.3 1.5 2.1v.2c-.1.4-.4.5-.8.9-.1.1-.6.7-1.1 1.1l-.6.6c-.1 0-.1.1-.2.1-.1.1-.3.2-.4.3-.2.1-.7.5-.8.6-.1.1-.2.1-.3.1-2.8 8.8-2.2 13.5-1.5 16.1.1.5.3 1 .4 1.3-.4.5-.8 1-1.2 1.4-1.2 1.5-2 2.6-2.6 4.2 0 .1 0 .1-.1.2 0 .1 0 .2-.1.2-.2.5-.3 1-.4 1.5-.6 2.3-.8 4.5-.9 6.6-.1 2.4-.2 4.6-.5 6.9.7.3 3.1.9 4.7.6.2
156-.1 0-3.9.6-5.7l.6-1.5c.4-.9.9-1.9 1.3-3.1.3-.7.5-1.5.7-2.4.1-.5.2-1 .3-1.6V74v-.1c.1-.6.1-1.3.1-2 0-.2-.7.3-1.1.9.3-1.8 1.3-2.1 2-3.2.3-.5.6-1.1.6-2 2.5-1.7 4-3.7 5-5.7.2-.4.4-.9.6-1.4.3-.8.5-1.6.7-2.4.3-1.4.8-3.2 1.2-4.8v-.1c.4-1.2.8-2.2 1.2-2.6-.2.9-.4 1.7-.6 2.5v.2c-.6 3.5-.7 6.2-2 9.2 1 2.6 1.9 3.9 2 7.6-2 0-3.2 1.6-3.7 3.2 1.2.3 3.9.7 8.3.1h.3c.1-.5.3-1.1.5-1.5.3-.8.5-1.5.6-2.2.2-1.3.1-2.4 0-3.2 3.9-3.7 2.6-11 1.6-16.6zm.3-15.1c.1-.3.2-.6.4-.8.2-.3.3-.7.5-1 .1-.3.3-.6.4-.9.5-1.5.4-2.8.3-3.5-.1 0-.1-.1-.2-.1-.5-.2-.9-.4-1.4-.6-.1 0-.2-.1-.3-.1-3.8-1.2-7.9-.9-11.9.1-1 .2-1.9.5-2.9.1-2.3-.8-3.9-1.9-4.6-2.8l-.2-.2c-.1.2-.2.4-.4.6.2 2.3-.5 3.9-1.4 5.1.9 1.2 2.6 2.8 3.6 3.4 1.1.6 1.7.7 3.4.4-.6.7-1.1 1-1.9 1.4.1.7.2 2 .5 3.4.3.3 1.2.8 2.3 1.3.5.3 1.1.5 1.7.7.8.3 1.7.6 2.4.8.1 0 .2.1.3.1.5.1 1.1.2 1.8.2h.9c2.1 0 4.5-.2 5.4-.3h.1c-.1-2.7.2-4.6.7-6.2.2-.3.4-.7.5-1.1zm-23.2 9.3v.2c-.3 1.7.5 2.4 1.9 3.4.6.5 0 .5.5.8.3.2.7.3 1 .3.3 0 .5 0 .8-.1.2-.1.4-.3.6-.5.1-.1.3-.2.5-.4.3-.2.6-.5.7-.6.1-.1.2-.1.3-.2.2-.2.5-.5.6-.7.2-.2.4-.5.5-.7 0-.1.1-.1.1-.1v-.1c.1-.4-.3-.8-.8-1.3-.2-.2-.4-.3-.5-.5-.3-.3-.6-.5-1-.7-.9-.5-1.9-.7-3-.7l-.3-.3c-2.2-2.5-3.2-4.8-3.9-6.5-.9-2.1-1.9-3.3-3.9-4.9 1 .4 1.8.8 2.3 1.1.5.4 1.3.4 1.9.2.2-.1.5-.2.7-.3.2-.1.4-.2.6-.4 1.6-1.3 2.5-3.8 2.6-5.6v-.1c.2-.3.6-1.1.8-1.4l.1.1c.1.1.3.2.6.5.1 0 .1.1.2.1.1.1.2.1.2.2.8.6 1.9 1.3 2.6 1.7 1.4.7 2.3.7 5.3-.1 2.2-.6 4.8-.8 6.8-.8 1.4 0 2.7.3 4 .7.2.1.4.1.5.2.3.1.6.2.9.4 0 0 .1 0 .1.1.8.4 2.1 1.2 2.5-.3.1-2-.6-3.9-1.6-5.3 0 0-.1 0-.1-.1-.1-.1-.2-.2-.4-.3-.1-.1-.2-.1-.3-.2-.1-.1-.2-.2-.4-.2-.6-.4-1.2-.8-1.6-.9-.1-.1-.3-.1-.4-.2h-.1-.1c-.1 0-.3-.1-.4-.1-.1 0-.1 0-.2-.1h-.1l-.2-.4c-.2-.1-.4-.2-.5-.2h-.6c-.3 0-.5.1-.7.1-.7.1-1.2.3-1.7.4-.2 0-.3.1-.5.1-.5.1-1 .2-1.6.2-.4 0-.9-.1-1.5-.2-.4-.1-.8-.2-1.1-.3-.2-.1-.4-.1-.6-.2-.6-.2-1.1-.3-1.7-.4h-.2-1.8c-.3 0-.6.1-1 .1H57.9c-.8 0-1.5 0-2.3-.1-.2 0-.5-.1-.7-.1-.5-.1-.9-.2-1.3-.4-.2-.1-.3-.1-.4-.2-.1 0-.2 0-.2-.1-.3-.1-.6-.1-.9-.1H51h-.1c-.4 0-.9.1-1.4.2-1.1.2-2.1.6-3 1.3-.3.2-.6.5-.8.8-.1.1-.2.2-.2.3-.4.6-.8 1.2-.9 2 0 .2-.1.4-.1.6 0 .2 1.7.7 2.3 2.8-.8-1.2-2.3-2.5-4.1-1.4-1.5 1-1.1 3.1-2.4 5.4-.3.5-.6.9-1 1.4-.8 1-.7 2.1.2 4.4 1.4 3.4 7.6 5.3 11.5 8.3l.4.4zm8.7-36.3c0 .6.1 1 .2 1.6v.1c0 .3.1.6.1.9.1 1.2.4 2 1 2.9 0 .1.1.1.1.2.3.2.5.3.8.4 1.1.2 3.1.3 4.2 0 .2-.1.5-.3.7-.5.4-.4.7-1.1.9-1.7.1-.7.3-1.3.4-1.8 0-.2.1-.4.1-.5v-.1c0-.2 0-.3.1-.5.2-.7.2-2.4.3-2.8.1-.7 0-1.8-.1-2.5 0-.2-.1-.4-.1-.5v-.1c-.2-.5-1.4-1.4-4.3-1.4-3.1 0-4 1-4.1 1.5v.1c0 .1 0 .3-.1.5-.1.4-.2 1.4-.2 1.9v2.3zm-6 88.6c0-.1-.1-.2-.1-.3-.7-1.5-1.1-3.5-1.3-4.6.4.1.7.6.8.3.2-.5-.4-1.5-.5-2.2v-.1c-.5-.5-4-.5-3.7-.3-.4.8-1 .6-1.3 2.1-.1.7.8.1 1.7.1-1.4.9-3 2.1-3.4 3.2-.1.1-.1.2-.1.3 0 .2-.1.4-.1.5-.1 1.2.5 1.6 2 2.4H48.4c1.4.3 3 .3 4.3.3 1.2-.2 1.6-.7 1.6-1.4-.2-.1-.2-.2-.2-.3z"style=fill:#efefef /><path d="M56.1 36.5c.3 1.4.5 2.4.8 4.2h-.2c-.1.5-.1.9-.1 1.3-1-.4-2.2-.5-2.6-.5-3.7-4.4-2.9-6.1-4.4-8.3.4-.2 1-.4 1.5-.8 1.6 1.9 3.3 3 5 4.1zm-1.7 13.2s-1.4 0-2.3-1c0 0-.1-.5.1-.7 0 0-1.2-1-1.5-1.7-.2-.5-.3-1.1-.2-1.6-4.4-3.7-10.9-4.2-12.9-9.1-.5-1.2-1.3-2.9-.9-3.9-.3.1-.5.2-.8.3-2.9.9-11.7 5.3-17.9 8.8 1.6 1.7 2.6 4.3 3.2 7.2l.3 1.5c.1.5.1 1 .2 1.5.1 1.4.4 2.7.8 3.9.2.8.6 1.5.9 2.2.6 1 1.2 1.9 2.1 2.6.6.5 1.2.9 1.9 1.3 2.1 1.1 5 1.6 8.6 1.5H37.9c.5 0 1 .1 1.5.1h.1c.4.1.9.1 1.3.2h.2c.4.1.9.2 1.3.4h.1c.4.1.8.3 1.1.5h.1c.4.2.7.4 1.1.6h.1c.7.4 1.3.9 1.9 1.5l.1.1c.6.5 1.1 1.1 1.5 1.8 0 .1.1.1.1.2s.1.1.1.2c.4.6 1.2 1.1 1.9 1.3.7-.9 1.5-1.8 2.2-2.8-1.6-6 0-11.7 1.8-16.9zm-26-15.9c5-2.4 9-4.1 9.9-4.5.3-.6.6-1.4.9-2.6.1-.3.2-.5.3-.8 1-2.7 2.7-2.8 3.5-3v-.2c.1-1.1.5-2 1-2.8-8.8 2.5-18 5.5-28 11.7-.1.1-.2.2-.4.2C11.3 34.5 3 40.3 1.3 51c2.4-2.7 6-5.6 10.5-8.5.1-.1.3-.2.5-.3.2-.1.5-.3.7-.4 1.2-.7 2.4-1.4 3.6-2.2 2.2-1.2 4.5-2.4 6.7-3.5 1.8-.8 3.5-1.6 5.1-2.3zm54.9 61.3l-.3-.3c-.8-.6-4.1-1.2-5.5-2.3-.4-.3-1.1-.7-1.7-1.1-1.6-.9-3.5-1.8-3.5-2.1v-.1c-.2-1.7-.2-7 .1-8.8.3-1.8.7-4.4.8-5.1.1-.6.5-1.2.1-1.2h-.4c-.2 0-.4.1-.8.1-1.5.3-4.3.6-6.6.4-.9-.1-1.6-.2-2-.3-.5-.1-.7-.2-.9-.3H62.3c-.4.5 0 2.7.6 4.8.3 1.1.8 2 1.2 3 .3.8.6 1.8.8 3.1 0 .2.1.4.1.7.2 2.8.3 3.6-.2 4.9-.1.3-.3.6-.4 1-.4.9-.7 1.7-.6 2.3 0 .2.1.4.1.5.2.4.6.7 1.2.8.2 0 .3.1.5.
1561.3 0 .6.1.9.1 3.4 0 5.2 0 8.6.4 2.5.4 3.9.6 5.1.5.4 0 .9-.1 1.4-.1 1.2-.2 1.8-.5 1.9-.9-.1.2-.1.1-.2-.1zM60.2 16.4zm-.5 1.7zm3.8.5c.1 0 .3.1.5.1.4.1.7.2 1.2.3.3.1.6.1.9.1h1.3c.3-.1.7-.1 1-.2.7-.2 1.5-.4 2.7-.6h.3c.3 0 .6.1.9.3.1.1.2.1.4.2.3.2.8.2 1.2.4h.1c.1 0 .1.1.2.1.6.3 1.3.7 1.9 1.1l.3.3c.9-.1 1.6-.2 2.1-.2h.1c-.2-.4-.3-1.3-1.8-.6-.6-.7-.8-1.3-2.1-.9-.1-.2-.2-.3-.3-.4l-.1-.1c-.1-.1-.2-.3-.3-.4 0-.1-.1-.1-.1-.2-.2-.3-.5-.5-.9-.7-.7-.4-1.5-.6-2.3-.5-.2 0-.4.1-.6.2-.1 0-.2.1-.2.1-.1 0-.2.1-.3.2-.5.3-1.3.8-2.1 1-.1 0-.1 0-.2.1-.2 0-.4.1-.5.1H66.5h-.1c-.4-.1-1.1-.2-2-.5-.1 0-.2-.1-.3-.1-.9-.2-1.8-.5-2.7-.8-.3-.1-.7-.2-1-.3-.1 0-.1 0-.2-.1h-.1s-.1 0-.1-.1c-.3-.3-.7-.6-1.3-.8-.5-.2-1.2-.4-2.1-.5-.2 0-.5 0-.7.1-.4.2-.8.6-1.2.9.1.1.3.3.4.5.1.2.2.4.3.7l-.6-.6c-.5-.4-1.1-.8-1.7-.9-.8-.2-1.4.4-2.3.9 1 0 1.8.1 2.5.4.1 0 .1 0 .2.1h.1c.1 0 .2.1.3.1.9.4 1.8.6 2.7.6h1.3c.5 0 .8-.1 1.1-.1.1 0 .4 0 .7-.1h2.2c.4.4.9.6 1.6.8z"style=fill:#88ce02 /><path d="M100 51.8c0-19.5-12.5-36.1-30-42.1.1-1.2.2-2.4.3-3.1.1-1.5.2-3.9-.5-4.9-1.6-2.3-9.1-2.1-10.5-.1-.4.6-.7 3.6-.6 5.9-1.1-.1-2.2-.1-3.3-.1-16.5 0-30.9 9-38.6 22.3-2.4 1.4-4.7 2.8-6.1 4C5.4 38 2.2 43.2 1 47c-1.6 4.7-1.1 7.6.4 5.8 1.2-1.5 6.6-5.9 10.1-8.2-.4 2.3-.6 4.8-.6 7.2 0 21 14.5 38.5 34 43.3-.1 1.1.1 2 .7 2.6.9.8 3.2 2 6.4 1.6 2.9-.3 3.5-.5 3.2-2.9h.2c2.7 0 5.3-.2 7.8-.7.1.1.2.2.4.3 1.5 1 7.1.8 9.6.7s6.2.9 8.6.5c2.9-.5 3.4-2.3 1.6-3.2-1.5-.8-3.8-1.3-6.7-3.1C90.6 83.4 100 68.7 100 51.8zM60.1 5.5c0-.5.1-1.5.2-2.1 0-.2 0-.4.1-.5v-.1c.1-.5 1-1.5 4.1-1.5 2.9 0 4.2.9 4.3 1.4v.1c0 .1 0 .3.1.5.1.8.2 1.9.1 2.7 0 .5-.1 2.1-.2 2.9 0 .1 0 .3-.1.5v.1c0 .2-.1.3-.1.5-.1.5-.2 1.1-.4 1.8-.1.6-.5 1.2-.9 1.7-.2.3-.5.5-.7.5-1.1.3-3.1.3-4.2 0-.3-.1-.5-.2-.8-.4 0-.1-.1-.1-.1-.2-.6-.9-.9-1.7-1-2.9 0-.4-.1-.6-.1-.9v-.1c-.1-.6-.2-1-.2-1.6v-.3c-.1-1.3-.1-2.1-.1-2.1zm-.4 7.5v-.4c.6.6 1.3 1.3 1.8 1.7.2.2.5.3.8.3.2 0 .3 0 .5.1h1.6c.8 0 1.6.1 2 0 .1 0 .2 0 .3-.1.6-.3 1.4-1 2.1-1.6 0 .6.1 1.2.1 1.7v1.5c0 .3 0 .5.1.7-.1.1-.2.1-.4.2-.7.4-1.7 1-2.3.9-.5-.1-1.5-.3-2.6-.7-1.2-.3-2.4-.8-3.2-1.2 0 0-.1 0-.1-.1-.2-.3-.4-.5-.6-.7-.3-.4-.5-.6-.5-.7.3-.4.4-.9.4-1.6zm.5 3.4zm-7.3-.3c.6.1 1.2.5 1.7.9.2.2.5.4.6.6-.1-.2-.2-.5-.3-.7-.1-.2-.3-.4-.4-.5.4-.3.8-.7 1.2-.9.2-.1.4-.1.7-.1.9.1 1.6.2 2.1.5.6.2 1 .5 1.3.8 0 0 .1 0 .1.1h.1c.1 0 .1 0 .2.1.3.1.6.2 1 .3.9.3 1.9.6 2.7.8.1 0 .2.1.3.1.9.2 1.6.4 2 .5h.4c.2 0 .4 0 .5-.1.1 0 .1 0 .2-.1.7-.2 1.5-.7 2.1-1 .1-.1.2-.1.3-.2.1 0 .2-.1.2-.1.2-.1.4-.2.6-.2.8-.2 1.7.1 2.3.5.3.2.6.4.9.7 0 .1.1.1.1.2.1.2.2.3.3.4l.1.1c.1.1.2.2.3.4 1.3-.4 1.5.2 2.1.9 1.6-.7 1.7.2 1.8.6h-.1c-.5 0-1.2 0-2.1.2l-.3-.3c-.5-.4-1.2-.8-1.9-1.1-.1 0-.1-.1-.2-.1h-.1c-.4-.2-.8-.2-1.2-.4-.1-.1-.2-.1-.4-.2-.3-.1-.6-.3-.9-.3h-.3c-1.2.1-2 .4-2.7.6-.3.1-.7.2-1 .2-.4.1-.8.1-1.3 0-.3 0-.6-.1-.9-.1-.5-.1-.8-.2-1.2-.3-.2 0-.3-.1-.5-.1h-.1c-.6-.2-1.2-.3-1.8-.4h-.1-2.1c-.4.1-.6.1-.7.1-.3 0-.7.1-1.1.1h-1.3c-.9 0-1.9-.2-2.7-.6-.1 0-.2-.1-.3-.1H53c-.1 0-.1 0-.2-.1-.7-.3-1.6-.4-2.5-.4 1.2-.8 1.8-1.4 2.6-1.3zm6.8 2zm-15.2 4.1c.1-.7.4-1.4.9-2 .1-.1.2-.2.2-.3l.8-.8c.9-.6 1.9-1.1 3-1.3.5-.1 1-.2 1.4-.2H52c.3 0 .6.1.9.1.1 0 .2 0 .2.1.1.1.2.1.4.2.4.2.8.3 1.3.4.2 0 .5.1.7.1.7.1 1.5.1 2.3.1H58.7c.4 0 .7-.1 1-.1H61.7c.6.1 1.1.2 1.7.4.2 0 .4.1.6.2.3.1.7.2 1.1.3.6.1 1.1.2 1.5.2.6 0 1.1-.1 1.6-.2.2 0 .3-.1.5-.1.5-.1 1-.3 1.7-.4.2 0 .5-.1.7-.1h.6c.2 0 .4.1.5.2l.1.1h.1c.1 0 .1 0 .2.1.2.1.3.1.4.1h.2c.1.1.3.1.4.2.4.2 1 .6 1.6.9.1.1.2.2.4.2.1.1.2.1.3.2.2.1.3.3.4.3l.1.1c1.1 1.4 1.8 3.3 1.6 5.3-.3 1.5-1.6.7-2.5.3 0 0-.1 0-.1-.1-.3-.1-.6-.2-.9-.4-.2-.1-.4-.1-.5-.2-1.2-.4-2.5-.7-4-.7-2 0-4.6.1-6.8.8-3 .8-4 .8-5.3.1-.8-.4-1.8-1.1-2.6-1.7-.1-.1-.2-.1-.2-.2-.1-.1-.1-.1-.2-.1-.3-.2-.6-.4-.6-.5l-.1-.1c-.2.3-.6 1-.8 1.4v.1c-.1 1.7-1 4.2-2.6 5.6-.2.1-.4.3-.6.4-.2.1-.5.2-.7.3-.7.2-1.4.2-1.9-.2-.5-.3-1.3-.7-2.3-1.1 2 1.6 3 2.8 3.9 4.9.7 1.7 1.7 4 3.9 6.5l.3.3c1.1 0 2.1.2 3 .7.4.2.7.4 1 .7.2.2.4.3.5.5.5.4.9.8.8 1.3v.1s0 .1-.1.1c-.1.2-.3.5-.5.7-.1.1-.4.4-.6.7-.1.1-.2.2-.3.2-.1.1-.4.3-.7.6-.2.2-.4.3-.5.4-.2.1-.4.4-.6.5-.3.1-.5.2-.8.1-.3 0-.7-.2-1-.3-.5-.3.1-.3-.5-.8-1.4-1-2.2-1.7-1.9-3.4v-.2c-.2-.1-.3-.3-.5-.4-3.9-3-10.1-4.9-11.5-8.3-.9-2.3-1-3.4-.2-4.4.4-.5.8-1 1-1.4 1.3-2.3.9-4.4 2.4
156-5.4 1.8-1.2 3.3.2 4.1 1.4-.5-2.1-2.3-2.6-2.3-2.8.3.1.3-.1.3-.3zm29 20s-.1 0 0 0c-.1 0-.1 0 0 0-.9.1-3.3.3-5.4.3h-.9c-.7 0-1.3-.1-1.8-.2-.1 0-.2 0-.3-.1-.7-.2-1.6-.5-2.4-.8-.6-.2-1.2-.5-1.7-.7-1.1-.5-2.1-1.1-2.3-1.3-.5-1.4-.7-2.7-.7-3.4.8-.4 1.3-.7 1.9-1.4-1.7.3-2.4.2-3.4-.4-1-.5-2.6-2.2-3.6-3.4 1-1.2 1.7-2.9 1.4-5.1.1-.2.3-.4.4-.6 0 .1.1.1.2.2.7.9 2.4 2 4.6 2.8 1.1.4 2 .1 2.9-.1 4-1 8.1-1.3 11.9-.1.1 0 .2.1.3.1.5.2.9.4 1.4.6.1 0 .1.1.2.1.1.7.2 2-.3 3.5-.1.3-.2.6-.4.9-.2.3-.3.6-.5 1-.1.3-.2.5-.4.8-.2.4-.3.8-.5 1.3-.4 1.4-.7 3.4-.6 6zm-23.9-9c.4-.2 1-.4 1.5-.8 1.6 1.8 3.3 3 5 4.1.3 1.4.5 2.4.8 4.2h-.2c-.1.5-.1.9-.1 1.3-1-.4-2.2-.5-2.6-.5-3.7-4.3-3-6-4.4-8.3zm-32.9 6.5c-1.3.7-2.5 1.4-3.6 2.2-.2.1-.5.3-.7.4-.1.1-.3.2-.5.3-4.5 2.9-8.1 5.8-10.5 8.5 1.7-10.8 10-16.5 14.3-19.2.1-.1.2-.2.4-.2 10-6.2 19.2-9.2 28-11.7-.5.8-.9 1.7-1 2.8v.2c-.8.1-2.5.2-3.5 3-.1.2-.2.5-.3.8-.3 1.2-.6 2-.9 2.6-.9.4-5 2.2-9.9 4.5-1.6.8-3.3 1.6-5 2.4-2.3 1-4.6 2.2-6.8 3.4zm28 24.8s0-.1 0 0c-.4-.3-.8-.5-1.2-.7h-.1c-.4-.2-.7-.3-1.1-.5h-.1c-.4-.1-.8-.3-1.3-.4h-.2c-.4-.1-.8-.2-1.3-.2h-.1c-.5-.1-1-.1-1.5-.1H35.9c-3.7.1-6.5-.4-8.6-1.5-.7-.4-1.4-.8-1.9-1.3-.9-.7-1.5-1.6-2.1-2.6-.4-.7-.7-1.4-.9-2.2-.4-1.2-.6-2.5-.8-3.9 0-.5-.1-1-.2-1.5l-.3-1.5c-.6-2.9-1.6-5.5-3.2-7.2 6.3-3.5 15-7.9 17.8-8.8.3-.1.6-.2.8-.3-.3 1.1.4 2.7.9 3.9 2.1 4.9 8.6 5.4 12.9 9.1 0 .5 0 1.1.2 1.6.5.6 1.7 1.6 1.7 1.6-.2.2-.1.7-.1.7.9 1 2.3 1 2.3 1-1.8 5.2-3.4 10.9-1.9 16.9-.7 1-1.5 1.8-2.2 2.8-.7-.2-1.4-.6-1.9-1.3 0-.1-.1-.1-.1-.2s-.1-.1-.1-.2l-1.5-1.8-.1-.1c-.5-.4-1.2-.9-1.9-1.3zm7.9 33.6c-1.3.1-2.9 0-4.3-.3h-.2-.1c-1.5-.8-2.1-1.2-2-2.4 0-.2 0-.3.1-.5 0-.1.1-.2.1-.3.5-1.1 2.1-2.2 3.4-3.2-.8 0-1.8.7-1.7-.1.2-1.5.9-1.3 1.3-2.1-.2-.3 3.3-.2 3.8.3v.1c0 .7.7 1.7.5 2.2-.1.3-.4-.2-.8-.3.2 1.1.6 3.1 1.3 4.6.1.1.1.2.1.3 0 .1.1.2.1.3 0 .7-.4 1.2-1.6 1.4zM59 67.7c0 .9-.3 1.6-.6 2-.7 1.1-1.7 1.4-2 3.2.4-.6 1.1-1.1 1.1-.9 0 .8-.1 1.4-.1 2v.2c-.1.6-.2 1.1-.3 1.6-.2.9-.5 1.7-.7 2.4-.4 1.2-.9 2.1-1.3 3.1l-.6 1.5c-.6 1.7-.4 5.6-.6 5.7-1.6.3-4.1-.3-4.7-.6.3-2.2.4-4.5.5-6.9.1-2.1.3-4.3.9-6.6.1-.5.3-1 .4-1.5 0-.1 0-.2.1-.2 0-.1 0-.1.1-.2.5-1.6 1.4-2.7 2.6-4.2.4-.4.7-.9 1.2-1.4-.1-.4-.2-.8-.4-1.3-.7-2.6-1.3-7.3 1.5-16.1.1 0 .2-.1.3-.1.2-.1.7-.5.8-.6.1-.1.3-.2.4-.3.1 0 .1-.1.2-.1l.6-.6 1.1-1.1c.4-.4.7-.5.8-.9v-.2c0-.8-1.1-1.5-1.5-2.1l-.1-.1c.1-.2.1-.4.2-.6 0-.2.1-.5.1-.8 0-.2.1-.5.1-.7.1.1.6.4 1.8.7.6.2 1.3.4 2.3.7 1.1.3 2.4.5 3.6.6 2.9.2 5.6 0 6.7-.2h.3v.1c0 .1 0 .2-.1.3v.9c0 .2 0 .3.1.5v.1c0 .4.1.7.2 1.1 0 .3.1.5.1.7v.1c0 .3.1.5.1.7 0 .2.1.3.1.5.1.2.1.4.2.6v.2c.1.4.2.8.2 1.1 1 5.7 2.3 12.9-1.1 16.7.2.8.3 1.9 0 3.2-.1.7-.3 1.4-.6 2.2-.2.5-.3 1-.5 1.5h-.3c-4.5.6-7.1.2-8.3-.1.5-1.6 1.7-3.3 3.7-3.2-.1-3.7-1.1-5-2-7.6 1.3-3 1.3-5.7 2-9.2v-.2c.2-.8.3-1.6.6-2.5-.4.5-.8 1.5-1.2 2.6v.1c-.5 1.5-.9 3.4-1.2 4.8-.2.8-.4 1.6-.7 2.4-.2.5-.4.9-.6 1.4-1.5 1.9-3 3.9-5.5 5.6zm18.5 24.9c1.5 1.1 4.7 1.8 5.5 2.3l.3.3c.1.1.1.2.1.3-.1.4-.7.7-1.9.9-.5.1-.9.1-1.4.1-1.3 0-2.6-.2-5.1-.5-3.4-.5-5.2-.4-8.6-.4-.3 0-.6 0-.9-.1-.2 0-.4-.1-.5-.1-.6-.2-1-.5-1.2-.8-.1-.2-.1-.3-.1-.5-.1-.7.2-1.5.6-2.3.2-.4.3-.7.4-1 .5-1.3.4-2.1.2-4.9 0-.2-.1-.4-.1-.7-.2-1.3-.5-2.3-.8-3.1-.4-1.1-.9-1.9-1.2-3-.6-2.1-1-4.3-.6-4.8H62.5c.2.1.5.2.9.3.5.1 1.1.2 2 .3 2.2.2 5.1-.2 6.6-.4.3-.1.6-.1.8-.1h.4c.4 0 .1.6-.1 1.2-.1.7-.5 3.3-.8 5.1-.3 1.8-.2 7.1-.1 8.8v.1c0 .3 1.9 1.2 3.5 2.1.7.2 1.4.5 1.8.9zm4.8-48.2c0 .1 0 .1 0 0-.1.1-.2.2-.2.3 0-.1-.1-.1-.1-.2 0 .1 0 .2-.1.2-.2.9-.6 2.4-2.2 4.1-.4.4-.7.6-1 .7-.5.1-.9 0-1.5-.1-.9-.2-1.3-.6-2.2-1.1-.1-.6-.3-1.3-.4-1.8 0-.3-.1-.5-.1-.6v-1l.4-.4s.7-1 1.8-1 2.2-.2 2.5-.5v-.1-.3c0-.1 0-.2-.1-.3-.4-1.4-2.1-1.8-1.4-4.8 0-.2.3-.5 1.2-.5-1.4-.3-2-.4-3-1.3.5-1.1 1-1.9 1.3-2.6.8-1.5.3-3.5.3-3.5.8-.3 1.6-.7 1.7-1 .9-2.8-.7-5.5-2.5-7.2 1.2-.1 2.6.1 3.6 1.1 2.4 2.4 1.8 5 1 6.8.6.7 2.1 2.9 1.2 5.3-.2.6-1.4.6-2.3 2.1 2.3-1.3 3.7-1 4.2.7 1 2.4-.6 5.3-2.1 7z"/><path d="M22 53.4v-.2c0-.2-.1-.5-.2-.9s-.1-.8-.2-1.3c-.5-4.7-1.9-9.4-4.9-11.3 3.7-2 16.8-8.5 21.9-10.5 2.9-1.2.8-.4-.2 1.4-.8 1.4-.3 2.9-.5 3.2-.6.8-12.6 10.5-15.9 19.6zm32.2-2.3c-3.4 3.8-12 11-18.2 11.4 8.7-.2 12.2 4.1 14.7 9.7 2.6-5.2 2.7-10.3 2.6-16.1 0-2.6 1.8-6 .9-5zm5.3-23L54.3 24s-1.1 3.1-1 4.6c.1 1.6-1.8 2.7-.9 3.6.9.9 3.2 2.5 4 3.4.7.9 1.1 7.1 1.1 7.1l2.2 2.7s1-1.8 1.1-6.3c.2-5.4-2.9-7.1-3.3-8.6-.4-1.4.6-2.9 2-2.4zm3.1 45.6l3.9.3s1.2-2.2 2.1-3.5c.9-1.4.4-1.6 0-4.6-.4-3-1.4-9.3-1.2-13.6l-3.1 10.2s1.8 5.6 1.6 6.4c-.1.8-3.3 4.8-3.3 4.8zm5 18.8c-1.1 0-2.5-.4-3.5-.8l-1 .3.2 4s5.2.7 4.6-.4c-.6-1.2-.3-3.1-.3-3.1zm12 .6c-1 0-.3.2.4 1.2.8 1 .1 2-.8 2.3l3.2.5 1.9-1.7c.1 0-3.7-2.3-4.7-2.3zM73 76c-1.6.5-4.2.8-5.9.8-1.7.1-3.7-.1-5-.5v1.4s1.2.5 5.4.5c3.5.1 5.7-.8 5.7-.8l.9-.8c-.1.1.5-1.1-1.1-.6zm-.2 3.1c-1.6.6-3.9.6-5.6.7-1.7.1-3.7-.1-5-.5l.1 1.4s.7.3 4.9.4c3.5.1 5.7-.7 5.7-.7l.3-.5c-.1-.1.3-1-.4-.8zm5.9-42.7c-.9-.8-1.4-2.4-1.5-3.3l-1.9 2.5.7 1.2s2.5.1 2.8.1c.4 0 .3-.1-.1-.5zM69 14.7c.6-.7.2-2.7.2-2.7L66 14.6l-4.4-.8-.5-1.3-1.3-.1c.8 1.8 1.8 2.5 3.3 3.1.9.4 4.5.9 5.9-.8z"style=opacity:.4;fill-rule:evenodd;clip-rule:evenodd /></svg></a></div></div>'; 157 if (element) { 158 root.style.position = "absolute"; 159 root.style.top = minimal ? "calc(100% - 42px)" : "calc(100% - 51px)"; 160 } 161 if (css) { 162 if (typeof(css) === "string") { 163 root.style.cssText = css; 164 } else if (typeof(css) === "object") { 165 css.data = "root"; 166 TweenLite.set(root, css).kill(); 167 } 168 if (root.style.top) { 169 root.style.bottom = "auto"; 170 } 171 if (root.style.width) { 172 TweenLite.set(root, {xPercent: -50, left: "50%", right: "auto", data:"root"}).kill(); 173 } 174 } 175 if (!minimal && root.offsetWidth < 600) { 176 root.setAttribute("class", "gs-dev-tools minimal"); 177 if (element) { 178 root.style.top = "calc(100% - 42px)"; 179 } 180 } 181 return root; 182 }, 183 _clickedOnce, //we don't preventDefault() on the first mousedown/touchstart/pointerdown so that iframes get focus properly. 184 _addListener = function(e, type, callback, capture) { 185 var handler, altType; 186 if (type === "mousedown" || type === "mouseup") { 187 e.style.cursor = "pointer"; 188 } 189 if (type === "mousedown") { 190 //some browsers call BOTH mousedown AND touchstart, for example, on a single interaction so we need to skip one of them if both are called within 100ms. 191 altType = e.onpointerdown !== undefined ? "pointerdown" : e.ontouchstart !== undefined ? "touchstart" : null; 192 if (altType) { 193 handler = function(event) { 194 if (event.target.nodeName.toLowerCase() !== "select" && event.type === altType) { //don't preventDefault() on a <select> or else it won't open! 195 event.stopPropagation(); 196 if (_clickedOnce) { //otherwise, both touchstart and mousedown will get called. 197 event.preventDefault(); 198 callback.call(e, event); 199 } 200 } else if (event.type !== altType) { 201 callback.call(e, event); 202 } 203 _clickedOnce = true; 204 }; 205 e.addEventListener(altType, handler, capture); 206 e.addEventListener(type, handler, capture); 207 return; 208 } 209 } 210 e.addEventListener(type, callback, capture); 211 }, 212 _removeListener = function(e, type, callback) { 213 e.removeEventListener(type, callback); 214 type = type !== "mousedown" ? null : e.onpointerdown !== undefined ? "pointerdown" : e.ontouchstart !== undefined ? "touchstart" : null; 215 if (type) { 216 e.removeEventListener(type, callback); 217 } 218 }, 219 _selectValue = function(element, value, label, insertIfAbsent) { 220 var options = element.options, 221 i = options.length, 222 option; 223 value += ""; 224 while (--i > -1) { 225 if (options[i].innerHTML === value || options[i].value === value) { 226 element.selectedIndex = i; 227 label.innerHTML = options[i].innerHTML; 228 return options[i]; 229 } 230 } 231 if (insertIfAbsent) { 232 option = _createElement("option", element); 233 option.setAttribute("value", value); 234 option.innerHTML = label.innerHTML = typeof(insertIfAbsent) === "string" ? insertIfAbsent : value; 235 element.selectedIndex = options.length - 1; 236 } 237 }, 238 //increments the selected value of a <select> up or down by a certain amount. 239 _shiftSelectedValue = function(element, amount, label) { 240 var options = element.options, 241 i = Math.min(options.length - 1, Math.max(0, element.selectedIndex + amount)); 242 element.selectedIndex = i; 243 if (label) { 244 label.innerHTML = options[i].innerHTML; 245 } 246 return options[i].value; 247 }, 248 _recordedRoot = new TimelineLite({data:"root", id:"Global Timeline", autoRemoveChildren:false, smoothChildTiming:true}), 249 _recordedTemp = new TimelineLite({data:"root", id:"Global Temp", autoRemoveChildren:false, smoothChildTiming:true}), 250 _rootTween = TweenLite.to(_recordedRoot, 1, {time:1, ease:Linear.easeNone, data:"root", id:"_rootTween", paused:true, immediateRender:false}), 251 _rootInstance, _rootIsDirty, _keyboardInstance, 252 //moves everything from _recordedTemp into _recordedRoot and updates the _rootTween if it is currently controlling the Global timeline (_recordedRoot). _recordedTemp is just a temporary recording area for anything that happens while _recordedRoot is paused. Returns true if the _recordedRoot's duration changed due to the merge. 253 _merge = function() { 254 var t = _recordedTemp._first, 255 duration, next; 256 if (t) { 257 if (_rootInstance && _rootInstance.animation() === _recordedRoot) { 258 duration = _recordedRoot._duration; 259 while (t) { 260 next = t._next; 261 if (!(typeof(t.target) === "function" && t.target === t.vars.onComplete && !t._duration) && !(t.target && t.target._gsIgnore)) { //typically, delayedCalls aren't included in the _recordedTemp, but since the hijacked add() below fires BEFORE TweenLite's constructor sets the target, we couldn't check that target === vars.onComplete there. And Draggable creates a tween with just an onComplete (no onReverseComplete), thus it fails that test. Therefore, we test again here to avoid merging that in. 262 _recordedRoot.add(t, t._startTime - t._delay); 263 } else { 264 SimpleTimeline.prototype.add.call(_globalTimeline, t, t._startTime - t._delay); 265 } 266 t = next; 267 } 268 return (duration !== _recordedRoot.duration()); 269 } else { 270 //if it's not the _recordedRoot that's currently playing ("global timeline"), merge the temp tweens back into the real global timeline instead and kill any that are already completed. No need to keep them recorded. 271 while (t) { 272 next = t._next; 273 if (t._gc || t._totalTime === t._totalDuration) { 274 t.kill(); 275 } else { 276 //SimpleTimeline.prototype.add.call(_globalTimeline, t, t._recordedTime); 277 //_globalTimeline.add(t, t._startTime - t._delay - _recordedTemp._startTime); 278 SimpleTimeline.prototype.add.call(_globalTimeline, t, t._startTime - t._delay); 279 } 280 t = next; 281 } 282 } 283 } 284 }, 285 _updateRootDuration = function() { 286 if (_rootInstance) {
287 _rootInstance.update(); 288 _rootIsDirty = false; 289 } 290 TweenLite.ticker.removeEventListener("tick", _updateRootDuration); 291 }, 292 293 _buildPlayPauseMorph = function(svg) { 294 var tl = new TimelineLite({data:"root", onComplete:function() { tl.kill(); }}); 295 tl.to(svg.querySelector(".play-1"), 0.5, {attr:{d:"M5.75,3.13 C5.75,9.79 5.75,16.46 5.75,23.13 4.08,23.13 2.41,23.13 0.75,23.13 0.75,16.46 0.75,9.79 0.75,3.12 2.41,3.12 4.08,3.12 5.75,3.12"}, ease:Power3.easeInOut, rotation:360, transformOrigin:"50% 50%"}) 296 .to(svg.querySelector(".play-2"), 0.5, {attr:{d:"M16.38,3.13 C16.38,9.79 16.38,16.46 16.38,23.13 14.71,23.13 13.04,23.13 11.38,23.13 11.38,16.46 11.38,9.79 11.38,3.12 13.04,3.12 14.71,3.12 16.38,3.12"}, ease:Power3.easeInOut, rotation:360, transformOrigin:"50% 50%"}, 0.05); 297 return tl; 298 }, 299 300 _buildLoopAnimation = function(svg) { 301 var tl = new TimelineLite({data:"root", paused:true, onComplete:function() { tl.kill(); }}); 302 tl.to(svg, 0.5, {rotation:360, ease:Power3.easeInOut, transformOrigin:"50% 50%"}) 303 .to(svg.querySelectorAll(".loop-path"), 0.5, {fill:"#91e600", ease:Linear.easeNone}, 0); 304 return tl; 305 }, 306 307 308 309 310 311 GSDevTools = function(vars) { 312 this.vars = vars = vars || {}; 313 vars.id = vars.id || (typeof(vars.animation) === "string" ? vars.animation : _idSeed++); //try to find a unique ID so that sessionStorage can be mapped to it (otherwise, for example, all the embedded codepens on a page would share the same settings). So if no id is defined, see if there's a string-based "animation" defined. Last of all, we default to a numeric counter that we increment. 314 _lookup[vars.id + ""] = this; 315 316 //GENERAL/UTILITY 317 var _self = this, 318 root = _createRootElement(vars.container, vars.minimal, vars.css), 319 find = function(s) { 320 return root.querySelector(s); 321 }, 322 record = function(key, value) { 323 if (vars.persist !== false && typeof(sessionStorage) !== "undefined") { 324 sessionStorage.setItem("gs-dev-" + key + vars.id, value); 325 } 326 return value; 327 }, 328 recall = function(key) { 329 var value; 330 if (vars.persist !== false && typeof(sessionStorage) !== "undefined") { 331 value = sessionStorage.getItem("gs-dev-" + key + vars.id); 332 return (key === "animation") ? value : (key === "loop") ? (value === "true") : parseFloat(value); // handle data typing too. 333 } 334 }, 335 336 337 //SCRUBBER/PROGRESS 338 playhead = find(".playhead"), 339 timelineTrack = find(".timeline-track"), 340 progressBar = find(".progress-bar"), 341 timeLabel = find(".time"), 342 durationLabel = find(".duration"), 343 pixelToTimeRatio, timeAtDragStart, dragged, skipDragUpdates, 344 progress = 0, 345 //spits back a common onPress function for anything that's dragged along the timeline (playhead, inPoint, outPoint). The originRatio is a value from 0-1 indicating how far along the x-axis the origin is located (0.5 is in the center, 0 is left, 1 is on right side). limitElement is optional, and sets the bounds such that the element can't be dragged past the limitElement. 346 onPressTimeline = function(element, originRatio, limitToInOut) { 347 return function(e) { 348 var trackBounds = timelineTrack.getBoundingClientRect(), 349 elementBounds = element.getBoundingClientRect(), 350 left = elementBounds.width * originRatio, 351 x = element._gsTransform.x, 352 minX = trackBounds.left - elementBounds.left - left + x, 353 maxX = trackBounds.right - elementBounds.right + (elementBounds.width - left) + x, 354 unlimitedMinX = minX, 355 limitBounds; 356 if (limitToInOut) { 357 if (element !== inPoint) { 358 limitBounds = inPoint.getBoundingClientRect(); 359 if (limitBounds.left) { //if inPoint is hidden (like display:none), ignore. 360 minX += (limitBounds.left + limitBounds.width) - trackBounds.left; 361 } 362 } 363 if (element !== outPoint) { 364 limitBounds = outPoint.getBoundingClientRect(); 365 if (limitBounds.left) { //if outPoint is hidden (like display:none), ignore. 366 maxX -= (trackBounds.left + trackBounds.width) - limitBounds.left; 367 } 368 } 369 } 370 pausedWhenDragStarted = paused; 371 this.applyBounds({minX:minX, maxX:maxX}); 372 //_merge(); 373 pixelToTimeRatio = linkedAnimation.duration() / trackBounds.width; 374 timeAtDragStart = -unlimitedMinX * pixelToTimeRatio; 375 376 if (!skipDragUpdates) { 377 linkedAnimation.pause(timeAtDragStart + pixelToTimeRatio * this.x); 378 } else { 379 linkedAnimation.pause(); 380 } 381 if (this.target === playhead) { 382 if (this.activated) { 383 this.allowEventDefault = false;
384 } 385 this.activated = true; 386 } 387 dragged = true; 388 }; 389 }, 390 progressDrag = Draggable.create(playhead, { 391 type:"x", 392 cursor: "ew-resize", 393 allowNativeTouchScrolling: false, 394 allowEventDefault:true, //otherwise, when dragged outside an iframe, the mouseup doesn't bubble up so it could seem "stuck" to the mouse. 395 onPress: onPressTimeline(playhead, 0.5, true), 396 onDrag: function() { 397 var time = timeAtDragStart + pixelToTimeRatio * this.x; 398 if (time < 0) { 399 time = 0; 400 } else if (time > linkedAnimation._duration) { 401 time = linkedAnimation._duration; 402 } 403 if (!skipDragUpdates) { 404 linkedAnimation.time(time); 405 } 406 progressBar.style.width = Math.min(outProgress - inProgress, Math.max(0, (time / linkedAnimation._duration) * 100 - inProgress)) + "%"; 407 timeLabel.innerHTML = time.toFixed(2); 408 }, 409 onRelease: function(e) { 410 if (!paused) { 411 linkedAnimation.resume(); 412 } 413 } 414 })[0], 415 inPoint = find(".in-point"), 416 outPoint = find(".out-point"), 417 resetInOut = function() { 418 inProgress = 0; 419 outProgress = 100; 420 inPoint.style.left = "0%"; 421 outPoint.style.left = "100%"; 422 record("in", inProgress); 423 record("out", outProgress); 424 updateProgress(true); 425 }, 426 inProgress = 0, 427 outProgress = 100, 428 pausedWhenDragStarted, 429 inDrag = Draggable.create(inPoint, { 430 type:"x", 431 cursor:"ew-resize", 432 zIndexBoost:false, 433 allowNativeTouchScrolling: false, 434 allowEventDefault:true, //otherwise, when dragged outside an iframe, the mouseup doesn't bubble up so it could seem "stuck" to the mouse. 435 onPress:onPressTimeline(inPoint, 1, true), 436 onDoubleClick: resetInOut, 437 onDrag: function() { 438 inProgress = (timeAtDragStart + pixelToTimeRatio * this.x) / linkedAnimation.duration() * 100; 439 linkedAnimation.progress(inProgress / 100); 440 updateProgress(true); 441 }, 442 onRelease: function() { 443 if (inProgress < 0) { 444 inProgress = 0; 445 } 446 _clearSelection(); 447 //for responsiveness, convert the px-based transform into %-based left position. 448 inPoint.style.left = inProgress + "%"; 449 record("in", inProgress); 450 TweenLite.set(inPoint, {x:0, data:"root", display:"block"}); //set display:block so that it remains visible even when the minimal skin is enabled. 451 if (!paused) { 452 linkedAnimation.resume(); 453 } 454 } 455 })[0], 456 outDrag = Draggable.create(outPoint, { 457 type:"x", 458 cursor:"ew-resize", 459 allowNativeTouchScrolling: false, 460 allowEventDefault:true, //otherwise, when dragged outside an iframe, the mouseup doesn't bubble up so it could seem "stuck" to the mouse. 461 zIndexBoost:false, 462 onPress:onPressTimeline(outPoint, 0, true), 463 onDoubleClick: resetInOut, 464 onDrag: function() { 465 outProgress = (timeAtDragStart + pixelToTimeRatio * this.x) / linkedAnimation.duration() * 100; 466 linkedAnimation.progress(outProgress / 100); 467 updateProgress(true); 468 }, 469 onRelease: function() { 470 if (outProgress > 100) { 471 outProgress = 100; 472 } 473 _clearSelection(); 474 //for responsiveness, convert the px-based transform into %-based left position. 475 outPoint.style.left = outProgress + "%"; 476 record("out", outProgress); 477 TweenLite.set(outPoint, {x:0, data:"root", display:"block"}); //set display:block so that it remains visible even when the minimal skin is enabled. 478 if (!pausedWhenDragStarted) { 479 play(); 480 linkedAnimation.resume(); 481 } 482 } 483 })[0], 484 updateProgress = function(force) { 485 if (progressDrag.isPressed && !force) { 486 return; 487 } 488 var p = (linkedAnimation.progress() * 100) || 0; 489 if (p > 100) { 490 p = 100; 491 } 492 if (p >= outProgress) { 493 if (loopEnabled && !linkedAnimation.paused() && !progressDrag.isDragging) { 494 p = inProgress; 495 if (linkedAnimation.target === selectedAnimation) { //in case there are callbacks on the timeline, when we jump back to the start we should seek() so that the playhead doesn't drag [backward] past those and trigger them. 496 linkedAnimation.target.seek(startTime + ((endTime - startTime) * inProgress / 100)); 497 } 498 linkedAnimation.progress(p / 100, true).resume(); 499 } else { 500 if (p !== outProgress) { 501 p = outProgress; 502 linkedAnimation.progress(p / 100); 503 } 504 if (!paused) { 505 pause(); 506 } 507 } 508 509 } else if (p < inProgress) { 510 p = inProgress; 511 linkedAnimation.progress(p / 100, true); 512 } 513 if (p !== progress || force) { 514 progressBar.style.left = inProgress + "%"; 515 progressBar.style.width = Math.max(0, p - inProgress) + "%"; 516 playhead.style.left = p + "%"; 517 timeLabel.innerHTML = linkedAnimation._time.toFixed(2);
518 durationLabel.innerHTML = linkedAnimation._duration.toFixed(2); 519 if (dragged) { 520 playhead.style.transform = "translate(-50%,0)"; 521 playhead._gsTransform.x = 0; 522 playhead._gsTransform.xPercent = -50; 523 dragged = false; 524 } 525 progress = p; 526 } else if (linkedAnimation._paused !== paused) { //like if the user has an addPause() in the middle of the animation. 527 togglePlayPause(); 528 } 529 }, 530 onPressSeekBar = function(e) { 531 if (progressDrag.isPressed) { 532 return; 533 } 534 var bounds = e.target.getBoundingClientRect(), 535 x = (e.changedTouches ? e.changedTouches[0] : e).clientX, 536 p = ((x - bounds.left) / bounds.width) * 100; 537 if (p < inProgress) { 538 inProgress = p = Math.max(0, p); 539 inPoint.style.left = inProgress + "%"; 540 inDrag.startDrag(e); 541 return; 542 } else if (p > outProgress) { 543 outProgress = p = Math.min(100, p); 544 outPoint.style.left = outProgress + "%"; 545 outDrag.startDrag(e); 546 return; 547 } 548 linkedAnimation.progress(p / 100).pause(); 549 updateProgress(true); 550 progressDrag.startDrag(e); 551 }, 552 553 554 555 //PLAY/PAUSE button 556 playPauseButton = find(".play-pause"), 557 playPauseMorph = _buildPlayPauseMorph(playPauseButton), 558 paused = false, 559 play = function() { 560 if (linkedAnimation.progress() >= outProgress / 100) { 561 if (linkedAnimation.target === selectedAnimation) { //in case there are callbacks on the timeline, when we jump back to the start we should seek() so that the playhead doesn't drag [backward] past those and trigger them. 562 linkedAnimation.target.seek(startTime + ((endTime - startTime) * inProgress / 100)); 563 } 564 linkedAnimation.progress(inProgress / 100, true); 565 } 566 playPauseMorph.play(); 567 linkedAnimation.play(); 568 if (paused) { 569 _self.update(); 570 } 571 paused = false; 572 }, 573 pause = function() { 574 playPauseMorph.reverse(); 575 if (linkedAnimation) { 576 linkedAnimation.pause(); 577 } 578 paused = true; 579 }, 580 togglePlayPause = function() { 581 if (paused) { 582 play(); 583 } else { 584 pause(); 585 } 586 }, 587 588 589 590 //REWIND button 591 onPressRewind = function(e) { 592 if (progressDrag.isPressed) { 593 return; 594 } 595 //_self.update(); 596 if (linkedAnimation.target === selectedAnimation) { //in case there are callbacks on the timeline, when we jump back to the start we should seek() so that the playhead doesn't drag [backward] past those and trigger them. 597 linkedAnimation.target.seek(startTime + ((endTime - startTime) * inProgress / 100)); 598 } 599 linkedAnimation.progress(inProgress / 100, true); 600 if (!paused) { 601 linkedAnimation.resume(); 602 } 603 }, 604 605 606 607 //LOOP button 608 loopButton = find(".loop"), 609 loopAnimation = _buildLoopAnimation(loopButton), 610 loopEnabled, 611 loop = function(value) { 612 loopEnabled = value; 613 record("loop", loopEnabled); 614 if (loopEnabled) { 615 loopAnimation.play(); 616 if (linkedAnimation.progress() >= outProgress / 100) { 617 if (linkedAnimation.target === selectedAnimation) { //in case there are callbacks on the timeline, when we jump back to the start we should seek() so that the playhead doesn't drag [backward] past those and trigger them. 618 linkedAnimation.target.seek(startTime + ((endTime - startTime) * inProgress / 100)); 619 } 620 linkedAnimation.progress(inProgress / 100, true).resume(); 621 } 622 } else { 623 loopAnimation.reverse(); 624 } 625 }, 626 627 toggleLoop = function() { 628 loop(!loopEnabled); 629 }, 630 631 632
633 //ANIMATIONS list 634 list = find(".animation-list"), 635 animationLabel = find(".animation-label"), 636 selectedAnimation, //the currently selected animation 637 linkedAnimation, //the animation that's linked to all the controls and scrubber. This is always _rootTween if globalSync is true, so it can be different than the selectedAnimation! 638 declaredAnimation, //whatever the user defines in the config object initially (often this will be null). If the user defines a string, it'll be resolved to a real Animation instance for this variable. 639 startTime, endTime, 640 updateList = function() { 641 var animations = _getChildrenOf((declaredAnimation && vars.globalSync === false) ? declaredAnimation : _recordedRoot, true), 642 options = list.children, 643 matches = 0, 644 option, i; 645 if (declaredAnimation && vars.globalSync === false) { 646 animations.unshift(declaredAnimation); 647 } else if (!vars.hideGlobalTimeline) { 648 animations.unshift(_recordedRoot); 649 } 650 for (i = 0; i < animations.length; i++) { 651 option = options[i] || _createElement("option", list); 652 option.animation = animations[i]; 653 matches = (i && animations[i].vars.id === animations[i-1].vars.id) ? matches + 1 : 0; 654 option.setAttribute("value", (option.innerHTML = animations[i].vars.id + (matches ? " [" + matches + "]" : (animations[i+1] && animations[i+1].vars.id === animations[i].vars.id) ? " [0]" : ""))); 655 } 656 for (; i < options.length; i++) { 657 list.removeChild(options[i]); 658 } 659 }, 660 animation = function(anim) { 661 var ts = 1, 662 tl, maxDuration; 663 if (!arguments.length) { 664 return selectedAnimation; 665 } 666 if (typeof(anim) === "string") { 667 anim = _getAnimationById(anim); 668 } 669 //console.log("animation() ", anim.vars.id); 670 if (!(anim instanceof Animation)) { 671 console.log("GSDevTools error: invalid animation."); 672 } 673 if (anim === selectedAnimation) { 674 return; 675 } 676 if (selectedAnimation) { 677 selectedAnimation._inProgress = inProgress; 678 selectedAnimation._outProgress = outProgress; 679 } 680 selectedAnimation = anim; 681 if (linkedAnimation) { 682 ts = linkedAnimation.timeScale(); 683 if (linkedAnimation.target === declaredAnimation) { 684 declaredAnimation.resume(); 685 linkedAnimation.kill(); 686 } 687 } 688 inProgress = selectedAnimation._inProgress || 0; 689 outProgress = selectedAnimation._outProgress || 100; 690 inPoint.style.left = inProgress + "%"; 691 outPoint.style.left = outProgress + "%"; 692 if (_fullyInitialized) { //don't record inProgress/outProgress unless we're fully instantiated because people may call GSDevTools.create() before creating/defining their animations, thus the inTime/outTime may not exist yet. 693 record("animation", selectedAnimation.vars.id); 694 record("in", inProgress); 695 record("out", outProgress); 696 } 697 startTime = 0; 698 maxDuration = Math.min(1000, vars.maxDuration || 1000, _getClippedDuration(selectedAnimation)); 699 if (selectedAnimation === _recordedRoot || vars.globalSync !== false) { 700 _merge(); 701 linkedAnimation = _rootTween; 702 if (_rootInstance && _rootInstance !== _self) { 703 console.log("Error: GSDevTools can only have one instance that's globally synchronized."); 704 } 705 _rootInstance = _self; 706 //_recording = true; 707 if (selectedAnimation !== _recordedRoot) { 708 tl = selectedAnimation; 709 endTime = tl.totalDuration(); 710 if (endTime > 99999999) { //in the case of an infinitely repeating animation, just use a single iteration's duration instead. 711 endTime = tl.duration(); 712 } 713 while (tl.timeline.timeline) { 714 startTime = (startTime / tl._timeScale) + tl._startTime; 715 endTime = (endTime / tl._timeScale) + tl._startTime; 716 tl = tl.timeline; 717 } 718 } else { 719 endTime = _recordedRoot.duration(); 720 } 721 if (endTime - startTime > maxDuration) { //cap end time at 1000 because it doesn't seem reasonable to accommodate super long stuff. 722 endTime = startTime + maxDuration; 723 } 724 _recordedRoot.pause(startTime); 725 _rootTween.vars.time = endTime; 726 _rootTween.invalidate();
727 _rootTween.duration(endTime - startTime).timeScale(ts); 728 //wait for a tick before starting because some browsers freeze things immediately following a <select> selection, like on MacOS it flashes a few times before disappearing, so this prevents a "jump". 729 if (paused) { 730 //jump forward and then back in order to make sure the start/end values are recorded internally right away and don't drift outside this tween. 731 _rootTween.progress(1).pause(0); 732 } else { 733 TweenLite.delayedCall(0.01, function() { 734 _rootTween.resume().progress(inProgress / 100); 735 if (paused) { 736 play(); 737 } 738 }); 739 } 740 741 } else { 742 if (_rootInstance === _self) { 743 _rootInstance = null; 744 } 745 if (selectedAnimation === declaredAnimation || !declaredAnimation) { 746 linkedAnimation = selectedAnimation; 747 } else { //if an animation is declared in the config object, and the user chooses a sub-animation (nested), we tween the playhead of the declaredAnimation to keep everything synchronized even though globalSync isn't true. 748 tl = selectedAnimation; 749 endTime = tl.totalDuration(); 750 if (endTime > 99999999) { //in the case of an infinitely repeating animation, just use a single iteration's duration instead. 751 endTime = tl.duration(); 752 } 753 while (tl.timeline.timeline && tl !== declaredAnimation) { 754 startTime = (startTime / tl._timeScale) + tl._startTime; 755 endTime = (endTime / tl._timeScale) + tl._startTime; 756 tl = tl.timeline; 757 } 758 if (endTime - startTime > maxDuration) { //cap end time at 1000 because it doesn't seem reasonable to accommodate super long stuff. 759 endTime = startTime + maxDuration; 760 } 761 declaredAnimation.pause(startTime); 762 linkedAnimation = TweenLite.to(declaredAnimation, endTime - startTime, {time:endTime, ease:Linear.easeNone, data:"root"}); 763 linkedAnimation.timeScale(ts); 764 } 765 _rootTween.pause(); 766 _recordedRoot.resume(); 767 linkedAnimation.seek(0); 768 } 769 770 durationLabel.innerHTML = linkedAnimation.duration().toFixed(2); 771 _selectValue(list, selectedAnimation.vars.id, animationLabel); 772 }, 773 updateRootDuration = function() { 774 var time, ratio, duration; 775 if (selectedAnimation === _recordedRoot) { 776 time = _recordedRoot._time; 777 _recordedRoot.progress(1, true).time(time, true); //jump to the end and back again because sometimes a tween that hasn't rendered yet will affect duration, like a TimelineMax.tweenTo() where the duration gets set in the onStart. 778 time = (_rootTween._timeline._time - _rootTween._startTime) * _rootTween._timeScale; 779 duration = Math.min(1000, _recordedRoot.duration()); 780 if (duration === 1000) { 781 duration = Math.min(1000, _getClippedDuration(_recordedRoot)); 782 } 783 ratio = _rootTween.duration() / duration; 784 if (ratio !== 1 && duration) { 785 inProgress *= ratio; 786 if (outProgress < 100) { 787 outProgress *= ratio; 788 } 789 _rootTween.seek(0); 790 _rootTween.vars.time = duration; 791 _rootTween.invalidate(); 792 _rootTween.duration(duration); 793 _rootTween.time(time); 794 durationLabel.innerHTML = duration.toFixed(2); 795 updateProgress(true); 796 } 797 } 798 }, 799 onChangeAnimation = function(e) { 800 animation(list.options[list.selectedIndex].animation); 801 if (e.target && e.target.blur) { //so that if an option is selected, and then the user tries to hit the up/down arrow, it doesn't just try selecting something else in the <select>. 802 e.target.blur(); 803 } 804 if (paused) { 805 play(); 806 } 807 }, 808 809 810 811 //TIMESCALE button 812 timeScale = find(".time-scale select"), 813 timeScaleLabel = find(".time-scale-label"), 814 onChangeTimeScale = function(e) { 815 var ts = parseFloat(timeScale.options[timeScale.selectedIndex].value) || 1; 816 linkedAnimation.timeScale(ts); 817 record("timeScale", ts); 818 if (!paused) { 819 if (linkedAnimation.progress() >= outProgress / 100) { 820 if (linkedAnimation.target === selectedAnimation) { //in case there are callbacks on the timeline, when we jump back to the start we should seek() so that the playhead doesn't drag [backward] past those and trigger them. 821 linkedAnimation.target.seek(startTime + ((endTime - startTime) * inProgress / 100)); 822 } 823 linkedAnimation.progress(inProgress / 100, true).pause(); 824 } else { 825 linkedAnimation.pause(); 826 } 827 TweenLite.delayedCall(0.01, function() { 828 linkedAnimation.resume(); 829 }); 830 } 831 timeScaleLabel.innerHTML = ts + "x"; 832 if (timeScale.blur) { //so that if an option is selected, and then the user tries to hit the up/down arrow, it doesn't just try selecting something else in the <select>. 833 timeScale.blur(); 834 } 835 }, 836 837 838 839 //AUTOHIDE 840 autoHideTween = TweenLite.to([find(".gs-bottom"), find(".gs-top")], 0.3, {autoAlpha:0, y:50, ease:Power2.easeIn, data:"root", paused:true}), 841 hidden = false, 842 onMouseOut = function(e) { 843 if (!Draggable.hitTest(e, root) && !progressDrag.isDragging && !inDrag.isDragging && !outDrag.isDragging) { 844 autoHideDelayedCall.restart(true); 845 } 846 }, 847 hide = function() {
848 if (!hidden) { 849 autoHideTween.play(); 850 autoHideDelayedCall.pause(); 851 hidden = true; 852 } 853 }, 854 show = function() { 855 autoHideDelayedCall.pause(); 856 if (hidden) { 857 autoHideTween.reverse(); 858 hidden = false; 859 } 860 }, 861 toggleHide = function() { 862 if (hidden) { 863 show(); 864 } else { 865 hide(); 866 } 867 }, 868 autoHideDelayedCall = TweenLite.delayedCall(1.3, hide).pause(), 869 _fullyInitialized, //we call initialize() initially, and then again on the very next tick just in case someone called GSDevTools.create() BEFORE they create their animations. This variable tracks that state. Note: we don't record sessionStorage.setItem() until we're fully initialized, otherwise we may inadvertently set in/out points to the defaults just because the animation couldn't be found (yet). 870 keyboardHandler, 871 initialize = function(preliminary) { 872 //if on startup, someone does a timeline.seek(), we must honor it, so when initialize() is called, we record _recordedRoot._startTime so that we can use that as an offset. Remember, however, that we call initialize() twice on startup, once after a tick has elapsed just in case someone called GSDevTools.create() before their animation code, so we must record the value (once). 873 if (_startupPhase && !_globalStartTime) { 874 _globalStartTime = _recordedRoot._startTime; 875 } 876 _fullyInitialized = !preliminary; 877 declaredAnimation = _getAnimationById(vars.animation); 878 if (declaredAnimation && !declaredAnimation.vars.id) { 879 declaredAnimation.vars.id = "[no id]"; 880 } 881 updateList(); 882 var savedAnimation = _getAnimationById(recall("animation")); 883 if (savedAnimation) { 884 savedAnimation._inProgress = recall("in") || 0; 885 savedAnimation._outProgress = recall("out") || 100; 886 } 887 if (vars.paused) { 888 pause(); 889 } 890 selectedAnimation = null; 891 animation(declaredAnimation || savedAnimation || _recordedRoot); 892 var ts = vars.timeScale || recall("timeScale"), 893 savedInOut = (savedAnimation === selectedAnimation); 894 if (ts) { 895 _selectValue(timeScale, ts, timeScaleLabel, ts + "x"); 896 _rootTween.timeScale(ts); 897 } 898 inProgress = (("inTime" in vars) ? _timeToProgress(vars.inTime, selectedAnimation, 0, 0) : savedInOut ? savedAnimation._inProgress : 0) || 0; 899 if (inProgress === 100 && !vars.animation && savedAnimation) { //in case there's a recorded animation (sessionStorage) and then the user defines an inTime that exceeds that animation's duration, just default back to the Global Timeline. Otherwise the in/out point will be at the very end and it'd be weird. 900 animation(_recordedRoot); 901 inProgress = _timeToProgress(vars.inTime, selectedAnimation, 0, 0) || 0; 902 } 903 if (inProgress) { 904 inPoint.style.left = inProgress + "%"; 905 inPoint.style.display = outPoint.style.display = "block"; //set display:block so that it remains visible even when the minimal skin is enabled. 906 } 907 outProgress = (("outTime" in vars) ? _timeToProgress(vars.outTime, selectedAnimation, 100, inProgress) : savedInOut ? savedAnimation._outProgress : 0) || 100; 908 if (outProgress < inProgress) { 909 outProgress = 100; 910 } 911 if (outProgress !== 100) { 912 outPoint.style.left = outProgress + "%"; 913 inPoint.style.display = outPoint.style.display = "block"; //set display:block so that it remains visible even when the minimal skin is enabled. 914 } 915 loopEnabled = ("loop" in vars) ? vars.loop : recall("loop"); 916 if (loopEnabled) { 917 loop(true); 918 } 919 if (vars.paused) { 920 linkedAnimation.progress(inProgress / 100, true).pause(); 921 } 922 if (_startupPhase && selectedAnimation === _recordedRoot && _globalStartTime && vars.globalSync !== false && !paused) { 923 linkedAnimation.time(-_globalStartTime, true); 924 } 925 updateProgress(true); 926 }; 927 928 929 930 //INITIALIZATION TASKS 931 _addListener(list, "change", onChangeAnimation); 932 _addListener(list, "mousedown", updateList); 933 _addListener(playPauseButton, "mousedown", togglePlayPause); 934 _addListener(find(".seek-bar"), "mousedown", onPressSeekBar); 935 _addListener(find(".rewind"), "mousedown", onPressRewind); 936 _addListener(loopButton, "mousedown", toggleLoop); 937 _addListener(timeScale, "change", onChangeTimeScale); 938 939 if (vars.visibility === "auto") { 940 _addListener(root, "mouseout", onMouseOut); 941 //_addListener(find(".gs-hit-area"), "mouseover", show); 942 _addListener(root, "mouseover", show); 943 944 } else if (vars.visibility === "hidden") { 945 hidden = true; 946 autoHideTween.progress(1); 947 } 948 949 if (vars.keyboard !== false) { 950 if (_keyboardInstance && vars.keyboard) { 951 console.log("[GSDevTools warning] only one instance can be affected by keyboard shortcuts. There is already one active."); 952 } else { 953 _keyboardInstance = _self; //we can't have multiple instances all affected by the keyboard. 954 keyboardHandler = function(e) { //window.parent allows things to work inside of an iframe, like on codepen. 955 var key = e.keyCode ? e.keyCode : e.which, 956 ts; 957 if (key === 32) { //spacebar 958 togglePlayPause(); 959 } else if (key === 38) { //up arrow 960 ts = parseFloat(_shiftSelectedValue(timeScale, -1, timeScaleLabel)); 961 linkedAnimation.timeScale(ts); 962 record("timeScale", ts); 963 } else if (key === 40) { //down arrow 964 ts = parseFloat(_shiftSelectedValue(timeScale, 1, timeScaleLabel)); 965 linkedAnimation.timeScale(ts); 966 record("timeScale", ts); 967 } else if (key === 37) { //left arrow 968 onPressRewind(e); 969 } else if (key === 39) { //right arrow 970 linkedAnimation.progress(outProgress / 100); 971 } else if (key === 76) { //"L" key 972 toggleLoop(); 973 } else if (key === 72) { //"H" key 974 toggleHide(); 975 } else if (key === 73) { //"I" key 976 inProgress = linkedAnimation.progress() * 100; 977 record("in", inProgress); 978 inPoint.style.left = inProgress + "%"; 979 updateProgress(true); 980 } else if (key === 79) { //"O" key 981 outProgress = linkedAnimation.progress() * 100; 982 record("out", outProgress); 983 outPoint.style.left = outProgress + "%"; 984 updateProgress(true); 985 } 986 }; 987 _addListener(_docElement, "keydown", keyboardHandler); 988 } 989 } 990 991 992 TweenLite.set(playhead, {xPercent:-50, x:0, data:"root"}); //so that when we drag, x is properly discerned (browsers report in pure pixels rather than percents) 993 TweenLite.set(inPoint, {xPercent:-100, x:0, data:"root"}); 994 inPoint._gsIgnore = outPoint._gsIgnore = playhead._gsIgnore = playPauseButton._gsIgnore = loopButton._gsIgnore = true; 995 996 //Draggable fires off a TweenLite.set() that affects the transforms, and we don't want them to get into the _recordedRoot, so kill those tweens. 997 TweenLite.killTweensOf([inPoint, outPoint, playhead]); 998 999 1000 initialize(_startupPhase); 1001 if (_startupPhase) { 1002 //developers may call GSDevTools.create() before they even create some of their animations, so the inTime/outTime or animation values may not exist, thus we wait for 1 tick and initialize again, just in case. 1003 TweenLite.delayedCall(0.0001, initialize, [false], this); 1004 } 1005 TweenLite.ticker.addEventListener("tick", updateProgress); 1006 1007 this.update = function(forceMerge) { 1008 if (_rootInstance === _self) { 1009 if (!_rootTween._paused || forceMerge) { 1010 _merge(); 1011 } 1012 updateRootDuration(); 1013 } 1014 }; 1015 1016 this.kill = function() { 1017 _removeListener(list, "change", onChangeAnimation); 1018 _removeListener(list, "mousedown", updateList); 1019 _removeListener(playPauseButton, "mousedown", togglePlayPause); 1020 _removeListener(find(".seek-bar"), "mousedown", onPressSeekBar); 1021 _removeListener(find(".rewind"), "mousedown", onPressRewind); 1022 _removeListener(loopButton, "mousedown", toggleLoop); 1023 _removeListener(timeScale, "change", onChangeTimeScale); 1024 progressDrag.disable(); 1025 inDrag.disable(); 1026 outDrag.disable(); 1027 TweenLite.ticker.removeEventListener("tick", updateProgress); 1028 _removeListener(root, "mouseout", onMouseOut); 1029 _removeListener(root, "mouseover", show); 1030 _removeListener(_docElement, "keydown", keyboardHandler); 1031 root.parentNode.removeChild(root); 1032 if (_rootInstance === _self) { 1033 _rootInstance = null; 1034 } 1035 delete _lookup[vars.id + ""]; 1036 }; 1037 1038 this.minimal = function(value) { 1039 var isMinimal = root.classList.contains("minimal"), 1040 p; 1041 if (!arguments.length || isMinimal === value) { 1042 return isMinimal; 1043 } 1044 if (value) { 1045 root.classList.add("minimal"); 1046 } else { 1047 root.classList.remove("minimal"); 1048 } 1049 if (vars.container) { 1050 root.style.top = value ? "calc(100% - 42px)" : "calc(100% - 51px)"; 1051 } 1052 if (progressDrag.isPressed) { 1053 skipDragUpdates = true; //just in case there's actually a tween/timeline in the linkedAnimation that is altering this GSDevTool instance's "minimal()" value, it could trigger a recursive loop in the drag handlers, like if they update linkedAnimation's time/progress which in turn triggers this minimal() function which in turn dues the same, and so on. 1054 progressDrag.endDrag(progressDrag.pointerEvent); 1055 skipDragUpdates = false;
1056 1057 p = linkedAnimation.progress() * 100; 1058 progressBar.style.width = Math.max(0, p - inProgress) + "%"; 1059 playhead.style.left = p + "%"; 1060 playhead.style.transform = "translate(-50%,0)"; 1061 playhead._gsTransform.x = 0; 1062 playhead._gsTransform.xPercent = -50; 1063 1064 progressDrag.startDrag(progressDrag.pointerEvent, true); 1065 } 1066 }; 1067 1068 //expose methods: 1069 this.animation = animation; 1070 this.updateList = updateList; 1071 1072 }, 1073 _recording = true, 1074 _startupPhase = true, //for the first 2 seconds, we don't record any zero-duration tweens because they're typically just setup stuff and/or the "from" or "startAt" tweens. In version 1.20.3 we started flagging those with data:"isStart"|"isFromStart" but this logic helps GSDevTools work with older versions too. 1075 _onOverwrite = TweenLite.onOverwrite, 1076 _globalStartTime = 0; //if on startup, someone does a timeline.seek(), we need to honor it, so when initialize() is called, it'll check the _recordedRoot._startTime so that we can use that as an offset. Remember, however, that we call initialize() twice on startup, once after a tick has elapsed just in case someone called GSDevTools.create() before their animation code, so we must record the value (once). 1077 1078 1079 1080 GSDevTools.version = "0.1.7"; 1081 GSDevTools.logOverwrites = false; 1082 GSDevTools.globalRecordingTime = 2; 1083 1084 GSDevTools.getById = function(id) { 1085 return id ? _lookup[id] : _rootInstance; 1086 }; 1087 1088 //align the all of the playheads so they're starting at 0 now. 1089 _globalTimeline._startTime += _globalTimeline._time; 1090 _recordedRoot._startTime = _recordedTemp._startTime = _globalTimeline._time = _globalTimeline._totalTime = 0; 1091 1092 //in case GSDevTools.create() is called before anything is actually on the global timeline, we've gotta update it or else the duration will be 0 and it'll be stuck. 1093 TweenLite.delayedCall(0.01, function() { 1094 if (_rootInstance) { 1095 _rootInstance.update(); 1096 } else { 1097 _merge(); 1098 } 1099 }); 1100 1101 //initially we record everything into the _recordedRoot TimelineLite because developers might call GSDevTools.create() AFTER some of their code executes, but after 2 seconds if there aren't any GSDevTool instances that have globalSync enabled, we should dump all the stuff from _recordedRoot into the global timeline to improve performance and avoid issues where _recordedRoot is paused and reaches its end and wants to stop the playhead. 1102 TweenLite.delayedCall(2, function() { 1103 var t, next, offset; 1104 if (!_rootInstance) { 1105 _merge(); 1106 t = _recordedRoot._first; 1107 offset = _recordedRoot._startTime; 1108 while (t) { 1109 next = t._next; 1110 //any animations that aren't finished should be dumped into the root timeline. If they're done, just kill them. 1111 if (t._totalDuration !== t._totalTime || t.ratio !== 1) { 1112 SimpleTimeline.prototype.add.call(_globalTimeline, t, t._startTime - t._delay + offset); 1113 } else { 1114 t.kill(); 1115 } 1116 t = next; 1117 } 1118 } 1119 if (GSDevTools.globalRecordingTime > 2) { 1120 TweenLite.delayedCall(GSDevTools.globalRecordingTime - 2, function() { 1121 if (_rootInstance) { 1122 _rootInstance.update(); 1123 } 1124 _recording = false; 1125 }); 1126 } else { 1127 _recording = false; 1128 } 1129 _startupPhase = false; 1130 }); 1131 1132 //hijack the adding of all children of the global timeline and dump them into _recordedRoot instead, unless they are "rooted" or "exported". If _recordedRoot is paused, we dump them into _recordedTemp so that they actually work, and we merge them back into _recordedRoot when it becomes unpaused. 1133 _globalTimeline.add = function(child, position, align, stagger) { 1134 var data = child.data; 1135 //TODO: idea: what if we ignore any zero-duration tweens that occur in the first second or two, and then lock the "global timeline" down after that anyway? Perhaps offer a way to update() manually? 1136 if (_recording && child.vars && data !== "root" && data !== "ignore" && data !== "isStart" && data !== "isFromStart" && data !== "_draggable" && !(_startupPhase && !child._duration && child instanceof TweenLite) && !(child.vars.onComplete && child.vars.onComplete === child.vars.onReverseComplete)) { //skip delayedCalls too 1137 var tl = _recordedRoot; 1138 if (_rootTween._time) { 1139 if (_rootTween._paused) { 1140 tl = _recordedTemp; 1141 child._recordedTime = _recordedRoot.rawTime(); 1142 } else { 1143 position = (_globalTimeline._time - _rootTween._startTime) * _rootTween._timeScale; 1144 if (!_rootIsDirty) { 1145 TweenLite.ticker.addEventListener("tick", _updateRootDuration); 1146 _rootIsDirty = true; 1147 } 1148 } 1149 } 1150 tl.add(child, position, align, stagger); 1151 //TODO: what if timeScale is set initially low, and a delayedCall() fires another animation?? It'd fire early. 1152 1153 if (child.vars.repeat) { //before 1.20.3, repeated TweenMax/TimelineMax instances didn't have their repeats applied until after the constructor ran (which was after it was added to its timeline), so we need to make sure _dirty is true in order to trigger re-calculation of totalDuration. Otherwise, _recordedRoot/_recordedTemp might stop at the end of the first iteration of a repeated tween. 1154 tl._dirty = true; 1155 } 1156 return this; 1157 } 1158 return SimpleTimeline.prototype.add.apply(this, arguments); 1159 }; 1160 _recordedRoot._enabled = _recordedTemp._enabled = function(value, ignoreTimeline) { 1161 //skip enabling/disabling the nested animations (performance improvement). SimpleTimeline._enabled() doesn't do that - only TimelineLite._enabled() does. 1162 return SimpleTimeline.prototype._enabled.apply(this, arguments); 1163 }; 1164 TimelineLite.prototype._remove = function(tween, skipDisable) { //replace TimelineLite's _remove because before 1.19.1, there was a bug that c
1164ould cause _time to jump to the end. We want to make sure GSDevTools can still work properly with slightly older versions. 1165 SimpleTimeline.prototype._remove.apply(this, arguments); 1166 var last = this._last; 1167 if (!last) { 1168 this._time = this._totalTime = this._duration = this._totalDuration = 0; 1169 } else if (this._time > this.duration()) { 1170 this._time = this._duration; 1171 this._totalTime = this._totalDuration; 1172 } 1173 return this; 1174 }; 1175 1176 TweenLite.onOverwrite = function(overwrittenTween, overwritingTween, target, overwrittenProperties) { 1177 if (GSDevTools.logOverwrites) { 1178 if (overwrittenProperties) { 1179 console.log("[Overwrite warning] the following properties were overwritten: ", overwrittenProperties, "| target:", target, "| overwritten tween: ", overwrittenTween, "| overwriting tween:", overwritingTween); 1180 } else { 1181 console.log("[Overwrite warning] the following tween was overwritten:", overwrittenTween, "by", overwritingTween); 1182 } 1183 } 1184 if (typeof(_onOverwrite) === "function") { 1185 _onOverwrite(overwrittenTween, overwritingTween, target, overwrittenProperties); 1186 } 1187 }; 1188 1189 GSDevTools.create = function(vars) { 1190 return new GSDevTools(vars); 1191 }; 1192 1193 return GSDevTools; 1194 1195 }, true); 1196 1197}); if (_gsScope._gsDefine) { _gsScope._gsQueue.pop()(); } 1198 1199//export to AMD/RequireJS and CommonJS/Node (precursor to full modular build system coming at a later date) 1200(function(name) { 1201 "use strict"; 1202 var getGlobal = function() { 1203 return (_gsScope.GreenSockGlobals || _gsScope)[name]; 1204 }; 1205 if (typeof(module) !== "undefined" && module.exports) { //node 1206 require("../TweenLite.js"); 1207 require("../TimelineLite.js"); 1208 require("../plugins/CSSPlugin.js"); 1209 module.exports = getGlobal(); 1210 } else if (typeof(define) === "function" && define.amd) { //AMD 1211 define(["TweenLite", "TimelineLite", "CSSPlugin"], getGlobal); 1212 } 1213}("GSDevTools")); 1214 1215 1216 1217 1218 1219 1220 1221 1222 1223 1224 1225 1226 1227 1228 1229 1230 1231 1232 1233 1234 1235 1236 1237 1238 1239 1240 1241 1242 1243 1244 1245 1246 1247 1248 1249 1250 1251 1252 1253 1254 1255 1256 1257 1258 1259 1260 1261 1262 1263 1264 1265 1266 1267 1268 1269 1270 1271 1272 1273 1274 1275 1276 1277 1278 1279 1280 1281 1282/*! 1283 * VERSION: 0.16.2 1284 * DATE: 2018-01-31 1285 * UPDATES AND DOCS AT: http://greensock.com 1286 * 1287 * Requires TweenLite and CSSPlugin version 1.17.0 or later (TweenMax contains both TweenLite and CSSPlugin). ThrowPropsPlugin is required for momentum-based continuation of movement after the mouse/touch is released (ThrowPropsPlugin is a membership benefit of Club GreenSock - http://greensock.com/club/). 1288 * 1289 * @license Copyright (c) 2008-2018, GreenSock. All rights reserved. 1290 * This work is subject to the terms at http://greensock.com/standard-license or for 1291 * Club GreenSock members, the software agreement that was issued with your membership. 1292 * 1293 * @author: Jack Doyle, [email protected] 1294 */ 1295var _gsScope = (typeof(module) !== "undefined" && module.exports && typeof(global) !== "undefined") ? global : this || window; //helps ensure compatibility with AMD/RequireJS and CommonJS/Node 1296(_gsScope._gsQueue || (_gsScope._gsQueue = [])).push( function() { 1297 1298 "use strict"; 1299 1300 _gsScope._gsDefine("utils.Draggable", ["events.EventDispatcher","TweenLite","plugins.CSSPlugin"], function(EventDispatcher, TweenLite, CSSPlugin) { 1301 1302 var _tempVarsXY = {css:{}, data:"_draggable"}, //speed optimization - we reuse the same vars object for x/y TweenLite.set() calls to minimize garbage collection tasks and improve performance. 1303 _tempVarsX = {css:{}, data:"_draggable"}, 1304 _tempVarsY = {css:{}, data:"_draggable"}, 1305 _tempVarsRotation = {css:{}}, 1306 _globals = _gsScope._gsDefine.globals, 1307 _tempEvent = {}, //for populating with pageX/pageY in old versions of IE 1308 _dummyElement = {style:{}}, 1309 _doc = _gsScope.document || {createElement: function() {return _dummyElement;}}, 1310 _docElement = _doc.documentElement || {}, 1311 _createElement = function(type) { 1312 return _doc.createElementNS ? _doc.createElementNS("http://www.w3.org/1999/xhtml", type) : _doc.createElement(type); 1313 }, 1314 _tempDiv = _createElement("div"), 1315 _emptyArray = [], 1316 _emptyFunc = function() { return false; }, 1317 _RAD2DEG = 180 / Math.PI, 1318 _max = 999999999999999, 1319 _getTime = Date.now || function() {return new Date().getTime();}, 1320 _isOldIE = !!(!_doc.addEventListener && _doc.all), 1321 _placeholderDiv = _doc.createElement("div"), 1322 _renderQueue = [], 1323 _lookup = {}, //when a Draggable is created, the target gets a unique _gsDragID property that allows gets associated with the Draggable instance for quick lookups in Draggable.get(). This avoids circular references that could cause gc problems. 1324 _lookupCount = 0, 1325 _clickableTagExp = /^(?:a|input|textarea|button|select)$/i, 1326 _dragCount = 0, //total number of elements currently being dragged 1327 _prefix, 1328 _isMultiTouching, 1329 _isAndroid = (_gsScope.navigator && _gsScope.navigator.userAgent.toLowerCase().indexOf("android") !== -1), //Android handles touch events in an odd way and it's virtually impossible to "feature test" so we resort to UA sniffing 1330 _lastDragTime = 0, 1331 _temp1 = {}, // a simple object we reuse and populate (usually x/y properties) to conserve memory and improve performance. 1332 _windowProxy = {}, //memory/performance optimization - we reuse this object during autoScroll to store window-related bounds/offsets. 1333 _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() 1334 if (typeof(a) === "string") { 1335 a = TweenLite.selector(a); 1336 } 1337 if (!a || a.nodeType) { //if it's not an array, wrap it in one. 1338 return [a]; 1339 } 1340 var b = [], 1341 l = a.length, 1342 i; 1343 for (i = 0; i !== l; b.push(a[i++])); 1344 return b; 1345 }, 1346 _copy = function(obj, factor) { 1347 var copy = {}, p; 1348 if (factor) { 1349 for (p in obj) { 1350 copy[p] = obj[p] * factor; 1351 } 1352 } else { 1353 for (p in obj) { 1354 copy[p] = obj[p]; 1355 } 1356 } 1357 return copy; 1358 }, 1359 ThrowPropsPlugin, 1360 1361 _renderQueueTick = function() { 1362 var i = _renderQueue.length; 1363 while (--i > -1) { 1364 _renderQueue[i](); 1365 } 1366 }, 1367 _addToRenderQueue = function(func) { 1368 _renderQueue.push(func); 1369 if (_renderQueue.length === 1) { 1370 TweenLite.ticker.addEventListener("tick", _renderQueueTick, this, false, 1); 1371 } 1372 }, 1373 _removeFromRenderQueue = function(func) { 1374 var i = _renderQueue.length; 1375 while (--i > -1) { 1376 if (_renderQueue[i] === func) { 1377 _renderQueue.splice(i, 1); 1378 } 1379 } 1380 TweenLite.to(_renderQueueTimeout, 0, {overwrite:"all", delay:15, onComplete:_renderQueueTimeout, data:"_draggable"}); //remove the "tick" listener only after the render queue is empty for 15 seconds (to improve performance). Adding/removing it constantly for every click/touch wouldn't deliver optimal speed, and we also don't want the ticker to keep calling the render method when things are idle for long periods of time (we want to improve battery life on mobile devices). 1381 }, 1382 _renderQueueTimeout = function() { 1383 if (!_renderQueue.length) { 1384 TweenLite.ticker.removeEventListener("tick", _renderQueueTick); 1385 } 1386 }, 1387 1388 _extend = function(obj, defaults) { 1389 var p; 1390 for (p in defaults) { 1391 if (obj[p] === undefined) { 1392 obj[p] = defaults[p]; 1393 } 1394 } 1395 return obj; 1396 }, 1397 _getDocScrollTop = function() { 1398 return (window.pageYOffset != null) ? window.pageYOffset : (_doc.scrollTop != null) ? _doc.scrollTop : _docElement.scrollTop || _doc.body.scrollTop || 0; 1399 }, 1400 _getDocScrollLeft = function() { 1401 return (window.pageXOffset != null) ? window.pageXOffset : (_doc.scrollLeft != null) ? _doc.scrollLeft : _docElement.scrollLeft || _doc.body.scrollLeft || 0; 1402 },
1403 _addScrollListener = function(e, callback) { 1404 _addListener(e, "scroll", callback); 1405 if (!_isRoot(e.parentNode)) { 1406 _addScrollListener(e.parentNode, callback); 1407 } 1408 }, 1409 _removeScrollListener = function(e, callback) { 1410 _removeListener(e, "scroll", callback); 1411 if (!_isRoot(e.parentNode)) { 1412 _removeScrollListener(e.parentNode, callback); 1413 } 1414 }, 1415 _isRoot = function (e) { 1416 return !!(!e || e === _docElement || e === _doc || e === _doc.body || e === window || !e.nodeType || !e.parentNode); 1417 }, 1418 _getMaxScroll = function(element, axis) { 1419 var dim = (axis === "x") ? "Width" : "Height", 1420 scroll = "scroll" + dim, 1421 client = "client" + dim, 1422 body = _doc.body; 1423 return Math.max(0, _isRoot(element) ? Math.max(_docElement[scroll], body[scroll]) - (window["inner" + dim] || _docElement[client] || body[client]) : element[scroll] - element[client]); 1424 }, 1425 _recordMaxScrolls = function(e) { //records _gsMaxScrollX and _gsMaxScrollY properties for the element and all ancestors up the chain so that we can cap it, otherwise dragging beyond the edges with autoScroll on can endlessly scroll. 1426 var isRoot = _isRoot(e), 1427 x = _getMaxScroll(e, "x"), 1428 y = _getMaxScroll(e, "y"); 1429 if (isRoot) { 1430 e = _windowProxy; 1431 } else { 1432 _recordMaxScrolls(e.parentNode); 1433 } 1434 e._gsMaxScrollX = x; 1435 e._gsMaxScrollY = y; 1436 e._gsScrollX = e.scrollLeft || 0; 1437 e._gsScrollY = e.scrollTop || 0; 1438 }, 1439 1440 //just used for IE8 and earlier to normalize events and populate pageX/pageY 1441 _populateIEEvent = function(e, preventDefault) { 1442 e = e || window.event; 1443 _tempEvent.pageX = e.clientX + _doc.body.scrollLeft + _docElement.scrollLeft; 1444 _tempEvent.pageY = e.clientY + _doc.body.scrollTop + _docElement.scrollTop; 1445 if (preventDefault) { 1446 e.returnValue = false; 1447 } 1448 return _tempEvent; 1449 }, 1450 1451 //grabs the first element it finds (and we include the window as an element), so if it's selector text, it'll feed that value to TweenLite.selector, if it's a jQuery object or some other selector engine's result, it'll grab the first one, and same for an array. If the value doesn't contain a DOM element, it'll just return null. 1452 _unwrapElement = function(value) { 1453 if (!value) { 1454 return value; 1455 } 1456 if (typeof(value) === "string") { 1457 value = TweenLite.selector(value); 1458 } 1459 if (value.length && value !== window && value[0] && value[0].style && !value.nodeType) { 1460 value = value[0]; 1461 } 1462 return (value === window || (value.nodeType && value.style)) ? value : null; 1463 }, 1464 1465 _checkPrefix = function(e, p) { 1466 var s = e.style, 1467 capped, i, a; 1468 if (s[p] === undefined) { 1469 a = ["O","Moz","ms","Ms","Webkit"]; 1470 i = 5; 1471 capped = p.charAt(0).toUpperCase() + p.substr(1); 1472 while (--i > -1 && s[a[i]+capped] === undefined) { } 1473 if (i < 0) { 1474 return ""; 1475 } 1476 _prefix = (i === 3) ? "ms" : a[i]; 1477 p = _prefix + capped; 1478 } 1479 return p; 1480 }, 1481 1482 _setStyle = function(e, p, value) { 1483 var s = e.style; 1484 if (!s) { 1485 return; 1486 } 1487 if (s[p] === undefined) { 1488 p = _checkPrefix(e, p); 1489 } 1490 if (value == null) { 1491 if (s.removeProperty) { 1492 s.removeProperty(p.replace(/([A-Z])/g, "-$1").toLowerCase()); 1493 } else { //note: old versions of IE use "removeAttribute()" instead of "removeProperty()" 1494 s.removeAttribute(p); 1495 } 1496 } else if (s[p] !== undefined) { 1497 s[p] = value; 1498 } 1499 }, 1500 1501 _getComputedStyle = _doc.defaultView ? _doc.defaultView.getComputedStyle : _emptyFunc, 1502 _horizExp = /(?:Left|Right|Width)/i, 1503 _suffixExp = /(?:\d|\-|\+|=|#|\.)*/g, 1504 _convertToPixels = function(t, p, v, sfx, recurse) { 1505 if (sfx === "px" || !sfx) { return v; } 1506 if (sfx === "auto" || !v) { return 0; } 1507 var horiz = _horizExp.test(p), 1508 node = t, 1509 style = _tempDiv.style, 1510 neg = (v < 0), 1511 pix; 1512 if (neg) { 1513 v = -v; 1514 } 1515 if (sfx === "%" && p.indexOf("border") !== -1) { 1516 pix = (v / 100) * (horiz ? t.clientWidth : t.clientHeight); 1517 } else { 1518 style.cssText = "border:0 solid red;position:" + _getStyle(t, "position", true) + ";line-height:0;"; 1519 if (sfx === "%" || !node.appendChild) { 1520 node = t.parentNode || _doc.body; 1521 style[(horiz ? "width" : "height")] = v + sfx; 1522 } else { 1523 style[(horiz ? "borderLeftWidth" : "borderTopWidth")] = v + sfx; 1524 } 1525 node.appendChild(_tempDiv); 1526 pix = parseFloat(_tempDiv[(horiz ? "offsetWidth" : "offsetHeight")]); 1527 node.removeChild(_tempDiv); 1528 if (pix === 0 && !recurse) { 1529 pix = _convertToPixels(t, p, v, sfx, true); 1530 } 1531 } 1532 return neg ? -pix : pix; 1533 }, 1534 _calculateOffset = function(t, p) { //for figuring out "top" or "left" in px when it's "auto". We need to factor in margin with the offsetLeft/offsetTop 1535 if (_getStyle(t, "position", true) !== "absolute") { return 0; } 1536 var dim = ((p === "left") ? "Left" : "Top"),
1537 v = _getStyle(t, "margin" + dim, true); 1538 return t["offset" + dim] - (_convertToPixels(t, p, parseFloat(v), (v + "").replace(_suffixExp, "")) || 0); 1539 }, 1540 1541 _getStyle = function(element, prop, keepUnits) { 1542 var rv = (element._gsTransform || {})[prop], 1543 cs; 1544 if (rv || rv === 0) { 1545 return rv; 1546 } else if (element.style[prop]) { 1547 rv = element.style[prop]; 1548 } else if ((cs = _getComputedStyle(element))) { 1549 rv = cs.getPropertyValue(prop.replace(/([A-Z])/g, "-$1").toLowerCase()); 1550 rv = (rv || cs.length) ? rv : cs[prop]; //Opera behaves VERY strangely - length is usually 0 and cs[prop] is the only way to get accurate results EXCEPT when checking for -o-transform which only works with cs.getPropertyValue()! 1551 } else if (element.currentStyle) { 1552 rv = element.currentStyle[prop]; 1553 } 1554 if (rv === "auto" && (prop === "top" || prop === "left")) { 1555 rv = _calculateOffset(element, prop); 1556 } 1557 return keepUnits ? rv : parseFloat(rv) || 0; 1558 }, 1559 1560 _dispatchEvent = function(instance, type, callbackName) { 1561 var vars = instance.vars, 1562 callback = vars[callbackName], 1563 listeners = instance._listeners[type]; 1564 if (typeof(callback) === "function") { 1565 callback.apply(vars[callbackName + "Scope"] || vars.callbackScope || instance, vars[callbackName + "Params"] || [instance.pointerEvent]); 1566 } 1567 if (listeners) { 1568 instance.dispatchEvent(type); 1569 } 1570 }, 1571 _getBounds = function(obj, context) { //accepts any of the following: a DOM element, jQuery object, selector text, or an object defining bounds as {top, left, width, height} or {minX, maxX, minY, maxY}. Returns an object with left, top, width, and height properties. 1572 var e = _unwrapElement(obj), 1573 top, left, offset; 1574 if (!e) { 1575 if (obj.left !== undefined) { 1576 offset = _getOffsetTransformOrigin(context); //the bounds should be relative to the origin 1577 return {left: obj.left - offset.x, top: obj.top - offset.y, width: obj.width, height: obj.height}; 1578 } 1579 left = obj.min || obj.minX || obj.minRotation || 0; 1580 top = obj.min || obj.minY || 0; 1581 return {left:left, top:top, width:(obj.max || obj.maxX || obj.maxRotation || 0) - left, height:(obj.max || obj.maxY || 0) - top}; 1582 } 1583 return _getElementBounds(e, context); 1584 }, 1585 1586 _svgBorderFactor, 1587 _svgBorderScales, 1588 _svgScrollOffset, 1589 _hasBorderBug, 1590 _hasReparentBug,//some browsers, like Chrome 49, alter the offsetTop/offsetLeft/offsetParent of elements when a non-identity transform is applied. 1591 _setEnvironmentVariables = function() { //some browsers factor the border into the SVG coordinate space, some don't (like Firefox). Some apply transforms to them, some don't. We feature-detect here so we know how to handle the border(s). We can't do this immediately - we must wait for the document.body to exist. 1592 if (!_doc.createElementNS) { 1593 _svgBorderFactor = 0; 1594 _svgBorderScales = false; 1595 return; 1596 } 1597 var div = _createElement("div"), 1598 svg = _doc.createElementNS("http://www.w3.org/2000/svg", "svg"), 1599 wrapper = _createElement("div"), 1600 style = div.style, 1601 parent = _doc.body || _docElement, 1602 isFlex = (_getStyle(parent, "display", true) === "flex"), //Firefox bug causes getScreenCTM() to return null when parent is display:flex and the element isn't rendered inside the window (like if it's below the scroll position) 1603 matrix, e1, point, oldValue; 1604 if (_doc.body && _transformProp) { 1605 style.position = "absolute"; 1606 parent.appendChild(wrapper); 1607 wrapper.appendChild(div); 1608 oldValue = div.offsetParent; 1609 wrapper.style[_transformProp] = "rotate(1deg)"; 1610 _hasReparentBug = (div.offsetParent === oldValue); 1611 wrapper.style.position = "absolute"; 1612 style.height = "10px"; 1613 oldValue = div.offsetTop; 1614 wrapper.style.border = "5px solid red"; 1615 _hasBorderBug = (oldValue !== div.offsetTop); //some browsers, like Firefox 38, cause the offsetTop/Left to be affected by a parent's border. 1616 parent.removeChild(wrapper); 1617 } 1618 style = svg.style; 1619 svg.setAttributeNS(null, "width", "400px"); 1620 svg.setAttributeNS(null, "height", "400px"); 1621 svg.setAttributeNS(null, "viewBox", "0 0 400 400"); 1622 style.display = "block"; 1623 style.boxSizing = "border-box"; 1624 style.border = "0px solid red"; 1625 style.transform = "none"; 1626 // in some browsers (like certain flavors of Android), the getScreenCTM() matrix is contaminated by the scroll position. We can run some logic here to detect that condition, but we ended up not needing this because we found another workaround using getBoundingClientRect(). 1627 div.style.cssText = "width:100px;height:100px;overflow:scroll;-ms-overflow-style:none;"; 1628 parent.appendChild(div); 1629 div.appendChild(svg); 1630 point = svg.createSVGPoint().matrixTransform(svg.getScreenCTM()); 1631 e1 = point.y; 1632 div.scrollTop = 100; 1633 point.x = point.y = 0; 1634 point = point.matrixTransform(svg.getScreenCTM()); 1635 _svgScrollOffset = (e1 - point.y < 100.1) ? 0 : e1 - point.y - 150; 1636 div.removeChild(svg); 1637 parent.removeChild(div); 1638 // -- end _svgScrollOffset calculation. 1639 parent.appendChild(svg); 1640 if (isFlex) { 1641 parent.style.display = "block"; //Firefox bug causes getScreenCTM() to return null when parent is display:flex and the element isn't rendered inside the window (like if it's below the scroll position) 1642 } 1643 matrix = svg.getScreenCTM(); 1644 e1 = matrix.e; 1645 style.border = "50px solid red"; 1646 matrix = svg.getScreenCTM(); 1647 if (e1 === 0 && matrix.e === 0 && matrix.f === 0 && matrix.a === 1) { //Opera has a bunch of bugs - it doesn't adjust the x/y of the matrix, nor does it scale when box-sizing is border-box but it does so elsewhere; to get the correct behavior we set _svgBorderScales to true. 1648 _svgBorderFactor = 1; 1649 _svgBorderScales = true; 1650 } else { 1651 _svgBorderFactor = (e1 !== matrix.e) ? 1 : 0; 1652 _svgBorderScales = (matrix.a !== 1); 1653 } 1654 if (isFlex) { 1655 parent.style.display = "flex"; 1656 } 1657 parent.removeChild(svg); 1658 }, 1659 1660 _supports3D = (_checkPrefix(_tempDiv, "perspective") !== ""), 1661 1662 // start matrix and point conversion methods... 1663 _transformOriginProp = _checkPrefix(_tempDiv, "transformOrigin").replace(/^ms/g, "Ms").replace(/([A-Z])/g, "-$1").toLowerCase(), 1664 _transformProp = _checkPrefix(_tempDiv, "transform"), 1665 _transformPropCSS = _transformProp.replace(/^ms/g, "Ms").replace(/([A-Z])/g, "-$1").toLowerCase(), 1666 _point1 = {}, //we reuse _point1 and _point2 objects inside matrix and point conversion methods to conserve memory and minimize garbage collection tasks. 1667 _point2 = {}, 1668 _SVGElement = _gsScope.SVGElement, 1669 _isSVG = function(e) { 1670 return !!(_SVGElement && typeof(e.getBBox) === "function" && e.getCTM && (!e.parentNode || (e.parentNode.getBBox && e.parentNode.getCTM))); 1671 }, 1672 _isIE10orBelow = (((/MSIE ([0-9]{1,}[\.0-9]{0,})/).exec(navigator.userAgent) || (/Trident\/.*rv:([0-9]{1,}[\.0-9]{0,})/).exec(navigator.userAgent)) && parseFloat( RegExp.$1 ) < 11), //Ideally we'd avoid user agent sniffing, but there doesn't seem to be a way to feature-detect and sense a border-related bug that only affects IE10 and IE9. 1673 _tempTransforms = [], 1674 _tempElements = [], 1675 _getSVGOffsets = function(e) { //SVG elements don't always report offsetTop/offsetLeft/offsetParent at all (I'm looking at you, Firefox 29 and Android), so we have to do some work to manufacture those values. You can pass any SVG element and it'll spit back an object with offsetTop, offsetLeft, offsetParent, scaleX, and scaleY properties. We need the scaleX and scaleY to handle the way SVG can resize itself based on the container. 1676 if (!e.getBoundingClientRect || !e.parentNode || !_transformProp) { 1677 return {offsetTop:0, offsetLeft:0, scaleX:1, scaleY:1, offsetParent:_docElement}; 1678 } 1679 if (Draggable.cacheSVGData !== false && e._dCache && e._dCache.lastUpdate === TweenLite.ticker.frame) { //performance optimization. Assume that if the offsets are requested again on the same tick, we can just feed back the values we already calculated (no need to keep recalculating until another tick elapses). 1680 return e._dCache; 1681 } 1682 var curElement = e, 1683 cache = _cache(e), 1684 eRect, parentRect, offsetParent, cs, m, i, point1, point2, borderWidth, borderHeight, width, height; 1685 cache.lastUpdate = TweenLite.ticker.frame; 1686 if (e.getBBox && !cache.isSVGRoot) { //if it's a nested/child SVG element, we must find the parent SVG canvas and measure the offset from there. 1687 curElement = e.parentNode; 1688 eRect = e.getBBox(); 1689 while (curElement && (curElement.nodeName + "").toLowerCase() !== "svg") { 1690 curElement = curElement.parentNode; 1691 } 1692 cs = _getSVGOffsets(curElement); 1693 cache.offsetTop = eRect.y * cs.scaleY; 1694 cache.offsetLeft = eRect.x * cs.scaleX; 1695 cache.scaleX = cs.scaleX; 1696 cache.scaleY = cs.scaleY; 1697 cache.offsetParent = curElement || _docElement; 1698 return cache; 1699 } 1700 //only root SVG elements continue here... 1701 offsetParent = cache.offsetParent; 1702 if (offsetParent === _doc.body) { 1703 offsetParent = _docElement; //avoids problems with margins/padding on the body 1704 } 1705 //walk up the ancestors and record any non-identity transforms (and reset them to "none") until we reach the offsetParent. We must do this so that the getBoundingClientRect() is accurate for measuring the offsetTop/offsetLeft. We'll revert the values later... 1706 _tempElements.length = _tempTransforms.length = 0; 1707 while (curElement) {
1708 m = _getStyle(curElement, _transformProp, true); 1709 if (m !== "matrix(1, 0, 0, 1, 0, 0)" && m !== "none" && m !== "translate3d(0px, 0px, 0px)") { 1710 _tempElements.push(curElement); 1711 _tempTransforms.push(curElement.style[_transformProp]); 1712 curElement.style[_transformProp] = "none"; 1713 } 1714 if (curElement === offsetParent) { 1715 break; 1716 } 1717 curElement = curElement.parentNode; 1718 } 1719 parentRect = offsetParent.getBoundingClientRect(); 1720 m = e.getScreenCTM(); 1721 point2 = e.createSVGPoint(); 1722 point1 = point2.matrixTransform(m); 1723 point2.x = point2.y = 10; 1724 point2 = point2.matrixTransform(m); 1725 cache.scaleX = (point2.x - point1.x) / 10; 1726 cache.scaleY = (point2.y - point1.y) / 10; 1727 if (_svgBorderFactor === undefined) { 1728 _setEnvironmentVariables(); 1729 } 1730 if (cache.borderBox && !_svgBorderScales && e.getAttribute("width")) { //some browsers (like Safari) don't properly scale the matrix to accommodate the border when box-sizing is border-box, so we must calculate it here... 1731 cs = _getComputedStyle(e) || {}; 1732 borderWidth = (parseFloat(cs.borderLeftWidth) + parseFloat(cs.borderRightWidth)) || 0; 1733 borderHeight = (parseFloat(cs.borderTopWidth) + parseFloat(cs.borderBottomWidth)) || 0; 1734 width = parseFloat(cs.width) || 0; 1735 height = parseFloat(cs.height) || 0; 1736 cache.scaleX *= (width - borderWidth) / width; 1737 cache.scaleY *= (height - borderHeight) / height; 1738 } 1739 if (_svgScrollOffset) { //some browsers (like Chrome for Android) have bugs in the way getScreenCTM() is reported (it doesn't factor in scroll position), so we must revert to a more expensive technique for calculating offsetTop/Left. 1740 eRect = e.getBoundingClientRect(); 1741 cache.offsetLeft = eRect.left - parentRect.left; 1742 cache.offsetTop = eRect.top - parentRect.top; 1743 } else { 1744 cache.offsetLeft = point1.x - parentRect.left; 1745 cache.offsetTop = point1.y - parentRect.top; 1746 } 1747 cache.offsetParent = offsetParent; 1748 i = _tempElements.length; 1749 while (--i > -1) { 1750 _tempElements[i].style[_transformProp] = _tempTransforms[i]; 1751 } 1752 return cache; 1753 }, 1754 _getOffsetTransformOrigin = function(e, decoratee) { //returns the x/y position of the transformOrigin of the element, in its own local coordinate system (pixels), offset from the top left corner. 1755 decoratee = decoratee || {}; 1756 if (!e || e === _docElement || !e.parentNode || e === window) { 1757 return {x:0, y:0}; 1758 } 1759 var cs = _getComputedStyle(e), 1760 v = (_transformOriginProp && cs) ? cs.getPropertyValue(_transformOriginProp) : "50% 50%", 1761 a = v.split(" "), 1762 x = (v.indexOf("left") !== -1) ? "0%" : (v.indexOf("right") !== -1) ? "100%" : a[0], 1763 y = (v.indexOf("top") !== -1) ? "0%" : (v.indexOf("bottom") !== -1) ? "100%" : a[1]; 1764 if (y === "center" || y == null) { 1765 y = "50%"; 1766 } 1767 if (x === "center" || isNaN(parseFloat(x))) { //remember, the user could flip-flop the values and say "bottom center" or "center bottom", etc. "center" is ambiguous because it could be used to describe horizontal or vertical, hence the isNaN(). If there's an "=" sign in the value, it's relative. 1768 x = "50%"; 1769 } 1770 if (e.getBBox && _isSVG(e)) { //SVG elements must be handled in a special way because their origins are calculated from the top left. 1771 if (!e._gsTransform) { 1772 TweenLite.set(e, {x:"+=0", overwrite:false}); //forces creation of the _gsTransform where we store all the transform components including xOrigin and yOrigin for SVG elements, as of GSAP 1.15.0 which also takes care of calculating the origin from the upper left corner of the SVG canvas. 1773 if (e._gsTransform.xOrigin === undefined) { 1774 console.log("Draggable requires at least GSAP 1.17.0"); 1775 } 1776 } 1777 v = e.getBBox(); 1778 decoratee.x = (e._gsTransform.xOrigin - v.x); 1779 decoratee.y = (e._gsTransform.yOrigin - v.y); 1780 } else { 1781 if (e.getBBox && (x + y).indexOf("%") !== -1) { //Firefox doesn't report offsetWidth/height on <svg> elements. 1782 e = e.getBBox(); 1783 e = {offsetWidth: e.width, offsetHeight: e.height}; 1784 } 1785 decoratee.x = ((x.indexOf("%") !== -1) ? e.offsetWidth * parseFloat(x) / 100 : parseFloat(x)); 1786 decoratee.y = ((y.indexOf("%") !== -1) ? e.offsetHeight * parseFloat(y) / 100 : parseFloat(y)); 1787 } 1788 return decoratee; 1789 }, 1790 _cache = function(e) { //computes some important values and stores them in a _dCache object attached to the element itself so that we can optimize performance 1791 if (Draggable.cacheSVGData !== false && e._dCache && e._dCache.lastUpdate === TweenLite.ticker.frame) { //performance optimization. Assume that if the offsets are requested again on the same tick, we can just feed back the values we already calculated (no need to keep recalculating until another tick elapses). 1792 return e._dCache; 1793 } 1794 var cache = e._dCache = e._dCache || {}, 1795 cs = _getComputedStyle(e), 1796 isSVG = (e.getBBox && _isSVG(e)), 1797 isSVGRoot = ((e.nodeName + "").toLowerCase() === "svg"), 1798 curSVG; 1799 cache.isSVG = isSVG; 1800 cache.isSVGRoot = isSVGRoot; 1801 cache.borderBox = (cs.boxSizing === "border-box"); 1802 cache.computedStyle = cs; 1803 if (isSVGRoot) { //some browsers don't report parentNode on SVG elements. 1804 curSVG = e.parentNode || _docElement; 1805 curSVG.insertBefore(_tempDiv, e); 1806 cache.offsetParent = _tempDiv.offsetParent || _docElement; //in some cases, Firefox still reports offsetParent as null. 1807 curSVG.removeChild(_tempDiv); 1808 } else if (isSVG) { 1809 curSVG = e.parentNode; 1810 while (curSVG && (curSVG.nodeName + "").toLowerCase() !== "svg") { //offsetParent is always the SVG canvas for SVG elements. 1811 curSVG = curSVG.parentNode; 1812 } 1813 cache.offsetParent = curSVG; 1814 } else { 1815 cache.offsetParent = e.offsetParent; 1816 } 1817 return cache; 1818 }, 1819 _getOffset2DMatrix = function(e, offsetOrigin, parentOffsetOrigin, zeroOr
1819igin, isBase) { //"isBase" helps us discern context - it should only be true when the element is the base one (the one at which we're starting to walk up the chain). It only matters in cases when it's an <svg> element itself because that's a case when we don't apply scaling. 1820 if (e === window || !e || !e.style || !e.parentNode) { 1821 return [1,0,0,1,0,0]; 1822 } 1823 var cache = e._dCache || _cache(e), 1824 parent = e.parentNode, 1825 parentCache = parent._dCache || _cache(parent), 1826 cs = cache.computedStyle, 1827 parentOffsetParent = cache.isSVG ? parentCache.offsetParent : parent.offsetParent, 1828 m, isRoot, offsets, rect, t, sx, sy, offsetX, offsetY, parentRect, borderTop, borderLeft, borderTranslateX, borderTranslateY; 1829 m = (cache.isSVG && (e.style[_transformProp] + "").indexOf("matrix") !== -1) ? e.style[_transformProp] : cs ? cs.getPropertyValue(_transformPropCSS) : e.currentStyle ? e.currentStyle[_transformProp] : "1,0,0,1,0,0"; //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 1830 if (e.getBBox && (e.getAttribute("transform") + "").indexOf("matrix") !== -1) { //SVG can store transform data in its "transform" attribute instead of the CSS, so look for that here (only accept matrix()). 1831 m = e.getAttribute("transform"); 1832 } 1833 m = (m + "").match(/(?:\-|\.|\b)(\d|\.|e\-)+/g) || [1,0,0,1,0,0]; 1834 if (m.length > 6) { 1835 m = [m[0], m[1], m[4], m[5], m[12], m[13]]; 1836 } 1837 if (zeroOrigin) { 1838 m[4] = m[5] = 0; 1839 } else if (cache.isSVG && (t = e._gsTransform) && (t.xOrigin || t.yOrigin)) { 1840 //SVGs handle origin very differently. Factor in GSAP's handling of origin values here: 1841 m[0] = parseFloat(m[0]); 1842 m[1] = parseFloat(m[1]); 1843 m[2] = parseFloat(m[2]); 1844 m[3] = parseFloat(m[3]); 1845 m[4] = parseFloat(m[4]) - (t.xOrigin - (t.xOrigin * m[0] + t.yOrigin * m[2])); 1846 m[5] = parseFloat(m[5]) - (t.yOrigin - (t.xOrigin * m[1] + t.yOrigin * m[3])); 1847 } 1848 if (offsetOrigin) { 1849 if (_svgBorderFactor === undefined) { 1850 _setEnvironmentVariables(); 1851 } 1852 offsets = (cache.isSVG || cache.isSVGRoot) ? _getSVGOffsets(e) : e; 1853 if (cache.isSVG) { //don't just rely on "instanceof _SVGElement" because if the SVG is embedded via an object tag, it won't work (SVGElement is mapped to a different object)) 1854 rect = e.getBBox(); 1855 parentRect = (parentCache.isSVGRoot) ? {x:0, y:0} : parent.getBBox(); 1856 offsets = {offsetLeft:rect.x - parentRect.x, offsetTop:rect.y - parentRect.y, offsetParent:cache.offsetParent}; 1857 } else if (cache.isSVGRoot) { 1858 borderTop = parseInt(cs.borderTopWidth, 10) || 0; 1859 borderLeft = parseInt(cs.borderLeftWidth, 10) || 0; 1860 borderTranslateX = ((m[0] - _svgBorderFactor) * borderLeft + m[2] * borderTop); 1861 borderTranslateY = (m[1] * borderLeft + (m[3] - _svgBorderFactor) * borderTop); 1862 1863 sx = offsetOrigin.x; 1864 sy = offsetOrigin.y; 1865 offsetX = (sx - (sx * m[0] + sy * m[2])); //accommodate the SVG root's transforms when the origin isn't in the top left. 1866 offsetY = (sy - (sx * m[1] + sy * m[3])); 1867 1868 m[4] = parseFloat(m[4]) + offsetX; 1869 m[5] = parseFloat(m[5]) + offsetY; 1870 offsetOrigin.x -= offsetX; 1871 offsetOrigin.y -= offsetY; 1872 sx = offsets.scaleX; 1873 sy = offsets.scaleY; 1874 if (!isBase) { //when getting the matrix for a root <svg> element itself (NOT in the context of an SVG element that's nested inside of it like a <path>), we do NOT apply the scaling! 1875 offsetOrigin.x *= sx; 1876 offsetOrigin.y *= sy; 1877 } 1878 m[0] *= sx; 1879 m[1] *= sy; 1880 m[2] *= sx; 1881 m[3] *= sy; 1882 if (!_isIE10orBelow) { 1883 offsetOrigin.x += borderTranslateX; 1884 offsetOrigin.y += borderTranslateY; 1885 } 1886 if (parentOffsetParent === _doc.body && offsets.offsetParent === _docElement) { //to avoid issues with margin/padding on the <body>, we always set the offsetParent to _docElement in the _getSVGOffsets() function but there's a condition we check later in this function for (parentOffsetParent === offsets.offsetParent) which w
1886ould fail if we don't run this logic. In other words, parentOffsetParent may be <body> and the <svg>'s offsetParent is also <body> but artificially set to _docElement to avoid margin/padding issues. 1887 parentOffsetParent = _docElement; 1888 } 1889 } else if (!_hasBorderBug && e.offsetParent) { 1890 offsetOrigin.x += parseInt(_getStyle(e.offsetParent, "borderLeftWidth"), 10) || 0; 1891 offsetOrigin.y += parseInt(_getStyle(e.offsetParent, "borderTopWidth"), 10) || 0; 1892 } 1893 isRoot = (parent === _docElement || parent === _doc.body); 1894 m[4] = Number(m[4]) + offsetOrigin.x + (offsets.offsetLeft || 0) - parentOffsetOrigin.x - (isRoot ? 0 : parent.scrollLeft || 0); 1895 m[5] = Number(m[5]) + offsetOrigin.y + (offsets.offsetTop || 0) - parentOffsetOrigin.y - (isRoot ? 0 : parent.scrollTop || 0); 1896 if (parent && _getStyle(e, "position", cs) === "fixed") { //fixed position elements should factor in the scroll position of the document. 1897 m[4] += _getDocScrollLeft(); 1898 m[5] += _getDocScrollTop(); 1899 } 1900 if (parent && parent !== _docElement && parentOffsetParent === offsets.offsetParent && !parentCache.isSVG && (!_hasReparentBug || _getOffset2DMatrix(parent).join("") === "100100")) { 1901 offsets = (parentCache.isSVGRoot) ? _getSVGOffsets(parent) : parent; 1902 m[4] -= offsets.offsetLeft || 0; 1903 m[5] -= offsets.offsetTop || 0; 1904 if (!_hasBorderBug && parentCache.offsetParent && !cache.isSVG && !cache.isSVGRoot) { 1905 m[4] -= parseInt(_getStyle(parentCache.offsetParent, "borderLeftWidth"), 10) || 0; 1906 m[5] -= parseInt(_getStyle(parentCache.offsetParent, "borderTopWidth"), 10) || 0; 1907 } 1908 } 1909 } 1910 return m; 1911 }, 1912 _getConcatenatedMatrix = function(e, invert) { 1913 if (!e || e === window || !e.parentNode) { 1914 return [1,0,0,1,0,0]; 1915 } 1916 //note: we keep reusing _point1 and _point2 in order to minimize memory usage and garbage collection chores. 1917 var originOffset = _getOffsetTransformOrigin(e, _point1), 1918 parentOriginOffset = _getOffsetTransformOrigin(e.parentNode, _point2), 1919 m = _getOffset2DMatrix(e, originOffset, parentOriginOffset, false, !invert), 1920 a, b, c, d, tx, ty, m2, determinant; 1921 while ((e = e.parentNode) && e.parentNode && e !== _docElement) { 1922 originOffset = parentOriginOffset; 1923 parentOriginOffset = _getOffsetTransformOrigin(e.parentNode, (originOffset === _point1) ? _point2 : _point1); 1924 m2 = _getOffset2DMatrix(e, originOffset, parentOriginOffset); 1925 a = m[0]; 1926 b = m[1]; 1927 c = m[2]; 1928 d = m[3]; 1929 tx = m[4]; 1930 ty = m[5]; 1931 m[0] = a * m2[0] + b * m2[2]; 1932 m[1] = a * m2[1] + b * m2[3]; 1933 m[2] = c * m2[0] + d * m2[2]; 1934 m[3] = c * m2[1] + d * m2[3]; 1935 m[4] = tx * m2[0] + ty * m2[2] + m2[4]; 1936 m[5] = tx * m2[1] + ty * m2[3] + m2[5]; 1937 } 1938 if (invert) { 1939 a = m[0]; 1940 b = m[1]; 1941 c = m[2]; 1942 d = m[3]; 1943 tx = m[4]; 1944 ty = m[5]; 1945 determinant = (a * d - b * c); 1946 m[0] = d / determinant; 1947 m[1] = -b / determinant; 1948 m[2] = -c / determinant; 1949 m[3] = a / determinant; 1950 m[4] = (c * ty - d * tx) / determinant; 1951 m[5] = -(a * ty - b * tx) / determinant; 1952 } 1953 return m; 1954 }, 1955 _localToGlobal = function(e, p, fromTopLeft, decoratee, zeroOrigin) { 1956 e = _unwrapElement(e); 1957 var m = _getConcatenatedMatrix(e, false, zeroOrigin), 1958 x = p.x, 1959 y = p.y; 1960 if (fromTopLeft) { 1961 _getOffsetTransformOrigin(e, p); 1962 x -= p.x; 1963 y -= p.y; 1964 } 1965 decoratee = (decoratee === true) ? p : decoratee || {}; 1966 decoratee.x = x * m[0] + y * m[2] + m[4]; 1967 decoratee.y = x * m[1] + y * m[3] + m[5]; 1968 return decoratee; 1969 }, 1970 _localizePoint = function(p, localToGlobal, globalToLocal) { 1971 var x = p.x * localToGlobal[0] + p.y * localToGlobal[2] + localToGlobal[4], 1972 y = p.x * localToGlobal[1] + p.y * localToGlobal[3] + localToGlobal[5]; 1973 p.x = x * globalToLocal[0] + y * globalToLocal[2] + globalToLocal[4]; 1974 p.y = x * globalToLocal[1] + y * globalToLocal[3] + globalToLocal[5]; 1975 return p; 1976 }, 1977 1978 _getElementBounds = function(e, context, fromTopLeft) { 1979 if (!(e = _unwrapElement(e))) { 1980 return null; 1981 } 1982 context = _unwrapElement(context); 1983 var isSVG = (e.getBBox && _isSVG(e)), 1984 origin, left, right, top, bottom, mLocalToGlobal, mGlobalToLocal, p1, p2, p3, p4, bbox, width, height, cache, borderLeft, borderTop, viewB
1984ox, viewBoxX, viewBoxY, computedDimensions, cs; 1985 if (e === window) { 1986 top = _getDocScrollTop(); 1987 left = _getDocScrollLeft(); 1988 right = left + (_docElement.clientWidth || e.innerWidth || _doc.body.clientWidth || 0); 1989 bottom = top + (((e.innerHeight || 0) - 20 < _docElement.clientHeight) ? _docElement.clientHeight : e.innerHeight || _doc.body.clientHeight || 0); //some browsers (like Firefox) ignore absolutely positioned elements, and collapse the height of the documentElement, so it could be 8px, for example, if you have just an absolutely positioned div. In that case, we use the innerHeight to resolve this. 1990 } else if (context === undefined || context === window) { 1991 return e.getBoundingClientRect(); 1992 } else { 1993 origin = _getOffsetTransformOrigin(e); 1994 left = -origin.x; 1995 top = -origin.y; 1996 if (isSVG) { 1997 bbox = e.getBBox(); 1998 width = bbox.width; 1999 height = bbox.height; 2000 } else if ((e.nodeName + "").toLowerCase() !== "svg" && e.offsetWidth) { //Chrome dropped support for "offsetWidth" on SVG elements 2001 width = e.offsetWidth; 2002 height = e.offsetHeight; 2003 } else { 2004 computedDimensions = _getComputedStyle(e); 2005 width = parseFloat(computedDimensions.width); 2006 height = parseFloat(computedDimensions.height); 2007 } 2008 right = left + width; 2009 bottom = top + height; 2010 if (e.nodeName.toLowerCase() === "svg" && !_isOldIE) { //root SVG elements are a special beast because they have 2 types of scaling - transforms on themselves as well as the stretching of the SVG canvas itself based on the outer size and the viewBox. If, for example, the SVG's viewbox is "0 0 100 100" but the CSS is set to width:200px; height:200px, that'd make it appear at 2x scale even though the element itself has no CSS transforms but the offsetWidth/offsetHeight are based on that css, not the viewBox so we need to adjust them accordingly. 2011 cache = _getSVGOffsets(e); 2012 cs = cache.computedStyle || {}; 2013 viewBox = (e.getAttribute("viewBox") || "0 0").split(" "); 2014 viewBoxX = parseFloat(viewBox[0]); 2015 viewBoxY = parseFloat(viewBox[1]); 2016 borderLeft = parseFloat(cs.borderLeftWidth) || 0; 2017 borderTop = parseFloat(cs.borderTopWidth) || 0; 2018 right -= width - ((width - borderLeft) / cache.scaleX) - viewBoxX; 2019 bottom -= height - ((height - borderTop) / cache.scaleY) - viewBoxY; 2020 left -= borderLeft / cache.scaleX - viewBoxX; 2021 top -= borderTop / cache.scaleY - viewBoxY; 2022 if (computedDimensions) { //when we had to use computed styles, factor in the border now. 2023 right += (parseFloat(cs.borderRightWidth) + borderLeft) / cache.scaleX; 2024 bottom += (borderTop + parseFloat(cs.borderBottomWidth)) / cache.scaleY; 2025 } 2026 } 2027 } 2028 if (e === context) { 2029 return {left:left, top:top, width: right - left, height: bottom - top}; 2030 } 2031 mLocalToGlobal = _getConcatenatedMatrix(e); 2032 mGlobalToLocal = _getConcatenatedMatrix(context, true); 2033 p1 = _localizePoint({x:left, y:top}, mLocalToGlobal, mGlobalToLocal); 2034 p2 = _localizePoint({x:right, y:top}, mLocalToGlobal, mGlobalToLocal); 2035 p3 = _localizePoint({x:right, y:bottom}, mLocalToGlobal, mGlobalToLocal); 2036 p4 = _localizePoint({x:left, y:bottom}, mLocalToGlobal, mGlobalToLocal); 2037 left = Math.min(p1.x, p2.x, p3.x, p4.x); 2038 top = Math.min(p1.y, p2.y, p3.y, p4.y); 2039 _temp1.x = _temp1.y = 0; 2040 if (fromTopLeft) { 2041 _getOffsetTransformOrigin(context, _temp1); 2042 } 2043 return {left:left + _temp1.x, top:top + _temp1.y, width:Math.max(p1.x, p2.x, p3.x, p4.x) - left, height:Math.max(p1.y, p2.y, p3.y, p4.y) - top}; 2044 }, 2045 // end matrix and point conversion methods 2046 2047 2048 2049 _isArrayLike = function(e) { 2050 return (e && 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. 2051 }, 2052 2053 _flattenArray = function(a) { 2054 var result = [], 2055 l = a.length, 2056 i, e, j; 2057 for (i = 0; i < l; i++) { 2058 e = a[i]; 2059 if (_isArrayLike(e)) { 2060 j = e.length; 2061 for (j = 0; j < e.length; j++) { 2062 result.push(e[j]); 2063 } 2064 } else if (e && e.length !== 0) { 2065 result.push(e); 2066 } 2067 } 2068 return result; 2069 }, 2070 2071 _isTouchDevice = (("ontouchstart" in _docElement) && ("orientation" in window)), 2072 _touchEventLookup = (function(types) { //we create an object that makes it easy to translate touch event types into their "pointer" counterparts if we're in a browser that uses those instead. Like IE10 uses "MSPointerDown" instead of "touchstart", for example. 2073 var standard = types.split(","), 2074 converted = ((_tempDiv.onpointerdown !== undefined) ? "pointerdown,pointermove,pointerup,pointercancel" : (_tempDiv.onmspointerdown !== undefined) ? "MSPointerDown,MSPointerMove,MSPointerUp,MSPointerCancel" : types).split(","), 2075 obj = {}, 2076 i = 4; 2077 while (--i > -1) { 2078 obj[standard[i]] = converted[i]; 2079 obj[converted[i]] = standard[i]; 2080 } 2081 return obj; 2082 }("touchstart,touchmove,touchend,touchcancel")), 2083 2084 _addListener = function(element, type, func, capture) { 2085 if (element.addEventListener) { 2086 element.addEventListener(_touchEventLookup[type], func, capture); 2087 if (type !== _touchEventLookup[type]) { //some browsers actually support both, so must we. 2088 element.addEventListener(type, func, capture); 2089 } 2090 } else if (element.attachEvent) { 2091 element.attachEvent("on" + type, func); 2092 } 2093 }, 2094 2095 _removeListener = function(element, type, func) { 2096 if (element.removeEventListener) { 2097 element.removeEventListener(_touchEventLookup[type], func); 2098 if (type !== _touchEventLookup[type]) { 2099 element.removeEventListener(type, func); 2100 } 2101 } else if (element.detachEvent) { 2102 element.detachEvent("on" + type, func); 2103 } 2104 }, 2105 2106 _hasTouchID = function(list, ID) { 2107 var i = list.length; 2108 while (--i > -1) { 2109 if (list[i].identifier === ID) { 2110 return true; 2111 } 2112 } 2113 return false;
2114 }, 2115 2116 _onMultiTouchDocumentEnd = function(e) { 2117 _isMultiTouching = (e.touches && _dragCount < e.touches.length); 2118 _removeListener(e.target, "touchend", _onMultiTouchDocumentEnd); 2119 }, 2120 2121 _onMultiTouchDocument = function(e) { 2122 _isMultiTouching = (e.touches && _dragCount < e.touches.length); 2123 _addListener(e.target, "touchend", _onMultiTouchDocumentEnd); 2124 }, 2125 2126 _parseThrowProps = function(draggable, snap, max, min, factor, forceZeroVelocity) { 2127 var vars = {}, 2128 a, i, l; 2129 if (snap) { 2130 if (factor !== 1 && snap instanceof Array) { //some data must be altered to make sense, like if the user passes in an array of rotational values in degrees, we must convert it to radians. Or for scrollLeft and scrollTop, we invert the values. 2131 vars.end = a = []; 2132 l = snap.length; 2133 if (typeof(snap[0]) === "object") { //if the array is populated with objects, like points ({x:100, y:200}), make copies before multiplying by the factor, otherwise we'll mess up the originals and the user may reuse it elsewhere. 2134 for (i = 0; i < l; i++) { 2135 a[i] = _copy(snap[i], factor); 2136 } 2137 } else { 2138 for (i = 0; i < l; i++) { 2139 a[i] = snap[i] * factor; 2140 } 2141 } 2142 max += 1.1; //allow 1.1 pixels of wiggle room when snapping in order to work around some browser inconsistencies in the way bounds are reported which can make them roughly a pixel off. For example, if "snap:[-$('#menu').width(), 0]" was defined and #menu had a wrapper that was used as the bounds, some browsers would be one pixel off, making the minimum -752 for example when snap was [-753,0], thus instead of snapping to -753, it would snap to 0 since -753 was below the minimum. 2143 min -= 1.1; 2144 } else if (typeof(snap) === "function") { 2145 vars.end = function(value) { 2146 var result = snap.call(draggable, value), 2147 copy, p; 2148 if (factor !== 1) { 2149 if (typeof(result) === "object") { 2150 copy = {}; 2151 for (p in result) { 2152 copy[p] = result[p] * factor; 2153 } 2154 result = copy; 2155 } else { 2156 result *= factor; 2157 } 2158 } 2159 return result; //we need to ensure that we can scope the function call to the Draggable instance itself so that users can access important values like maxX, minX, maxY, minY, x, and
2159y from within that function. 2160 }; 2161 } else { 2162 vars.end = snap; 2163 } 2164 } 2165 if (max || max === 0) { 2166 vars.max = max; 2167 } 2168 if (min || min === 0) { 2169 vars.min = min; 2170 } 2171 if (forceZeroVelocity) { 2172 vars.velocity = 0; 2173 } 2174 return vars; 2175 }, 2176 2177 _isClickable = function(e) { //sometimes it's convenient to mark an element as clickable by adding a data-clickable="true" attribute (in which case we won't preventDefault() the mouse/touch event). This method checks if the element is an <a>, <input>, or <button> or has an onclick or has the data-clickable or contentEditable attribute set to true (or any of its parent elements). 2178 var data; 2179 return (!e || !e.getAttribute || e.nodeName === "BODY") ? false : ((data = e.getAttribute("data-clickable")) === "true" || (data !== "false" && (e.onclick || _clickableTagExp.test(e.nodeName + "") || e.getAttribute("contentEditable") === "true"))) ? true : _isClickable(e.parentNode); 2180 }, 2181 2182 _setSelectable = function(elements, selectable) { 2183 var i = elements.length, 2184 e; 2185 while (--i > -1) { 2186 e = elements[i]; 2187 e.ondragstart = e.onselectstart = selectable ? null : _emptyFunc; 2188 _setStyle(e, "userSelect", (selectable ? "text" : "none")); 2189 } 2190 }, 2191 2192 _addPaddingBR, 2193 _addPaddingLeft = (function() { //this function is in charge of analyzing browser behavior related to padding. It sets the _addPaddingBR to true if the browser doesn't normally factor in the bottom or right padding on the element inside the scrolling area, and it sets _addPaddingLeft to true if it's a browser that requires the extra offset (offsetLeft) to be added to the paddingRight (like Opera). 2194 var div = _doc.createElement("div"), 2195 child = _doc.createElement("div"), 2196 childStyle = child.style, 2197 parent = _doc.body || _tempDiv, 2198 val; 2199 childStyle.display = "inline-block"; 2200 childStyle.position = "relative"; 2201 div.style.cssText = child.innerHTML = "width:90px; height:40px; padding:10px; overflow:auto; visibility: hidden"; 2202 div.appendChild(child); 2203 parent.appendChild(div); 2204 _addPaddingBR = (child.offsetHeight + 18 > div.scrollHeight); //div.scrollHeight should be child.offsetHeight + 20 because of the 10px of padding on each side, but some browsers ignore one side. We allow a 2px margin of error. 2205 childStyle.width = "100%"; 2206 if (!_transformProp) { 2207 childStyle.paddingRight = "500px"; 2208 val = div.scrollLeft = div.scrollWidth - div.clientWidth; 2209 childStyle.left = "-90px"; 2210 val = (val !== div.scrollLeft); 2211 } 2212 parent.removeChild(div); 2213 return val; 2214 }()), 2215 2216 2217 2218 2219 //The ScrollProxy class wraps an element's contents into another div (we call it "content") that we either add padding when necessary or apply a translate3d() transform in order to overscroll (scroll past the boundaries). This allows us to simply set the scrollTop/scrollLeft (or top/left for easier reverse-axis orientation, which is what we do in Draggable) and it'll do all the work for us. For example, if we tried setting scrollTop to -100 on a normal DOM element, it wouldn't work - it'd look the same as setting it to 0, but if we set scrollTop of a ScrollProxy to -100, it'll give the correct appearance by either setting paddingTop of the wrapper to 100 or applying a 100-pixel translateY. 2220 ScrollProxy = function(element, vars) { 2221 element = _unwrapElement(element); 2222 vars = vars || {}; 2223 var content = _doc.createElement("div"), 2224 style = content.style, 2225 node = element.firstChild, 2226 offsetTop = 0, 2227 offsetLeft = 0, 2228 prevTop = element.scrollTop, 2229 prevLeft = element.scrollLeft, 2230 scrollWidth = element.scrollWidth, 2231 scrollHeight = element.scrollHeight, 2232 extraPadRight = 0, 2233 maxLeft = 0, 2234 maxTop = 0, 2235 elementWidth, elementHeight, contentHeight, nextNode, transformStart, transformEnd; 2236 2237 if (_supports3D && vars.force3D !== false) { 2238 transformStart = "translate3d("; 2239 transformEnd = "px,0px)"; 2240 } else if (_transformProp) { 2241 transformStart = "translate("; 2242 transformEnd = "px)"; 2243 } 2244 2245 this.scrollTop = function(value, force) { 2246 if (!arguments.length) { 2247 return -this.top(); 2248 } 2249 this.top(-value, force); 2250 }; 2251 2252 this.scrollLeft = function(value, force) { 2253 if (!arguments.length) { 2254 return -this.left(); 2255 } 2256 this.left(-value, force); 2257 }; 2258 2259 this.left = function(value, force) { 2260 if (!arguments.length) { 2261 return -(element.scrollLeft + offsetLeft); 2262 } 2263 var dif = element.scrollLeft - prevLeft, 2264 oldOffset = offsetLeft; 2265 if ((dif > 2 || dif < -2) && !force) { //if the user interacts with the scrollbar (or something else scrolls it, like the mouse wheel), we should kill any tweens of the ScrollProxy. 2266 prevLeft = element.scrollLeft; 2267 TweenLite.killTweensOf(this, true, {left:1, scrollLeft:1}); 2268 this.left(-prevLeft); 2269 if (vars.onKill) { 2270 vars.onKill(); 2271 } 2272 return; 2273 } 2274 value = -value; //invert because scrolling works in the opposite direction 2275 if (value < 0) { 2276 offsetLeft = (value - 0.5) | 0; 2277 value = 0; 2278 } else if (value > maxLeft) { 2279 offsetLeft = (value - maxLeft) | 0; 2280 value = maxLeft; 2281 } else { 2282 offsetLeft = 0; 2283 } 2284 if (offsetLeft || oldOffset) { 2285 if (transformStart) { 2286 if (!this._suspendTransforms) { 2287 style[_transformProp] = transformStart + -offsetLeft + "px," + -offsetTop + transformEnd; 2288 } 2289 } else { 2290 style.left = -offsetLeft + "px"; 2291 } 2292 if (_addPaddingLeft && offsetLeft + extraPadRight >= 0) { 2293 style.paddingRight = offsetLeft + extraPadRight + "px"; 2294 } 2295 } 2296 element.scrollLeft = value | 0; 2297 prevLeft = element.scrollLeft; //don't merge this with the line above because some browsers adjsut the scrollLeft after it's set, so in order to be 100% accurate in tracking it, we need to ask the browser to report it. 2298 }; 2299 2300 this.top = function(value, force) { 2301 if (!arguments.length) { 2302 return -(element.scrollTop + offsetTop); 2303 } 2304 var dif = element.scrollTop - prevTop, 2305 oldOffset = offsetTop; 2306 if ((dif > 2 || dif < -2) && !force) { //if the user interacts with the scrollbar (or something else scrolls it, like the mouse wheel), we should kill any tweens of the ScrollProxy. 2307 prevTop = element.scrollTop; 2308 TweenLite.killTweensOf(this, true, {top:1, scrollTop:1}); 2309 this.top(-prevTop); 2310 if (vars.onKill) { 2311 vars.onKill(); 2312 } 2313 return; 2314 } 2315 value = -value; //invert because scrolling works in the opposite direction 2316 if (value < 0) { 2317 offsetTop = (value - 0.5) | 0; 2318 value = 0; 2319 } else if (value > maxTop) { 2320 offsetTop = (value - maxTop) | 0; 2321 value = maxTop; 2322 } else { 2323 offsetTop = 0; 2324 } 2325 if (offsetTop || oldOffset) { 2326 if (transformStart) { 2327 if (!this._suspendTransforms) { 2328 style[_transformProp] = transformStart + -offsetLeft + "px," + -offsetTop + transformEnd; 2329 } 2330 } else { 2331 style.top = -offsetTop + "px"; 2332 } 2333 } 2334 element.scrollTop = value | 0; 2335 prevTop = element.scrollTop; 2336 }; 2337 2338 this.maxScrollTop = function() { 2339 return maxTop; 2340 }; 2341 2342 this.maxScrollLeft = function() { 2343 return maxLeft; 2344 }; 2345 2346 this.disable = function() { 2347 node = content.firstChild; 2348 while (node) { 2349 nextNode = node.nextSibling; 2350 element.appendChild(node); 2351 node = nextNode; 2352 } 2353 if (element === content.parentNode) { //in case disable() is called when it's already disabled. 2354 element.removeChild(content); 2355 } 2356 }; 2357 2358 this.enable = function() { 2359 node = element.firstChild; 2360 if (node === content) { 2361 return; 2362 } 2363 while (node) { 2364 nextNode = node.nextSibling; 2365 content.appendChild(node); 2366 node = nextNode; 2367 } 2368 element.appendChild(content); 2369 this.calibrate(); 2370 }; 2371 2372 this.calibrate = function(force) { 2373 var widthMatches = (element.clientWidth === elementWidth), 2374 x, y; 2375 prevTop = element.scrollTop; 2376 prevLeft = element.scrollLeft; 2377 if (widthMatches && element.clientHeight === elementHeight && content.offsetHeight === contentHeight && scrollWidth === element.scrollWidth && scrollHeight === element.scrollHeight && !force) { 2378 return; //no need to recalculate things if the width and height haven't changed. 2379 } 2380 if (offsetTop || offsetLeft) { 2381 x = this.left(); 2382 y = this.top(); 2383 this.left(-element.scrollLeft); 2384 this.top(-element.scrollTop); 2385 } 2386 //first, we need to remove any width constraints to see how the content naturally flows so that we can see if it's wider than the containing element. If so, we've got to re
2386cord the amount of overage so that we can apply that as padding in order for browsers to correctly handle things. Then we switch back to a width of 100% (without that, some browsers don't flow the content correctly) 2387 if (!widthMatches || force) { 2388 style.display = "block"; 2389 style.width = "auto"; 2390 style.paddingRight = "0px"; 2391 extraPadRight = Math.max(0, element.scrollWidth - element.clientWidth); 2392 //if the content is wider than the container, we need to add the paddingLeft and paddingRight in order for things to behave correctly. 2393 if (extraPadRight) { 2394 extraPadRight += _getStyle(element, "paddingLeft") + (_addPaddingBR ? _getStyle(element, "paddingRight") : 0); 2395 } 2396 } 2397 style.display = "inline-block"; 2398 style.position = "relative"; 2399 style.overflow = "visible"; 2400 style.verticalAlign = "top"; 2401 style.width = "100%"; 2402 style.paddingRight = extraPadRight + "px"; 2403 //some browsers neglect to factor in the bottom padding when calculating the scrollHeight, so we need to add that padding to the content when that happens. Allow a 2px margin for error 2404 if (_addPaddingBR) { 2405 style.paddingBottom = _getStyle(element, "paddingBottom", true); 2406 } 2407 if (_isOldIE) { 2408 style.zoom = "1"; 2409 } 2410 elementWidth = element.clientWidth; 2411 elementHeight = element.clientHeight; 2412 scrollWidth = element.scrollWidth; 2413 scrollHeight = element.scrollHeight; 2414 maxLeft = element.scrollWidth - elementWidth; 2415 maxTop = element.scrollHeight - elementHeight; 2416 contentHeight = content.offsetHeight; 2417 style.display = "block"; 2418 if (x || y) { 2419 this.left(x); 2420 this.top(y); 2421 } 2422 }; 2423 2424 this.content = content; 2425 this.element = element; 2426 this._suspendTransforms = false; 2427 this.enable(); 2428 }, 2429 2430 2431 2432 2433 2434 Draggable = function(target, vars) { 2435 EventDispatcher.call(this, target); 2436 target = _unwrapElement(target); //in case the target is a selector object or selector text 2437 if (!ThrowPropsPlugin) { 2438 ThrowPropsPlugin = _globals.com.greensock.plugins.ThrowPropsPlugin; 2439 } 2440 this.vars = vars = _copy(vars || {}); 2441 this.target = target; 2442 this.x = this.y = this.rotation = 0; 2443 this.dragResistance = parseFloat(vars.dragResistance) || 0; 2444 this.edgeResistance = isNaN(vars.edgeResistance) ? 1 : parseFloat(vars.edgeResistance) || 0; 2445 this.lockAxis = vars.lockAxis; 2446 this.autoScroll = vars.autoScroll || 0; 2447 this.lockedAxis = null; 2448 this.allowEventDefault = !!vars.allowEventDefault; 2449 var type = (vars.type || (_isOldIE ? "top,left" : "x,y")).toLowerCase(), 2450 xyMode = (type.indexOf("x") !== -1 || type.indexOf("y") !== -1), 2451 rotationMode = (type.indexOf("rotation") !== -1), 2452 xProp = rotationMode ? "rotation" : xyMode ? "x" : "left", 2453 yProp = xyMode ? "y" : "top", 2454 allowX = (type.indexOf("x") !== -1 || type.indexOf("left") !== -1 || type === "scroll"), 2455 allowY = (type.indexOf("y") !== -1 || type.indexOf("top") !== -1 || type === "scroll"), 2456 minimumMovement = vars.minimumMovement || 2, 2457 self = this, 2458 triggers = _slice(vars.trigger || vars.handle || target), 2459 killProps = {}, 2460 dragEndTime = 0, 2461 checkAutoScrollBounds = false, 2462 autoScrollMarginTop = vars.autoScrollMarginTop || 40, 2463 autoScrollMarginRight = vars.autoScrollMarginRight || 40, 2464 autoScrollMarginBottom = vars.autoScrollMarginBottom || 40, 2465 autoScrollMarginLeft = vars.autoScrollMarginLeft || 40, 2466 isClickable = vars.clickableTest || _isClickable, 2467 clickTime = 0, 2468 enabled, scrollProxy, startPointerX, startPointerY, startElementX, startElementY, hasBounds, hasDragCallback, maxX, minX, maxY, minY, tempVars, cssVars, touch, touchID, rotationOrigin, dirty, old, snapX, snapY, snapXY, isClicking, touchEventTarget, matrix, interrupted, startScrollTop, startScrollLeft, applyObj, allowNativeTouchScrolling, touchDragAxis, isDispatching, clickDispatch, trustedClickDispatch, 2469 2470 onContextMenu = function(e) { //used to prevent long-touch from triggering a context menu. 2471 if (self.isPressed && e.which < 2) {
2472 self.endDrag(); 2473 } else { 2474 e.preventDefault(); 2475 e.stopPropagation(); 2476 return false; 2477 } 2478 }, 2479 2480 //this method gets called on every tick of TweenLite.ticker which allows us to synchronize the renders to the core engine (which is typically synchronized with the display refresh via requestAnimationFrame). This is an optimization - it's better than applying the values inside the "mousemove" or "touchmove" event handler which may get called many times inbetween refreshes. 2481 render = function(suppressEvents) { 2482 if (self.autoScroll && self.isDragging && (checkAutoScrollBounds || dirty)) { 2483 var e = target, 2484 autoScrollFactor = self.autoScroll * 15, //multiplying by 15 just gives us a better "feel" speed-wise. 2485 parent, isRoot, rect, pointerX, pointerY, changeX, changeY, gap; 2486 checkAutoScrollBounds = false; 2487 _windowProxy.scrollTop = ((window.pageYOffset != null) ? window.pageYOffset : (_docElement.scrollTop != null) ? _docElement.scrollTop : _doc.body.scrollTop); 2488 _windowProxy.scrollLeft = ((window.pageXOffset != null) ? window.pageXOffset : (_docElement.scrollLeft != null) ? _docElement.scrollLeft : _doc.body.scrollLeft); 2489 pointerX = self.pointerX - _windowProxy.scrollLeft; 2490 pointerY = self.pointerY - _windowProxy.scrollTop; 2491 while (e && !isRoot) { //walk up the chain and sense wherever the pointer is within 40px of an edge that's scrollable. 2492 isRoot = _isRoot(e.parentNode); 2493 parent = isRoot ? _windowProxy : e.parentNode; 2494 rect = isRoot ? {bottom:Math.max(_docElement.clientHeight, window.innerHeight || 0), right: Math.max(_docElement.clientWidth, window.innerWidth || 0), left:0, top:0} : parent.getBoundingClientRect(); 2495 changeX = changeY = 0; 2496 if (allowY) { 2497 gap = parent._gsMaxScrollY - parent.scrollTop; 2498 if (gap < 0) { 2499 changeY = gap; 2500 } else if (pointerY > rect.bottom - autoScrollMarginBottom && gap) { 2501 checkAutoScrollBounds = true; 2502 changeY = Math.min(gap, (autoScrollFactor * (1 - Math.max(0, (rect.bottom - pointerY)) / autoScrollMarginBottom)) | 0); 2503 } else if (pointerY < rect.top + autoScrollMarginTop && parent.scrollTop) { 2504 checkAutoScrollBounds = true; 2505 changeY = -Math.min(parent.scrollTop, (autoScrollFactor * (1 - Math.max(0, (pointerY - rect.top)) / autoScrollMarginTop)) | 0); 2506 } 2507 if (changeY) { 2508 parent.scrollTop += changeY; 2509 } 2510 } 2511 if (allowX) { 2512 gap = parent._gsMaxScrollX - parent.scrollLeft; 2513 if (gap < 0) { 2514 changeX = gap; 2515 } else if (pointerX > rect.right - autoScrollMarginRight && gap) { 2516 checkAutoScrollBounds = true; 2517 changeX = Math.min(gap, (autoScrollFactor * (1 - Math.max(0, (rect.right - pointerX)) / autoScrollMarginRight)) | 0); 2518 } else if (pointerX < rect.left + autoScrollMarginLeft && parent.scrollLeft) { 2519 checkAutoScrollBounds = true; 2520 changeX = -Math.min(parent.scrollLeft, (autoScrollFactor * (1 - Math.max(0, (pointerX - rect.left)) / autoScrollMarginLeft)) | 0); 2521 } 2522 if (changeX) { 2523 parent.scrollLeft += changeX; 2524 } 2525 } 2526 2527 if (isRoot && (changeX || changeY)) { 2528 window.scrollTo(parent.scrollLeft, parent.scrollTop); 2529 setPointerPosition(self.pointerX + changeX, self.pointerY + changeY); 2530 } 2531 e = parent; 2532 } 2533 } 2534 if (dirty) { 2535 var x = self.x, 2536 y = self.y, 2537 min = 0.000001; 2538 if (x < min && x > -min) { //browsers don't handle super small decimals well. 2539 x = 0; 2540 } 2541 if (y < min && y > -min) { 2542 y = 0; 2543 } 2544 if (rotationMode) { 2545 self.deltaX = x - applyObj.data.rotation; 2546 applyObj.data.rotation = self.rotation = x; 2547 applyObj.setRatio(1); //note: instead of doing TweenLite.set(), as a performance optimization we skip right to the method that renders the transforms inside CSSPlugin. For old versions of IE, though, we do a normal TweenLite.set() to leverage its ability to re-reroute to an IE-specific 2D renderer. 2548 } else { 2549 if (scrollProxy) { 2550 if (allowY) { 2551 self.deltaY = y - scrollProxy.top(); 2552 scrollProxy.top(y); 2553 } 2554 if (allowX) { 2555 self.deltaX = x - scrollProxy.left(); 2556 scrollProxy.left(x); 2557 } 2558 } else if (xyMode) { 2559 if (allowY) { 2560 self.deltaY = y - applyObj.data.y; 2561 applyObj.data.y = y; 2562 } 2563 if (allowX) { 2564 self.deltaX = x - applyObj.data.x; 2565 applyObj.data.x = x; 2566 } 2567 applyObj.setRatio(1); //note: instead of doing TweenLite.set(), as a performance optimization we skip right to the method that renders the transforms inside CSSPlugin. For old versions of IE, though, we do a normal TweenLite.set() to leverage its ability to re-reroute to an IE-specific 2D renderer. 2568 } else { 2569 if (allowY) { 2570 self.deltaY = y - parseFloat(target.style.top || 0); 2571 target.style.top = y + "px"; 2572 } 2573 if (allowX) { 2574 self.deltaY = x - parseFloat(target.style.left || 0); 2575 target.style.left = x + "px"; 2576 } 2577 } 2578 } 2579 if (hasDragCallback && !suppressEvents && !isDispatching) { 2580 isDispatching = true;
2580 //in case onDrag has an update() call (avoid endless loop) 2581 _dispatchEvent(self, "drag", "onDrag"); 2582 isDispatching = false; 2583 } 2584 } 2585 dirty = false; 2586 }, 2587 2588 //copies the x/y from the element (whether that be transforms, top/left, or ScrollProxy's top/left) to the Draggable's x and y (and rotation if necessary) properties so that they reflect reality and it also (optionally) applies any snapping necessary. This is used by the ThrowPropsPlugin tween in an onUpdate to ensure things are synced and snapped. 2589 syncXY = function(skipOnUpdate, skipSnap) { 2590 var x = self.x, 2591 y = self.y, 2592 snappedValue; 2593 if (!target._gsTransform && (xyMode || rotationMode)) { //just in case the _gsTransform got wiped, like if the user called clearProps on the transform or something (very rare), doing an x tween forces a re-parsing of the transforms and population of the _gsTransform. 2594 TweenLite.set(target, {x:"+=0", overwrite:false, data:"_draggable"}); 2595 } 2596 if (xyMode) { 2597 self.y = target._gsTransform.y; 2598 self.x = target._gsTransform.x; 2599 } else if (rotationMode) { 2600 self.x = self.rotation = target._gsTransform.rotation; 2601 } else if (scrollProxy) { 2602 self.y = scrollProxy.top(); 2603 self.x = scrollProxy.left(); 2604 } else { 2605 self.y = parseInt(target.style.top, 10) || 0; 2606 self.x = parseInt(target.style.left, 10) || 0; 2607 } 2608 if ((snapX || snapY || snapXY) && !skipSnap && (self.isDragging || self.isThrowing)) { 2609 if (snapXY) { 2610 _temp1.x = self.x; 2611 _temp1.y = self.y; 2612 snappedValue = snapXY(_temp1); 2613 if (snappedValue.x !== self.x) { 2614 self.x = snappedValue.x; 2615 dirty = true; 2616 } 2617 if (snappedValue.y !== self.y) { 2618 self.y = snappedValue.y; 2619 dirty = true; 2620 } 2621 } 2622 if (snapX) { 2623 snappedValue = snapX(self.x); 2624 if (snappedValue !== self.x) { 2625 self.x = snappedValue; 2626 if (rotationMode) { 2627 self.rotation = snappedValue; 2628 } 2629 dirty = true; 2630 } 2631 } 2632 if (snapY) { 2633 snappedValue = snapY(self.y); 2634 if (snappedValue !== self.y) { 2635 self.y = snappedValue; 2636 } 2637 dirty = true; 2638 } 2639 } 2640 if (dirty) { 2641 render(true); 2642 } 2643 if (!skipOnUpdate) { 2644 self.deltaX = self.x - x; 2645 self.deltaY = self.y - y; 2646 _dispatchEvent(self, "throwupdate", "onThrowUpdate"); 2647 } 2648 }, 2649 2650 calculateBounds = function() { 2651 var bounds, targetBounds, snap, snapIsRaw; 2652 hasBounds = false; 2653 if (scrollProxy) { 2654 scrollProxy.calibrate(); 2655 self.minX = minX = -scrollProxy.maxScrollLeft(); 2656 self.minY = minY = -scrollProxy.maxScrollTop(); 2657 self.maxX = maxX = self.maxY = maxY = 0; 2658 hasBounds = true; 2659 } else if (!!vars.bounds) { 2660 bounds = _getBounds(vars.bounds, target.parentNode); //could be a selector/jQuery object or a DOM element or a generic object like {top:0, left:100, width:1000, height:800} or {minX:100, maxX:1100, minY:0, maxY:800} 2661 if (rotationMode) { 2662 self.minX = minX = bounds.left; 2663 self.maxX = maxX = bounds.left + bounds.width; 2664 self.minY = minY = self.maxY = maxY = 0; 2665 } else if (vars.bounds.maxX !== undefined || vars.bounds.maxY !== undefined) { 2666 bounds = vars.bounds; 2667 self.minX = minX = bounds.minX; 2668 self.minY = minY = bounds.minY; 2669 self.maxX = maxX = bounds.maxX; 2670 self.maxY = maxY = bounds.maxY; 2671 } else { 2672 targetBounds = _getBounds(target, target.parentNode); 2673 self.minX = minX = _getStyle(target, xProp) + bounds.left - targetBounds.left; 2674 self.minY = minY = _getStyle(target, yProp) + bounds.top - targetBounds.top; 2675 self.maxX = maxX = minX + (bounds.width - targetBounds.width); 2676 self.maxY = maxY = minY + (bounds.height - targetBounds.height); 2677 } 2678 if (minX > maxX) { 2679 self.minX = maxX; 2680 self.maxX = maxX = minX; 2681 minX = self.minX; 2682 } 2683 if (minY > maxY) { 2684 self.minY = maxY; 2685 self.maxY = maxY = minY; 2686 minY = self.minY; 2687 } 2688 if (rotationMode) { 2689 self.minRotation = minX; 2690 self.maxRotation = maxX; 2691 } 2692 hasBounds = true; 2693 } 2694 if (vars.liveSnap) { 2695 snap = (vars.liveSnap === true) ? (vars.snap || {}) : vars.liveSnap; 2696 snapIsRaw = (snap instanceof Array || typeof(snap) === "function"); 2697 if (rotationMode) { 2698 snapX = buildSnapFunc((snapIsRaw ? snap : snap.rotation), minX, maxX, 1); 2699 snapY = null; 2700 } else { 2701 if (snap.points) { 2702 snapXY = buildPointSnapFunc((snapIsRaw ? snap : snap.points), minX, maxX, minY, maxY, snap.radius, scrollProxy ? -1 : 1); 2703 } else { 2704 if (allowX) { 2705 snapX = buildSnapFunc((snapIsRaw ? snap : snap.x || snap.left || snap.scrollLeft), minX, maxX, scrollProxy ? -1 : 1); 2706 } 2707 if (allowY) { 2708 snapY = buildSnapFunc((snapIsRaw ? snap : snap.y || snap.top || snap.scrollTop), minY, maxY, scrollProxy ? -1 : 1); 2709 } 2710 } 2711 } 2712 } 2713 2714 }, 2715 2716 onThrowComplete = function() { 2717 self.isThrowing = false;
2718 _dispatchEvent(self, "throwcomplete", "onThrowComplete"); 2719 }, 2720 onThrowOverwrite = function() { 2721 self.isThrowing = false; 2722 }, 2723 2724 animate = function(throwProps, forceZeroVelocity) { 2725 var snap, snapIsRaw, tween, overshootTolerance; 2726 if (throwProps && ThrowPropsPlugin) { 2727 if (throwProps === true) { 2728 snap = vars.snap || vars.liveSnap || {}; 2729 snapIsRaw = (snap instanceof Array || typeof(snap) === "function"); 2730 throwProps = {resistance:(vars.throwResistance || vars.resistance || 1000) / (rotationMode ? 10 : 1)}; 2731 if (rotationMode) { 2732 throwProps.rotation = _parseThrowProps(self, snapIsRaw ? snap : snap.rotation, maxX, minX, 1, forceZeroVelocity); 2733 } else { 2734 if (allowX) { 2735 throwProps[xProp] = _parseThrowProps(self, snapIsRaw ? snap : snap.points || snap.x || snap.left || snap.scrollLeft, maxX, minX, scrollProxy ? -1 : 1, forceZeroVelocity || (self.lockedAxis === "x")); 2736 } 2737 if (allowY) { 2738 throwProps[yProp] = _parseThrowProps(self, snapIsRaw ? snap : snap.points || snap.y || snap.top || snap.scrollTop, maxY, minY, scrollProxy ? -1 : 1, forceZeroVelocity || (self.lockedAxis === "y")); 2739 } 2740 if (snap.points || (snap instanceof Array && typeof(snap[0]) === "object")) { 2741 throwProps.linkedProps = xProp + "," + yProp; 2742 throwProps.radius = snap.radius; //note: we also disable liveSnapping while throwing if there's a "radius" defined, otherwise it looks weird to have the item thrown past a snapping point but live-snapping mid-tween. We do this by altering the onUpdateParams so that "skipSnap" parameter is true for syncXY. 2743 } 2744 } 2745 } 2746 self.isThrowing = true; 2747 overshootTolerance = (!isNaN(vars.overshootTolerance)) ? vars.overshootTolerance : (vars.edgeResistance === 1) ? 0 : (1 - self.edgeResistance) + 0.2; 2748 self.tween = tween = ThrowPropsPlugin.to(scrollProxy || target, {throwProps:throwProps, data:"_draggable", ease:(vars.ease || _globals.Power3.easeOut), onComplete:onThrowComplete, onOverwrite:onThrowOverwrite, onUpdate:(vars.fastMode ? _dispatchEvent : syncXY), onUp
2748dateParams:(vars.fastMode ? [self, "onthrowupdate", "onThrowUpdate"] : (snap && snap.radius) ? [false, true] : _emptyArray)}, (isNaN(vars.maxDuration) ? 2 : vars.maxDuration), (!isNaN(vars.minDuration) ? vars.minDuration : (overshootTolerance === 0 || (typeof(throwProps) === "object" && throwProps.resistance > 1000)) ? 0 : 0.5), overshootTolerance); 2749 if (!vars.fastMode) { 2750 //to populate the end values, we just scrub the tween to the end, record the values, and then jump back to the beginning. 2751 if (scrollProxy) { 2752 scrollProxy._suspendTransforms = true; //Microsoft browsers have a bug that causes them to briefly render the position incorrectly (it flashes to the end state when we seek() the tween even though we jump right back to the current position, and this only seems to happen when we're affecting both top and left), so we set a _suspendTransforms flag to prevent it from actually applying the values in the ScrollProxy. 2753 } 2754 tween.render(tween.duration(), true, true); 2755 syncXY(true, true); 2756 self.endX = self.x; 2757 self.endY = self.y; 2758 if (rotationMode) { 2759 self.endRotation = self.x; 2760 } 2761 tween.play(0); 2762 syncXY(true, true); 2763 if (scrollProxy) { 2764 scrollProxy._suspendTransforms = false; 2765 } 2766 } 2767 } else if (hasBounds) { 2768 self.applyBounds(); 2769 } 2770 }, 2771 2772 updateMatrix = function(shiftStart) { 2773 var start = matrix || [1,0,0,1,0,0], 2774 a, b, c, d, tx, ty, determinant, pointerX, pointerY; 2775 matrix = _getConcatenatedMatrix(target.parentNode, true); 2776 if (shiftStart && self.isPressed && start.join(",") !== matrix.join(",")) { //if the matrix changes WHILE the element is pressed, we must adjust the startPointerX and startPointerY accordingly, so we invert the original matrix and figure out where the pointerX and pointerY were in the global space, then apply the new matrix to get the updated coordinates. 2777 a = start[0]; 2778 b = start[1]; 2779 c = start[2]; 2780 d = start[3]; 2781 tx = start[4]; 2782 ty = start[5]; 2783 determinant = (a * d - b * c); 2784 pointerX = startPointerX * (d / determinant) + startPointerY * (-c / determinant) + ((c * ty - d * tx) / determinant); 2785 pointerY = startPointerX * (-b / determinant) + startPointerY * (a / determinant) + (-(a * ty - b * tx) / determinant); 2786 startPointerY = pointerX * matrix[1] + pointerY * matrix[3] + matrix[5]; 2787 startPointerX = pointerX * matrix[0] + pointerY * matrix[2] + matrix[4]; 2788 } 2789 if (!matrix[1] && !matrix[2] && matrix[0] == 1 && matrix[3] == 1 && matrix[4] == 0 && matrix[5] == 0) { //if there are no transforms, we can optimize performance by not factoring in the matrix 2790 matrix = null; 2791 } 2792 2793 }, 2794 2795 recordStartPositions = function() { 2796 var edgeTolerance = 1 - self.edgeResistance; 2797 updateMatrix(false); 2798 if (matrix) { 2799 startPointerX = self.pointerX * matrix[0] + self.pointerY * matrix[2] + matrix[4]; //translate to local coordinate system 2800 startPointerY = self.pointerX * matrix[1] + self.pointerY * matrix[3] + matrix[5]; 2801 } 2802 if (dirty) { 2803 setPointerPosition(self.pointerX, self.pointerY); 2804 render(true); 2805 } 2806 if (scrollProxy) { 2807 calculateBounds(); 2808 startElementY = scrollProxy.top(); 2809 startElementX = scrollProxy.left(); 2810 } else { 2811 //if the element is in the process of tweening, don't force snapping to occur because it could make it jump. Imagine the user throwing, then before it's done, clicking on the element in its inbetween state. 2812 if (isTweening()) { 2813 syncXY(true, true); 2814 calculateBounds(); 2815 } else { 2816 self.applyBounds(); 2817 } 2818 if (rotationMode) { 2819 rotationOrigin = self.rotationOrigin = _localToGlobal(target, {x:0, y:0}); 2820 syncXY(true, true); 2821 startElementX = self.x; //starting rotation (x always refers to rotation in type:"rotation", measured in degrees) 2822 startElementY = self.y = Math.atan2(rotationOrigin.y - self.pointerY, self.pointerX - rotationOrigin.x) * _RAD2DEG; 2823 } else { 2824 startScrollTop = target.parentNode ? target.parentNode.scrollTop || 0 : 0; 2825 startScrollLeft = target.parentNode ? target.parentNode.scrollLeft || 0 : 0;
2826 startElementY = _getStyle(target, yProp); //record the starting top and left values so that we can just add the mouse's movement to them later. 2827 startElementX = _getStyle(target, xProp); 2828 } 2829 } 2830 if (hasBounds && edgeTolerance) { 2831 if (startElementX > maxX) { 2832 startElementX = maxX + (startElementX - maxX) / edgeTolerance; 2833 } else if (startElementX < minX) { 2834 startElementX = minX - (minX - startElementX) / edgeTolerance; 2835 } 2836 if (!rotationMode) { 2837 if (startElementY > maxY) { 2838 startElementY = maxY + (startElementY - maxY) / edgeTolerance; 2839 } else if (startElementY < minY) { 2840 startElementY = minY - (minY - startElementY) / edgeTolerance; 2841 } 2842 } 2843 } 2844 self.startX = startElementX; 2845 self.startY = startElementY; 2846 }, 2847 2848 isTweening = function() { 2849 return (self.tween && self.tween.isActive()); 2850 }, 2851 2852 removePlaceholder = function() { 2853 if (_placeholderDiv.parentNode && !isTweening() && !self.isDragging) { //_placeholderDiv just props open auto-scrolling containers so they don't collapse as the user drags left/up. We remove it after dragging (and throwing, if necessary) finishes. 2854 _placeholderDiv.parentNode.removeChild(_placeholderDiv); 2855 } 2856 }, 2857 2858 buildSnapFunc = function(snap, min, max, factor) { 2859 if (typeof(snap) === "function") { 2860 return function(n) { 2861 var edgeTolerance = !self.isPressed ? 1 : 1 - self.edgeResistance; //if we're tweening, disable the edgeTolerance because it's already factored into the tweening values (we don't want to apply it multiple times) 2862 return snap.call(self, (n > max ? max + (n - max) * edgeTolerance : (n < min) ? min + (n - min) * edgeTolerance : n)) * factor; 2863 }; 2864 } 2865 if (snap instanceof Array) { 2866 return function(n) { 2867 var i = snap.length, 2868 closest = 0, 2869 absDif = _max, 2870 val, dif; 2871 while (--i > -1) { 2872 val = snap[i]; 2873 dif = val - n; 2874 if (dif < 0) { 2875 dif = -dif; 2876 } 2877 if (dif < absDif && val >= min && val <= max) { 2878 closest = i; 2879 absDif = dif; 2880 } 2881 } 2882 return snap[closest]; 2883 }; 2884 } 2885 return isNaN(snap) ? function(n) { return n; } : function() { return snap * factor; }; 2886 }, 2887 2888 buildPointSnapFunc = function(snap, minX, maxX, minY, maxY, radius, factor) { 2889 radius = (radius && radius < _max) ? radius * radius : _max; //so we don't have to Math.sqrt() in the functions. Performance optimization. 2890 if (typeof(snap) === "function") { 2891 return function(point) { 2892 var edgeTolerance = !self.isPressed ? 1 : 1 - self.edgeResistance, 2893 x = point.x, 2894 y = point.y, 2895 result, dx, dy; //if we're tweening, disable the edgeTolerance because it's already factored into the tweening values (we don't want to apply it multiple times) 2896 point.x = x = (x > maxX ? maxX + (x - maxX) * edgeTolerance : (x < minX) ? minX + (x - minX) * edgeTolerance : x); 2897 point.y = y = (y > maxY ? maxY + (y - maxY) * edgeTolerance : (y < minY) ? minY + (y - minY) * edgeTolerance : y); 2898 result = snap.call(self, point); 2899 if (result !== point) { 2900 point.x = result.x; 2901 point.y = result.y; 2902 } 2903 if (factor !== 1) { 2904 point.x *= factor; 2905 point.y *= factor; 2906 } 2907 if (radius < _max) { 2908 dx = point.x - x; 2909 dy = point.y - y; 2910 if (dx * dx + dy * dy > radius) { 2911 point.x = x; 2912 point.y = y; 2913 } 2914 } 2915 return point; 2916 }; 2917 } 2918 if (snap instanceof Array) { 2919 return function(p) { 2920 var i = snap.length, 2921 closest = 0, 2922 minDist = _max, 2923 x, y, point, dist; 2924 while (--i > -1) { 2925 point = snap[i]; 2926 x = point.x - p.x; 2927 y = point.y - p.y; 2928 dist = x * x + y * y; 2929 if (dist < minDist) { 2930 closest = i; 2931 minDist = dist; 2932 } 2933 } 2934 return (minDist <= radius) ? snap[closest] : p; 2935 }; 2936 } 2937 return function(n) { return n; }; 2938 }, 2939 2940 //called when the mouse is pressed (or touch starts) 2941 onPress = function(e, force) { 2942 var i; 2943 if (!enabled || self.isPressed || !e || ((e.type === "mousedown" || e.type === "pointerdown") && !force && _getTime() - clickTime < 30 && _touchEventLookup[self.pointerEvent.type])) { //when we DON'T preventDefault() in order to accommodate touch-scrolling and the user just taps, many browsers also fire a mousedown/mouseup sequence AFTER the touchstart/touchend sequence, thus it'd result in two quick "click" events being dispatched. This line senses that condition and halts it on the subsequent mousedown. 2944 return; 2945 } 2946 interrupted = isTweening(); 2947 self.pointerEvent = e; 2948 if (_touchEventLookup[e.type]) { //note: on iOS, BOTH touchmove and mousemove are dispatched, but the mousemove has pageY and pageX of 0 which w
2948ould mess up the calculations and needlessly hurt performance. 2949 touchEventTarget = (e.type.indexOf("touch") !== -1) ? (e.currentTarget || e.target) : _doc; //pointer-based touches (for Microsoft browsers) don't remain locked to the original target like other browsers, so we must use the document instead. The event type would be "MSPointerDown" or "pointerdown". 2950 _addListener(touchEventTarget, "touchend", onRelease); 2951 _addListener(touchEventTarget, "touchmove", onMove); 2952 _addListener(touchEventTarget, "touchcancel", onRelease); 2953 _addListener(_doc, "touchstart", _onMultiTouchDocument); 2954 } else { 2955 touchEventTarget = null; 2956 _addListener(_doc, "mousemove", onMove); //attach these to the document instead of the box itself so that if the user's mouse moves too quickly (and off of the box), things still work. 2957 } 2958 touchDragAxis = null; 2959 _addListener(_doc, "mouseup", onRelease); 2960 if (e && e.target) { 2961 _addListener(e.target, "mouseup", onRelease); //we also have to listen directly on the element because some browsers don't bubble up the event to the _doc on elements with contentEditable="true" 2962 } 2963 isClicking = (isClickable.call(self, e.target) && !vars.dragClickables && !force); 2964 if (isClicking) { 2965 _addListener(e.target, "change", onRelease); //in some browsers, when you mousedown on a <select> element, no mouseup gets dispatched! So we listen for a "change" event instead. 2966 _dispatchEvent(self, "press", "onPress"); 2967 _setSelectable(triggers, true); //accommodates things like inputs and elements with contentEditable="true" (otherwise user couldn't drag to select text) 2968 return; 2969 } 2970 allowNativeTouchScrolling = (!touchEventTarget || allowX === allowY || self.vars.allowNativeTouchScrolling === false) ? false : allowX ? "y" : "x"; 2971 if (_isOldIE) { 2972 e = _populateIEEvent(e, true); 2973 } else if (!allowNativeTouchScrolling && !self.allowEventDefault) { 2974 e.preventDefault(); 2975 if (e.preventManipulation) { 2976 e.preventManipulation(); //for some Microsoft browsers 2977 } 2978 } 2979 if (e.changedTouches) { //touch events store the data slightly differently 2980 e = touch = e.changedTouches[0]; 2981 touchID = e.identifier; 2982 } else if (e.pointerId) { 2983 touchID = e.pointerId; //for some Microsoft browsers 2984 } else { 2985 touch = touchID = null; 2986 } 2987 _dragCount++; 2988 _addToRenderQueue(render); //causes the Draggable to render on each "tick" of TweenLite.ticker (performance optimization - updating values in a mousemove can cause them to happen too frequently, like multiple times between frame redraws which is wasteful, and it also prevents values from updating properly in IE8) 2989 startPointerY = self.pointerY = e.pageY; //record the starting x and y so that we can calculate the movement from the original in _onMouseMove 2990 startPointerX = self.pointerX = e.pageX; 2991 if (allowNativeTouchScrolling || self.autoScroll) { 2992 _recordMaxScrolls(target.parentNode); 2993 } 2994 if (target.parentNode && self.autoScroll && !scrollProxy && !rotationMode && target.parentNode._gsMaxScrollX && !_placeholderDiv.parentNode && !target.getBBox) { //add a placeholder div to prevent the parent container from collapsing when the user drags the element left. 2995 _placeholderDiv.style.width = target.parentNode.scrollWidth + "px"; 2996 target.parentNode.appendChild(_placeholderDiv); 2997 } 2998 recordStartPositions(); 2999 if (self.tween) { 3000 self.tween.kill(); 3001 } 3002 self.isThrowing = false; 3003 TweenLite.killTweensOf(scrollProxy || target, true, killProps); //in case the user tries to drag it before the last tween is done. 3004 if (scrollProxy) { 3005 TweenLite.killTweensOf(target, true, {scrollTo:1}); //just in case the original target's scroll position is being tweened somewhere else. 3006 } 3007 self.tween = self.lockedAxis = null; 3008 if (vars.zIndexBoost || (!rotationMode && !scrollProxy && vars.zIndexBoost !== false)) { 3009 target.style.zIndex = Draggable.zIndex++; 3010 } 3011 self.isPressed = true; 3012 hasDragCallback = !!(vars.onDrag || self._listeners.drag); 3013 if (!rotationMode) { 3014 i = triggers.length; 3015 while (--i > -1) {
3016 _setStyle(triggers[i], "cursor", vars.cursor || "move"); 3017 } 3018 } 3019 _dispatchEvent(self, "press", "onPress"); 3020 }, 3021 3022 //called every time the mouse/touch moves 3023 onMove = function(e) { 3024 var originalEvent = e, 3025 touches, pointerX, pointerY, i, dx, dy; 3026 if (!enabled || _isMultiTouching || !self.isPressed || !e) { 3027 return; 3028 } 3029 self.pointerEvent = e; 3030 touches = e.changedTouches; 3031 if (touches) { //touch events store the data slightly differently 3032 e = touches[0]; 3033 if (e !== touch && e.identifier !== touchID) { //Usually changedTouches[0] will be what we're looking for, but in case it's not, look through the rest of the array...(and Android browsers don't reuse the event like iOS) 3034 i = touches.length; 3035 while (--i > -1 && (e = touches[i]).identifier !== touchID) {} 3036 if (i < 0) { 3037 return; 3038 } 3039 } 3040 } else if (e.pointerId && touchID && e.pointerId !== touchID) { //for some Microsoft browsers, we must attach the listener to the doc rather than the trigger so that when the finger moves outside the bounds of the trigger, things still work. So if the event we're receiving has a pointerId that doesn't match the touchID, ignore it (for multi-touch) 3041 return; 3042 } 3043 if (_isOldIE) { 3044 e = _populateIEEvent(e, true); 3045 } else { 3046 if (touchEventTarget && allowNativeTouchScrolling && !touchDragAxis) { //Android browsers force us to decide on the first "touchmove" event if we should allow the default (scrolling) behavior or preventDefault(). Otherwise, a "touchcancel" will be fired and then no "touchmove" or "touchend" will fire during the scrolling (no good). 3047 pointerX = e.pageX; 3048 pointerY = e.pageY; 3049 if (matrix) { 3050 i = pointerX * matrix[0] + pointerY * matrix[2] + matrix[4]; 3051 pointerY = pointerX * matrix[1] + pointerY * matrix[3] + matrix[5]; 3052 pointerX = i; 3053 } 3054 dx = Math.abs(pointerX - startPointerX); 3055 dy = Math.abs(pointerY - startPointerY); 3056 if ((dx !== dy && (dx > minimumMovement || dy > minimumMovement)) || (_isAndroid && allowNativeTouchScrolling === touchDragAxis)) { 3057 touchDragAxis = (dx > dy && allowX) ? "x" : "y"; 3058 if (self.vars.lockAxisOnTouchScroll !== false) { 3059 self.lockedAxis = (touchDragAxis === "x") ? "y" : "x"; 3060 if (typeof(self.vars.onLockAxis) === "function") { 3061 self.vars.onLockAxis.call(self, originalEvent); 3062 } 3063 } 3064 if (_isAndroid && allowNativeTouchScrolling === touchDragAxis) { 3065 onRelease(originalEvent); 3066 return; 3067 } 3068 } 3069 } 3070 if (!self.allowEventDefault && (!allowNativeTouchScrolling || (touchDragAxis && allowNativeTouchScrolling !== touchDragAxis)) && originalEvent.cancelable !== false) { 3071 originalEvent.preventDefault(); 3072 if (originalEvent.preventManipulation) { //for some Microsoft browsers 3073 originalEvent.preventManipulation(); 3074 } 3075 } 3076 } 3077 if (self.autoScroll) { 3078 checkAutoScrollBounds = true; 3079 } 3080 setPointerPosition(e.pageX, e.pageY); 3081 }, 3082 3083 setPointerPosition = function(pointerX, pointerY) { 3084 var dragTolerance = 1 - self.dragResistance, 3085 edgeTolerance = 1 - self.edgeResistance, 3086 xChange, yChange, x, y, dif, temp; 3087 3088 self.pointerX = pointerX; 3089 self.pointerY = pointerY; 3090 if (rotationMode) { 3091 y = Math.atan2(rotationOrigin.y - pointerY, pointerX - rotationOrigin.x) * _RAD2DEG; 3092 dif = self.y - y; 3093 if (dif > 180) { 3094 startElementY -= 360; 3095 self.y = y; 3096 } else if (dif < -180) { 3097 startElementY += 360; 3098 self.y = y; 3099 } 3100 if (self.x !== startElementX || Math.abs(startElementY - y) > minimumMovement) { 3101 self.y = y; 3102 x = startElementX + (startElementY - y) * dragTolerance; 3103 } else { 3104 x = startElementX; 3105 } 3106 3107 } else { 3108 if (matrix) { 3109 temp = pointerX * matrix[0] + pointerY * matrix[2] + matrix[4]; 3110 pointerY = pointerX * matrix[1] + pointerY * matrix[3] + matrix[5]; 3111 pointerX = temp; 3112 } 3113 yChange = (pointerY - startPointerY); 3114 xChange = (pointerX - startPointerX); 3115 if (yChange < minimumMovement && yChange > -minimumMovement) { 3116 yChange = 0; 3117 } 3118 if (xChange < minimumMovement && xChange > -minimumMovement) { 3119 xChange = 0; 3120 } 3121 if ((self.lockAxis || self.lockedAxis) && (xChange || yChange)) { 3122 temp = self.lockedAxis; 3123 if (!temp) { 3124 self.lockedAxis = temp = (allowX && Math.abs(xChange) > Math.abs(yChange)) ? "y" : allowY ? "x" : null; 3125 if (temp && typeof(self.vars.onLockAxis) === "function") { 3126 self.vars.onLockAxis.call(self, self.pointerEvent); 3127 } 3128 } 3129 if (temp === "y") { 3130 yChange = 0; 3131 } else if (temp === "x") { 3132 xChange = 0; 3133 } 3134 } 3135 x = startElementX + xChange * dragTolerance; 3136 y = startElementY + yChange * dragTolerance; 3137 } 3138 3139 if ((snapX || snapY || snapXY) && (self.x !== x || (self.y !== y && !rotationMode))) { 3140 if (snapXY) { 3141 _temp1.x = x; 3142 _temp1.y = y; 3143 temp = snapXY(_temp1); 3144 x = temp.x; 3145 y = temp.y; 3146 } 3147 if (snapX) { 3148 x = snapX(x); 3149 } 3150 if (snapY) { 3151 y = snapY(y); 3152 } 3153 } else if (hasBounds) { 3154 if (x > maxX) { 3155 x = maxX + (x - maxX) * edgeTolerance; 3156 } else if (x < minX) { 3157 x = minX + (x - minX) * edgeTolerance; 3158 } 3159 if (!rotationMode) { 3160 if (y > maxY) { 3161 y = maxY + (y - maxY) * edgeTolerance; 3162 } else if (y < minY) { 3163 y = minY + (y - minY) * edgeTolerance; 3164 } 3165 } 3166 } 3167 if (!rotationMode && !matrix) { 3168 x = Math.round(x); //helps work around an issue with some Win Touch devices 3169 y = Math.round(y); 3170 } 3171 if (self.x !== x || (self.y !== y && !rotationMode)) { 3172 if (rotationMode) { 3173 self.endRotation = self.x = self.endX = x; 3174 dirty = true; 3175 } else { 3176 if (allowY) { 3177 self.y = self.endY = y; 3178 dirty = true; //a flag that indicates we need to render the target next time the TweenLite.ticker dispatches a "tick" event (typically on a requestAnimationFrame) - this is a performance optimization (we shouldn't render on every move because sometimes many move events can get dispatched between screen refreshes, and that'd be wasteful to render every time) 3179 } 3180 if (allowX) { 3181 self.x = self.endX = x; 3182 dirty = true; 3183 } 3184 } 3185 if (!self.isDragging && self.isPressed) { 3186 self.isDragging = true; 3187 _dispatchEvent(self, "dragstart", "onDragStart"); 3188 } 3189 } 3190 }, 3191 3192 //called when the mouse/touch is released 3193 onRelease = function(e, force) { 3194 if (!enabled || !self.isPressed || (e && touchID != null && !force && ((e.pointerId && e.pointerId !== touchID) || (e.changedTouches && !_hasTouchID(e.changedTouches, touchID))))) { //for some Microsoft browsers, we must attach the listener to the doc rather than the trigger so that when the finger moves outside the bounds of the trigger, things still work. So if the event we're receiving has a pointerId that doesn't match the touchID, ignore it (for multi-touch) 3195 return; 3196 } 3197 self.isPressed = false;
3198 var originalEvent = e, 3199 wasDragging = self.isDragging, 3200 placeholderDelayedCall = TweenLite.delayedCall(0.001, removePlaceholder), 3201 touches, i, syntheticEvent, eventTarget, syntheticClick; 3202 if (touchEventTarget) { 3203 _removeListener(touchEventTarget, "touchend", onRelease); 3204 _removeListener(touchEventTarget, "touchmove", onMove); 3205 _removeListener(touchEventTarget, "touchcancel", onRelease); 3206 _removeListener(_doc, "touchstart", _onMultiTouchDocument); 3207 } else { 3208 _removeListener(_doc, "mousemove", onMove); 3209 } 3210 _removeListener(_doc, "mouseup", onRelease); 3211 if (e && e.target) { 3212 _removeListener(e.target, "mouseup", onRelease); 3213 } 3214 dirty = false; 3215 if (isClicking) { 3216 if (e) { 3217 _removeListener(e.target, "change", onRelease); 3218 self.pointerEvent = originalEvent; 3219 } 3220 _setSelectable(triggers, false); 3221 _dispatchEvent(self, "release", "onRelease"); 3222 _dispatchEvent(self, "click", "onClick"); 3223 isClicking = false; 3224 return; 3225 } 3226 _removeFromRenderQueue(render); 3227 if (!rotationMode) { 3228 i = triggers.length; 3229 while (--i > -1) { 3230 _setStyle(triggers[i], "cursor", vars.cursor || "move"); 3231 } 3232 } 3233 if (wasDragging) { 3234 dragEndTime = _lastDragTime = _getTime(); 3235 self.isDragging = false; 3236 } 3237 _dragCount--; 3238 if (e) { 3239 if (_isOldIE) { 3240 e = _populateIEEvent(e, false); 3241 } 3242 touches = e.changedTouches; 3243 if (touches) { //touch events store the data slightly differently 3244 e = touches[0]; 3245 if (e !== touch && e.identifier !== touchID) { //Usually changedTouches[0] will be what we're looking for, but in case it's not, look through the rest of the array...(and Android browsers don't reuse the event like iOS) 3246 i = touches.length; 3247 while (--i > -1 && (e = touches[i]).identifier !== touchID) {} 3248 if (i < 0) { 3249 return; 3250 } 3251 } 3252 } 3253 self.pointerEvent = originalEvent; 3254 self.pointerX = e.pageX; 3255 self.pointerY = e.pageY; 3256 } 3257 if (originalEvent && !wasDragging) { 3258 if (interrupted && (vars.snap || vars.bounds)) { //otherwise, if the user clicks on the object while it's animating to a snapped position, and then releases without moving 3 pixels, it will just stay there (it should animate/snap) 3259 animate(vars.throwProps); 3260 } 3261 _dispatchEvent(self, "release", "onRelease"); 3262 if ((!_isAndroid || originalEvent.type !== "touchmove") && originalEvent.type.indexOf("cancel") === -1) { //to accommodate native scrolling on Android devices, we have to immediately call onRelease() on the first touchmove event, but that shouldn't trigger a "click". 3263 _dispatchEvent(self, "click", "onClick"); 3264 if (_getTime() - clickTime < 300) { 3265 _dispatchEvent(self, "doubleclick", "onDoubleClick"); 3266 } 3267 eventTarget = originalEvent.target || originalEvent.srcElement || target; //old IE uses srcElement 3268 clickTime = _getTime(); 3269 syntheticClick = function() { // some browsers (like Firefox) won't trust script-generated clicks, so if the user tries to click on a video to play it, for example, it simply won't work. Since a regular "click" event will most likely be generated anyway (one that has its isTrusted flag set to true), we must slightly delay our script-generated click so that the "real"/trusted one is prioritized. Remember, when there are duplicate events in quick succession, we suppress all but the first one. Some browsers don't even trigger the "real" one at all, so our synthetic one is a safety valve that ensures that no matter what, a click event does get dispatched. 3270 if (clickTime !== clickDispatch && self.enabled() && !self.isPressed) { 3271 if (eventTarget.click) { //some browsers (like mobile Safari) don't properly trigger the click event 3272 eventTarget.click(); 3273 } else if (_doc.createEvent) { 3274 syntheticEvent = _doc.createEvent("MouseEvents"); 3275 syntheticEvent.initMouseEvent("click", true, true, window, 1, self.pointerEvent.
3275screenX, self.pointerEvent.screenY, self.pointerX, self.pointerY, false, false, false, false, 0, null); 3276 eventTarget.dispatchEvent(syntheticEvent); 3277 } 3278 } 3279 }; 3280 if (!_isAndroid && !originalEvent.defaultPrevented) { //iOS Safari requires the synthetic click to happen immediately or else it simply won't work, but Android doesn't play nice. 3281 TweenLite.delayedCall(0.00001, syntheticClick); //in addition to the iOS bug workaround, there's a Firefox issue with clicking on things like a video to play, so we must fake a click event in a slightly delayed fashion. Previously, we listened for the "click" event with "capture" false which solved the video-click-to-play issue, but it would allow the "click" event to be dispatched twice like if you were using a jQuery.click() because that was handled in the capture phase, thus we had to switch to the capture phase to avoid the double-dispatching, but do the delayed synthetic click. 3282 } 3283 } 3284 } else { 3285 animate(vars.throwProps); //will skip if throwProps isn't defined or ThrowPropsPlugin isn't loaded. 3286 if (!_isOldIE && !self.allowEventDefault && originalEvent && (vars.dragClickables || !isClickable.call(self, originalEvent.target)) && wasDragging && (!allowNativeTouchScrolling || (touchDragAxis && allowNativeTouchScrolling === touchDragAxis)) && originalEvent.cancelable !== false) { 3287 originalEvent.preventDefault(); 3288 if (originalEvent.preventManipulation) { 3289 originalEvent.preventManipulation(); //for some Microsoft browsers 3290 } 3291 } 3292 _dispatchEvent(self, "release", "onRelease"); 3293 } 3294 if (isTweening()) { 3295 placeholderDelayedCall.duration( self.tween.duration() ); //sync the timing so that the placeholder DIV gets 3296 } 3297 if (wasDragging) { 3298 _dispatchEvent(self, "dragend", "onDragEnd"); 3299 } 3300 return true; 3301 }, 3302 3303 updateScroll = function(e) { 3304 if (e && self.isDragging && !scrollProxy) { 3305 var parent = e.target || e.srcElement || target.parentNode, 3306 deltaX = parent.scrollLeft - parent._gsScrollX, 3307 deltaY = parent.scrollTop - parent._gsScrollY; 3308 if (deltaX || deltaY) { 3309 if (matrix) { 3310 startPointerX -= deltaX * matrix[0] + deltaY * matrix[2]; 3311 startPointerY -= deltaY * matrix[3] + deltaX * matrix[1]; 3312 } else { 3313 startPointerX -= deltaX; 3314 startPointerY -= deltaY; 3315 } 3316 parent._gsScrollX += deltaX; 3317 parent._gsScrollY += deltaY; 3318 setPointerPosition(self.pointerX, self.pointerY); 3319 } 3320 } 3321 }, 3322 3323 onClick = function(e) { //this was a huge pain in the neck to align all the various browsers and their behaviors. Chrome, Firefox, Safari, Opera, Android, and Microsoft Edge all handle events differently! Some will only trigger native behavior (like checkbox toggling) from trusted events. Others don't even support isTrusted, but require 2 events to flow through before triggering native behavior. Edge treats everything as trusted but also mandates that 2 flow through to trigger the correct native behavior. 3324 var time = _getTime(), 3325 recentlyClicked = (time - clickTime < 40), 3326 recentlyDragged = (time - dragEndTime < 40), 3327 alreadyDispatched = (recentlyClicked && clickDispatch === clickTime), 3328 isModern = !!e.preventDefault, 3329 defaultPrevented = (self.pointerEvent && self.pointerEvent.defaultPrevented), 3330 alreadyDispatchedTrusted = (recentlyClicked && trustedClickDispatch === clickTime), 3331 trusted = e.isTrusted || (e.isTrusted == null && recentlyClicked && alreadyDispatched); //note: Safari doesn't support isTrusted, and it won't properly execute native behavior (like toggling checkboxes) on the first synthetic "click" event - we must wait for the 2nd and treat it as trusted (but stop propagation at that point). Confusing, I know. Don't you love cross-browser compatibility challenges? 3332 if (isModern && (alreadyDispatched || (recentlyDragged && self.vars.suppressClickOnDrag !== false) )) { 3333 e.stopImmediatePropagation(); 3334 } 3335 if (recentlyClicked && !(self.pointerEvent && self.pointerEvent.defaultPrevented) && (!alreadyDispatched || (trusted !== alreadyDispatchedTrusted))) { //let the first click pass through unhindered. Let the next one only if it's trusted, then no more (stop quick-succession ones) 3336 if (trusted && alreadyDispatched) { 3337 trustedClickDispatch = clickTime; 3338 } 3339 clickDispatch = clickTime; 3340 return; 3341 } 3342 if (self.isPressed || recentlyDragged || recentlyClicked) { 3343 if (!isModern) { 3344 e.returnValue = false;
3345 } else if (!trusted || !e.detail || !recentlyClicked || defaultPrevented) { 3346 e.preventDefault(); 3347 if (e.preventManipulation) { 3348 e.preventManipulation(); //for some Microsoft browsers 3349 } 3350 } 3351 } 3352 }, 3353 3354 localizePoint = function(p) { 3355 return matrix ? {x:p.x * matrix[0] + p.y * matrix[2] + matrix[4], y:p.x * matrix[1] + p.y * matrix[3] + matrix[5]} : {x:p.x, y:p.y}; 3356 }; 3357 3358 old = Draggable.get(this.target); 3359 if (old) { 3360 old.kill(); // avoids duplicates (an element can only be controlled by one Draggable) 3361 } 3362 3363 //give the user access to start/stop dragging... 3364 this.startDrag = function(e, align) { 3365 var r1, r2, p1, p2; 3366 onPress(e || self.pointerEvent, true); 3367 //if the pointer isn't on top of the element, adjust things accordingly 3368 if (align && !self.hitTest(e || self.pointerEvent)) { 3369 r1 = _parseRect(e || self.pointerEvent); 3370 r2 = _parseRect(target); 3371 p1 = localizePoint({x:r1.left + r1.width / 2, y:r1.top + r1.height / 2}); 3372 p2 = localizePoint({x:r2.left + r2.width / 2, y:r2.top + r2.height / 2}); 3373 startPointerX -= p1.x - p2.x; 3374 startPointerY -= p1.y - p2.y; 3375 } 3376 if (!self.isDragging) { 3377 self.isDragging = true; 3378 _dispatchEvent(self, "dragstart", "onDragStart"); 3379 } 3380 }; 3381 this.drag = onMove; 3382 this.endDrag = function(e) { 3383 onRelease(e || self.pointerEvent, true); 3384 }; 3385 this.timeSinceDrag = function() { 3386 return self.isDragging ? 0 : (_getTime() - dragEndTime) / 1000; 3387 }; 3388 this.timeSinceClick = function() { 3389 return (_getTime() - clickTime) / 1000; 3390 }; 3391 this.hitTest = function(target, threshold) { 3392 return Draggable.hitTest(self.target, target, threshold); 3393 }; 3394 3395 this.getDirection = function(from, diagonalThreshold) { //from can be "start" (default), "velocity", or an element 3396 var mode = (from === "velocity" && ThrowPropsPlugin) ? from : (typeof(from) === "object" && !rotationMode) ? "element" : "start", 3397 xChange, yChange, ratio, direction, r1, r2; 3398 if (mode === "element") { 3399 r1 = _parseRect(self.target); 3400 r2 = _parseRect(from); 3401 } 3402 xChange = (mode === "start") ? self.x - startElementX : (mode === "velocity") ? ThrowPropsPlugin.getVelocity(this.target, xProp) : (r1.left + r1.width / 2) - (r2.left + r2.width / 2); 3403 if (rotationMode) { 3404 return xChange < 0 ? "counter-clockwise" : "clockwise"; 3405 } else { 3406 diagonalThreshold = diagonalThreshold || 2; 3407 yChange = (mode === "start") ? self.y - startElementY : (mode === "velocity") ? ThrowPropsPlugin.getVelocity(this.target, yProp) : (r1.top + r1.height / 2) - (r2.top + r2.height / 2); 3408 ratio = Math.abs(xChange / yChange); 3409 direction = (ratio < 1 / diagonalThreshold) ? "" : (xChange < 0) ? "left" : "right"; 3410 if (ratio < diagonalThreshold) { 3411 if (direction !== "") { 3412 direction += "-"; 3413 } 3414 direction += (yChange < 0) ? "up" : "down"; 3415 } 3416 } 3417 return direction; 3418 }; 3419 3420 3421 this.applyBounds = function(newBounds) { 3422 var x, y, forceZeroVelocity, e, parent, isRoot; 3423 if (newBounds && vars.bounds !== newBounds) { 3424 vars.bounds = newBounds; 3425 return self.update(true); 3426 } 3427 syncXY(true); 3428 calculateBounds(); 3429 if (hasBounds) { 3430 x = self.x; 3431 y = self.y; 3432 if (x > maxX) { 3433 x = maxX; 3434 } else if (x < minX) { 3435 x = minX; 3436 } 3437 if (y > maxY) { 3438 y = maxY; 3439 } else if (y < minY) { 3440 y = minY; 3441 } 3442 if (self.x !== x || self.y !== y) { 3443 forceZeroVelocity = true; 3444 self.x = self.endX = x; 3445 if (rotationMode) { 3446 self.endRotation = x; 3447 } else { 3448 self.y = self.endY = y; 3449 } 3450 dirty = true; 3451 render(true); 3452 if (self.autoScroll && !self.isDragging) { 3453 _recordMaxScrolls(target.parentNode); 3454 e = target; 3455 _windowProxy.scrollTop = ((window.pageYOffset != null) ? window.pageYOffset : (_docElement.scrollTop != null) ? _docElement.scrollTop : _doc.body.scrollTop); 3456 _windowProxy.scrollLeft = ((window.pageXOffset != null) ? window.pageXOffset : (_docElement.scrollLeft != null) ? _docElement.scrollLeft : _doc.body.scrollLeft); 3457 while (e && !isRoot) { //walk up the chain and sense wherever the scrollTop/scrollLeft exceeds the maximum. 3458 isRoot = _isRoot(e.parentNode); 3459 parent = isRoot ? _windowProxy : e.parentNode; 3460 if (allowY && parent.scrollTop > parent._gsMaxScrollY) { 3461 parent.scrollTop = parent._gsMaxScrollY; 3462 } 3463 if (allowX && parent.scrollLeft > parent._gsMaxScrollX) { 3464 parent.scrollLeft = parent._gsMaxScrollX; 3465 } 3466 e = parent; 3467 } 3468 } 3469 } 3470 if (self.isThrowing && (forceZeroVelocity || self.endX > maxX || self.endX < minX || self.endY > maxY || self.endY < minY)) { 3471 animate(vars.throwProps, forceZeroVelocity); 3472 } 3473 } 3474 return self; 3475 }; 3476 3477 this.update = function(applyBounds, sticky, ignoreExternalChanges) { 3478 var x = self.x, 3479 y = self.y; 3480 updateMatrix(!sticky);
3481 if (applyBounds) { 3482 self.applyBounds(); 3483 } else { 3484 if (dirty && ignoreExternalChanges) { 3485 render(true); 3486 } 3487 syncXY(true); 3488 } 3489 if (sticky) { 3490 setPointerPosition(self.pointerX, self.pointerY); 3491 if (dirty) { 3492 render(true); 3493 } 3494 } 3495 if (self.isPressed && !sticky && ((allowX && Math.abs(x - self.x) > 0.01) || (allowY && (Math.abs(y - self.y) > 0.01 && !rotationMode)))) { 3496 recordStartPositions(); 3497 } 3498 if (self.autoScroll) { 3499 _recordMaxScrolls(target.parentNode); 3500 checkAutoScrollBounds = self.isDragging; 3501 render(true); 3502 } 3503 if (self.autoScroll) { //in case reparenting occurred. 3504 _removeScrollListener(target, updateScroll); 3505 _addScrollListener(target, updateScroll); 3506 } 3507 return self; 3508 }; 3509 3510 this.enable = function(type) { 3511 var id, i, trigger; 3512 if (type !== "soft") { 3513 i = triggers.length; 3514 while (--i > -1) { 3515 trigger = triggers[i]; 3516 _addListener(trigger, "mousedown", onPress); 3517 _addListener(trigger, "touchstart", onPress); 3518 _addListener(trigger, "click", onClick, true); //note: used to pass true for capture but it prevented click-to-play-video functionality in Firefox. 3519 if (!rotationMode) { 3520 _setStyle(trigger, "cursor", vars.cursor || "move"); 3521 } 3522 _setStyle(trigger, "touchCallout", "none"); 3523 _setStyle(trigger, "touchAction", (allowX === allowY) ? "none" : allowX ? "pan-y" : "pan-x"); 3524 if (_isSVG(trigger)) { // a bug in chrome doesn't respect touch-action on SVG elements - it only works if we set it on the parent SVG. 3525 _setStyle(trigger.ownerSVGElement || trigger, "touchAction", (allowX === allowY) ? "none" : allowX ? "pan-y" : "pan-x"); 3526 } 3527 if (!this.vars.allowContextMenu) { 3528 _addListener(trigger, "contextmenu", onContextMenu); 3529 } 3530 } 3531 _setSelectable(triggers, false); 3532 } 3533 _addScrollListener(target, updateScroll); 3534 enabled = true; 3535 if (ThrowPropsPlugin && type !== "soft") { 3536 ThrowPropsPlugin.track(scrollProxy || target, (xyMode ? "x,y" : rotationMode ? "rotation" : "top,left")); 3537 } 3538 if (scrollProxy) { 3539 scrollProxy.enable(); 3540 } 3541 target._gsDragID = id = "d" + (_lookupCount++); 3542 _lookup[id] = this; 3543 if (scrollProxy) { 3544 scrollProxy.element._gsDragID = id; 3545 } 3546 TweenLite.set(target, {x:"+=0", overwrite:false, data:"_draggable"}); //simply ensures that there's a _gsTransform on the element. 3547 applyObj = { 3548 t:target, 3549 data:_isOldIE ? cssVars : target._gsTransform, 3550 tween:{}, 3551 setRatio:(_isOldIE ? function() { TweenLite.set(target, tempVars); } : CSSPlugin._internals.setTransformRatio || CSSPlugin._internals.set3DTransformRatio) 3552 }; 3553 recordStartPositions(); 3554 self.update(true); 3555 return self; 3556 }; 3557 3558 this.disable = function(type) { 3559 var dragging = self.isDragging, 3560 i, trigger; 3561 if (!rotationMode) { 3562 i = triggers.length; 3563 while (--i > -1) { 3564 _setStyle(triggers[i], "cursor", null); 3565 } 3566 } 3567 if (type !== "soft") { 3568 i = triggers.length; 3569 while (--i > -1) { 3570 trigger = triggers[i]; 3571 _setStyle(trigger, "touchCallout", null); 3572 _setStyle(trigger, "touchAction", null); 3573 _removeListener(trigger, "mousedown", onPress); 3574 _removeListener(trigger, "touchstart", onPress); 3575 _removeListener(trigger, "click", onClick); 3576 _removeListener(trigger, "contextmenu", onContextMenu); 3577 } 3578 _setSelectable(triggers, true); 3579 if (touchEventTarget) { 3580 _removeListener(touchEventTarget, "touchcancel", onRelease); 3581 _removeListener(touchEventTarget, "touchend", onRelease); 3582 _removeListener(touchEventTarget, "touchmove", onMove); 3583 } 3584 _removeListener(_doc, "mouseup", onRelease); 3585 _removeListener(_doc, "mousemove", onMove); 3586 } 3587 _removeScrollListener(target, updateScroll); 3588 enabled = false; 3589 if (ThrowPropsPlugin && type !== "soft") { 3590 ThrowPropsPlugin.untrack(scrollProxy || target, (xyMode ? "x,y" : rotationMode ? "rotation" : "top,left")); 3591 } 3592 if (scrollProxy) { 3593 scrollProxy.disable(); 3594 } 3595 _removeFromRenderQueue(render); 3596 self.isDragging = self.isPressed = isClicking = false;
3597 if (dragging) { 3598 _dispatchEvent(self, "dragend", "onDragEnd"); 3599 } 3600 return self; 3601 }; 3602 3603 this.enabled = function(value, type) { 3604 return arguments.length ? (value ? self.enable(type) : self.disable(type)) : enabled; 3605 }; 3606 3607 this.kill = function() { 3608 self.isThrowing = false; 3609 TweenLite.killTweensOf(scrollProxy || target, true, killProps); 3610 self.disable(); 3611 delete _lookup[target._gsDragID]; 3612 return self; 3613 }; 3614 3615 if (type.indexOf("scroll") !== -1) { 3616 scrollProxy = this.scrollProxy = new ScrollProxy(target, _extend({onKill:function() { //ScrollProxy's onKill() gets called if/when the ScrollProxy senses that the user interacted with the scroll position manually (like using the scrollbar). IE9 doesn't fire the "mouseup" properly when users drag the scrollbar of an element, so this works around that issue. 3617 if (self.isPressed) { 3618 onRelease(null); 3619 }}}, vars)); 3620 //a bug in many Android devices' stock browser causes scrollTop to get forced back to 0 after it is altered via JS, so we set overflow to "hidden" on mobile/touch devices (they hide the scroll bar anyway). That works around the bug. (This bug is discussed at https://code.google.com/p/android/issues/detail?id=19625) 3621 target.style.overflowY = (allowY && !_isTouchDevice) ? "auto" : "hidden"; 3622 target.style.overflowX = (allowX && !_isTouchDevice) ? "auto" : "hidden"; 3623 target = scrollProxy.content; 3624 } 3625 3626 if (vars.force3D !== false) { 3627 TweenLite.set(target, {force3D:true}); //improve performance by forcing a GPU layer when possible 3628 } 3629 if (rotationMode) { 3630 killProps.rotation = 1; 3631 } else { 3632 if (allowX) { 3633 killProps[xProp] = 1; 3634 } 3635 if (allowY) { 3636 killProps[yProp] = 1; 3637 } 3638 } 3639 if (rotationMode) { 3640 tempVars = _tempVarsRotation; 3641 cssVars = tempVars.css; 3642 tempVars.overwrite = false; 3643 } else if (xyMode) { 3644 tempVars = (allowX && allowY) ? _tempVarsXY : allowX ? _tempVarsX : _tempVarsY; 3645 cssVars = tempVars.css; 3646 tempVars.overwrite = false; 3647 } 3648 3649 this.enable(); 3650 }, 3651 p = Draggable.prototype = new EventDispatcher(); 3652 3653 p.constructor = Draggable; 3654 p.pointerX = p.pointerY = p.startX = p.startY = p.deltaX = p.deltaY = 0; 3655 p.isDragging = p.isPressed = false; 3656 Draggable.version = "0.16.1"; 3657 Draggable.zIndex = 1000; 3658 3659 _addListener(_doc, "touchcancel", function() { 3660 //some older Android devices intermittently stop dispatching "touchmove" events if we don't listen for "touchcancel" on the document. Very strange indeed. 3661 }); 3662 _addListener(_doc, "contextmenu", function(e) { 3663 var p; 3664 for (p in _lookup) { 3665 if (_lookup[p].isPressed) { 3666 _lookup[p].endDrag(); 3667 } 3668 } 3669 }); 3670 3671 Draggable.create = function(targets, vars) { 3672 if (typeof(targets) === "string") { 3673 targets = TweenLite.selector(targets); 3674 } 3675 var a = (!targets || targets.length === 0) ? [] : _isArrayLike(targets) ? _flattenArray(targets) : [targets], 3676 i = a.length; 3677 while (--i > -1) { 3678 a[i] = new Draggable(a[i], vars); 3679 } 3680 return a; 3681 }; 3682 3683 Draggable.get = function(target) { 3684 return _lookup[(_unwrapElement(target) || {})._gsDragID]; 3685 }; 3686 3687 Draggable.timeSinceDrag = function() { 3688 return (_getTime() - _lastDragTime) / 1000; 3689 }; 3690 3691 var _tempRect = {}, //reuse to reduce garbage collection tasks 3692 _oldIERect = function(e) { //IE8 doesn't support getBoundingClientRect(), so we use this as a backup. 3693 var top = 0, 3694 left = 0, 3695 width, height; 3696 e = _unwrapElement(e); 3697 width = e.offsetWidth; 3698 height = e.offsetHeight; 3699 while(e) { 3700 top += e.offsetTop; 3701 left += e.offsetLeft; 3702 e = e.offsetParent; 3703 } 3704 return {top: top, left: left, width: width, height: height}; 3705 }, 3706 _parseRect = function(e, undefined) { //accepts a DOM element, a mouse event, or a rectangle object and returns the corresponding rectangle with left, right, width, height, top, and bottom properties 3707 if (e === window) { 3708 _tempRect.left = _tempRect.top = 0; 3709 _tempRect.width = _tempRect.right = _docElement.clientWidth || e.innerWidth || _doc.body.clientWidth || 0;
3710 _tempRect.height = _tempRect.bottom = ((e.innerHeight || 0) - 20 < _docElement.clientHeight) ? _docElement.clientHeight : e.innerHeight || _doc.body.clientHeight || 0; 3711 return _tempRect; 3712 } 3713 var r = (e.pageX !== undefined) ? {left:e.pageX - _getDocScrollLeft(), top:e.pageY - _getDocScrollTop(), right:e.pageX - _getDocScrollLeft() + 1, bottom:e.pageY - _getDocScrollTop() + 1} : (!e.nodeType && e.left !== undefined && e.top !== undefined) ? e : _isOldIE ? _oldIERect(e) : _unwrapElement(e).getBoundingClientRect(); 3714 if (r.right === undefined && r.width !== undefined) { 3715 r.right = r.left + r.width; 3716 r.bottom = r.top + r.height; 3717 } else if (r.width === undefined) { //some browsers don't include width and height properties. We can't just set them directly on r because some browsers throw errors, so create a new generic object. 3718 r = {width: r.right - r.left, height: r.bottom - r.top, right: r.right, left: r.left, bottom: r.bottom, top: r.top}; 3719 } 3720 return r; 3721 }; 3722 3723 Draggable.hitTest = function(obj1, obj2, threshold) { 3724 if (obj1 === obj2) { 3725 return false; 3726 } 3727 var r1 = _parseRect(obj1), 3728 r2 = _parseRect(obj2), 3729 isOutside = (r2.left > r1.right || r2.right < r1.left || r2.top > r1.bottom || r2.bottom < r1.top), 3730 overlap, area, isRatio; 3731 if (isOutside || !threshold) { 3732 return !isOutside; 3733 } 3734 isRatio = ((threshold + "").indexOf("%") !== -1); 3735 threshold = parseFloat(threshold) || 0; 3736 overlap = {left:Math.max(r1.left, r2.left), top:Math.max(r1.top, r2.top)}; 3737 overlap.width = Math.min(r1.right, r2.right) - overlap.left; 3738 overlap.height = Math.min(r1.bottom, r2.bottom) - overlap.top; 3739 if (overlap.width < 0 || overlap.height < 0) { 3740 return false; 3741 } 3742 if (isRatio) { 3743 threshold *= 0.01; 3744 area = overlap.width * overlap.height; 3745 return (area >= r1.width * r1.height * threshold || area >= r2.width * r2.height * threshold); 3746 } 3747 return (overlap.width > threshold && overlap.height > threshold); 3748 }; 3749 3750 _placeholderDiv.style.cssText = "visibility:hidden;height:1px;top:-1px;pointer-events:none;position:relative;clear:both;"; 3751 3752 return Draggable; 3753 3754 }, true); 3755 3756}); if (_gsScope._gsDefine) { _gsScope._gsQueue.pop()(); } 3757 3758/* eslint-enable */
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.