PageSourceSearch

https://www.ctia.org/GSDevTools.js

js ctia.org collected 2026-10-09 23:50:21 UTC 188,945 bytes, 3,758 lines download raw bytes

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.