UNPKG

97.5 kBJavaScriptView Raw
1function _assertThisInitialized(self) { if (self === void 0) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return self; }
2
3function _inheritsLoose(subClass, superClass) { subClass.prototype = Object.create(superClass.prototype); subClass.prototype.constructor = subClass; subClass.__proto__ = superClass; }
4
5/*!
6 * Draggable 3.5.1
7 * https://greensock.com
8 *
9 * @license Copyright 2008-2020, GreenSock. All rights reserved.
10 * Subject to the terms at https://greensock.com/standard-license or for
11 * Club GreenSock members, the agreement issued with that membership.
12 * @author: Jack Doyle, jack@greensock.com
13 */
14
15/* eslint-disable */
16import { getGlobalMatrix, Matrix2D } from "./utils/matrix.js";
17
18var gsap,
19 _win,
20 _doc,
21 _docElement,
22 _body,
23 _tempDiv,
24 _placeholderDiv,
25 _coreInitted,
26 _checkPrefix,
27 _toArray,
28 _supportsPassive,
29 _isTouchDevice,
30 _touchEventLookup,
31 _dragCount,
32 _isMultiTouching,
33 _isAndroid,
34 InertiaPlugin,
35 _defaultCursor,
36 _supportsPointer,
37 _windowExists = function _windowExists() {
38 return typeof window !== "undefined";
39},
40 _getGSAP = function _getGSAP() {
41 return gsap || _windowExists() && (gsap = window.gsap) && gsap.registerPlugin && gsap;
42},
43 _isFunction = function _isFunction(value) {
44 return typeof value === "function";
45},
46 _isObject = function _isObject(value) {
47 return typeof value === "object";
48},
49 _isUndefined = function _isUndefined(value) {
50 return typeof value === "undefined";
51},
52 _emptyFunc = function _emptyFunc() {
53 return false;
54},
55 _transformProp = "transform",
56 _transformOriginProp = "transformOrigin",
57 _round = function _round(value) {
58 return Math.round(value * 10000) / 10000;
59},
60 _isArray = Array.isArray,
61 _createElement = function _createElement(type, ns) {
62 var e = _doc.createElementNS ? _doc.createElementNS((ns || "http://www.w3.org/1999/xhtml").replace(/^https/, "http"), type) : _doc.createElement(type); //some servers swap in https for http in the namespace which can break things, making "style" inaccessible.
63
64 return e.style ? e : _doc.createElement(type); //some environments won't allow access to the element's style when created with a namespace in which case we default to the standard createElement() to work around the issue. Also note that when GSAP is embedded directly inside an SVG file, createElement() won't allow access to the style object in Firefox (see https://greensock.com/forums/topic/20215-problem-using-tweenmax-in-standalone-self-containing-svg-file-err-cannot-set-property-csstext-of-undefined/).
65},
66 _RAD2DEG = 180 / Math.PI,
67 _bigNum = 1e20,
68 _identityMatrix = new Matrix2D(),
69 _getTime = Date.now || function () {
70 return new Date().getTime();
71},
72 _renderQueue = [],
73 _lookup = {},
74 //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.
75_lookupCount = 0,
76 _clickableTagExp = /^(?:a|input|textarea|button|select)$/i,
77 _lastDragTime = 0,
78 _temp1 = {},
79 // a simple object we reuse and populate (usually x/y properties) to conserve memory and improve performance.
80_windowProxy = {},
81 //memory/performance optimization - we reuse this object during autoScroll to store window-related bounds/offsets.
82_copy = function _copy(obj, factor) {
83 var copy = {},
84 p;
85
86 for (p in obj) {
87 copy[p] = factor ? obj[p] * factor : obj[p];
88 }
89
90 return copy;
91},
92 _extend = function _extend(obj, defaults) {
93 for (var p in defaults) {
94 if (!(p in obj)) {
95 obj[p] = defaults[p];
96 }
97 }
98
99 return obj;
100},
101 _renderQueueTick = function _renderQueueTick() {
102 return _renderQueue.forEach(function (func) {
103 return func();
104 });
105},
106 _addToRenderQueue = function _addToRenderQueue(func) {
107 _renderQueue.push(func);
108
109 if (_renderQueue.length === 1) {
110 gsap.ticker.add(_renderQueueTick);
111 }
112},
113 _renderQueueTimeout = function _renderQueueTimeout() {
114 return !_renderQueue.length && gsap.ticker.remove(_renderQueueTick);
115},
116 _removeFromRenderQueue = function _removeFromRenderQueue(func) {
117 var i = _renderQueue.length;
118
119 while (i--) {
120 if (_renderQueue[i] === func) {
121 _renderQueue.splice(i, 1);
122 }
123 }
124
125 gsap.to(_renderQueueTimeout, {
126 overwrite: true,
127 delay: 15,
128 duration: 0,
129 onComplete: _renderQueueTimeout,
130 data: "_draggable"
131 }); //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).
132},
133 _setDefaults = function _setDefaults(obj, defaults) {
134 for (var p in defaults) {
135 if (!(p in obj)) {
136 obj[p] = defaults[p];
137 }
138 }
139
140 return obj;
141},
142 _addListener = function _addListener(element, type, func, capture) {
143 if (element.addEventListener) {
144 var touchType = _touchEventLookup[type];
145 capture = capture || (_supportsPassive ? {
146 passive: false
147 } : null);
148 element.addEventListener(touchType || type, func, capture);
149 touchType && type !== touchType && element.addEventListener(type, func, capture); //some browsers actually support both, so must we. But pointer events cover all.
150 }
151},
152 _removeListener = function _removeListener(element, type, func) {
153 if (element.removeEventListener) {
154 var touchType = _touchEventLookup[type];
155 element.removeEventListener(touchType || type, func);
156 touchType && type !== touchType && element.removeEventListener(type, func);
157 }
158},
159 _preventDefault = function _preventDefault(event) {
160 event.preventDefault && event.preventDefault();
161 event.preventManipulation && event.preventManipulation(); //for some Microsoft browsers
162},
163 _hasTouchID = function _hasTouchID(list, ID) {
164 var i = list.length;
165
166 while (i--) {
167 if (list[i].identifier === ID) {
168 return true;
169 }
170 }
171},
172 _onMultiTouchDocumentEnd = function _onMultiTouchDocumentEnd(event) {
173 _isMultiTouching = event.touches && _dragCount < event.touches.length;
174
175 _removeListener(event.target, "touchend", _onMultiTouchDocumentEnd);
176},
177 _onMultiTouchDocument = function _onMultiTouchDocument(event) {
178 _isMultiTouching = event.touches && _dragCount < event.touches.length;
179
180 _addListener(event.target, "touchend", _onMultiTouchDocumentEnd);
181},
182 _getDocScrollTop = function _getDocScrollTop(doc) {
183 return _win.pageYOffset || doc.scrollTop || doc.documentElement.scrollTop || doc.body.scrollTop || 0;
184},
185 _getDocScrollLeft = function _getDocScrollLeft(doc) {
186 return _win.pageXOffset || doc.scrollLeft || doc.documentElement.scrollLeft || doc.body.scrollLeft || 0;
187},
188 _addScrollListener = function _addScrollListener(e, callback) {
189 _addListener(e, "scroll", callback);
190
191 if (!_isRoot(e.parentNode)) {
192 _addScrollListener(e.parentNode, callback);
193 }
194},
195 _removeScrollListener = function _removeScrollListener(e, callback) {
196 _removeListener(e, "scroll", callback);
197
198 if (!_isRoot(e.parentNode)) {
199 _removeScrollListener(e.parentNode, callback);
200 }
201},
202 _isRoot = function _isRoot(e) {
203 return !!(!e || e === _docElement || e.nodeType === 9 || e === _doc.body || e === _win || !e.nodeType || !e.parentNode);
204},
205 _getMaxScroll = function _getMaxScroll(element, axis) {
206 var dim = axis === "x" ? "Width" : "Height",
207 scroll = "scroll" + dim,
208 client = "client" + dim;
209 return Math.max(0, _isRoot(element) ? Math.max(_docElement[scroll], _body[scroll]) - (_win["inner" + dim] || _docElement[client] || _body[client]) : element[scroll] - element[client]);
210},
211 _recordMaxScrolls = function _recordMaxScrolls(e, skipCurrent) {
212 //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.
213 var x = _getMaxScroll(e, "x"),
214 y = _getMaxScroll(e, "y");
215
216 if (_isRoot(e)) {
217 e = _windowProxy;
218 } else {
219 _recordMaxScrolls(e.parentNode, skipCurrent);
220 }
221
222 e._gsMaxScrollX = x;
223 e._gsMaxScrollY = y;
224
225 if (!skipCurrent) {
226 e._gsScrollX = e.scrollLeft || 0;
227 e._gsScrollY = e.scrollTop || 0;
228 }
229},
230 _setStyle = function _setStyle(element, property, value) {
231 var style = element.style;
232
233 if (!style) {
234 return;
235 }
236
237 if (_isUndefined(style[property])) {
238 property = _checkPrefix(property, element) || property;
239 }
240
241 if (value == null) {
242 style.removeProperty && style.removeProperty(property.replace(/([A-Z])/g, "-$1").toLowerCase());
243 } else {
244 style[property] = value;
245 }
246},
247 _getComputedStyle = function _getComputedStyle(element) {
248 return _win.getComputedStyle(element instanceof Element ? element : element.host || (element.parentNode || {}).host || element);
249},
250 //the "host" stuff helps to accommodate ShadowDom objects.
251_tempRect = {},
252 //reuse to reduce garbage collection tasks
253_parseRect = function _parseRect(e) {
254 //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
255 if (e === _win) {
256 _tempRect.left = _tempRect.top = 0;
257 _tempRect.width = _tempRect.right = _docElement.clientWidth || e.innerWidth || _body.clientWidth || 0;
258 _tempRect.height = _tempRect.bottom = (e.innerHeight || 0) - 20 < _docElement.clientHeight ? _docElement.clientHeight : e.innerHeight || _body.clientHeight || 0;
259 return _tempRect;
260 }
261
262 var doc = e.ownerDocument || _doc,
263 r = !_isUndefined(e.pageX) ? {
264 left: e.pageX - _getDocScrollLeft(doc),
265 top: e.pageY - _getDocScrollTop(doc),
266 right: e.pageX - _getDocScrollLeft(doc) + 1,
267 bottom: e.pageY - _getDocScrollTop(doc) + 1
268 } : !e.nodeType && !_isUndefined(e.left) && !_isUndefined(e.top) ? e : _toArray(e)[0].getBoundingClientRect();
269
270 if (_isUndefined(r.right) && !_isUndefined(r.width)) {
271 r.right = r.left + r.width;
272 r.bottom = r.top + r.height;
273 } else if (_isUndefined(r.width)) {
274 //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.
275 r = {
276 width: r.right - r.left,
277 height: r.bottom - r.top,
278 right: r.right,
279 left: r.left,
280 bottom: r.bottom,
281 top: r.top
282 };
283 }
284
285 return r;
286},
287 _dispatchEvent = function _dispatchEvent(target, type, callbackName) {
288 var vars = target.vars,
289 callback = vars[callbackName],
290 listeners = target._listeners[type],
291 result;
292
293 if (_isFunction(callback)) {
294 result = callback.apply(vars.callbackScope || target, vars[callbackName + "Params"] || [target.pointerEvent]);
295 }
296
297 if (listeners && target.dispatchEvent(type) === false) {
298 result = false;
299 }
300
301 return result;
302},
303 _getBounds = function _getBounds(target, context) {
304 //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.
305 var e = _toArray(target)[0],
306 top,
307 left,
308 offset;
309
310 if (!e.nodeType && e !== _win) {
311 if (!_isUndefined(target.left)) {
312 offset = {
313 x: 0,
314 y: 0
315 }; //_getOffsetTransformOrigin(context); //the bounds should be relative to the origin
316
317 return {
318 left: target.left - offset.x,
319 top: target.top - offset.y,
320 width: target.width,
321 height: target.height
322 };
323 }
324
325 left = target.min || target.minX || target.minRotation || 0;
326 top = target.min || target.minY || 0;
327 return {
328 left: left,
329 top: top,
330 width: (target.max || target.maxX || target.maxRotation || 0) - left,
331 height: (target.max || target.maxY || 0) - top
332 };
333 }
334
335 return _getElementBounds(e, context);
336},
337 _point1 = {},
338 //we reuse to minimize garbage collection tasks.
339_getElementBounds = function _getElementBounds(element, context) {
340 context = _toArray(context)[0];
341 var isSVG = element.getBBox && element.ownerSVGElement,
342 doc = element.ownerDocument || _doc,
343 left,
344 right,
345 top,
346 bottom,
347 matrix,
348 p1,
349 p2,
350 p3,
351 p4,
352 bbox,
353 width,
354 height,
355 cs,
356 contextParent;
357
358 if (element === _win) {
359 top = _getDocScrollTop(doc);
360 left = _getDocScrollLeft(doc);
361 right = left + (doc.documentElement.clientWidth || element.innerWidth || doc.body.clientWidth || 0);
362 bottom = top + ((element.innerHeight || 0) - 20 < doc.documentElement.clientHeight ? doc.documentElement.clientHeight : element.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.
363 } else if (context === _win || _isUndefined(context)) {
364 return element.getBoundingClientRect();
365 } else {
366 left = top = 0;
367
368 if (isSVG) {
369 bbox = element.getBBox();
370 width = bbox.width;
371 height = bbox.height;
372 } else {
373 if (element.viewBox && (bbox = element.viewBox.baseVal)) {
374 left = bbox.x || 0;
375 top = bbox.y || 0;
376 width = bbox.width;
377 height = bbox.height;
378 }
379
380 if (!width) {
381 cs = _getComputedStyle(element);
382 bbox = cs.boxSizing === "border-box";
383 width = (parseFloat(cs.width) || element.clientWidth || 0) + (bbox ? 0 : parseFloat(cs.borderLeftWidth) + parseFloat(cs.borderRightWidth));
384 height = (parseFloat(cs.height) || element.clientHeight || 0) + (bbox ? 0 : parseFloat(cs.borderTopWidth) + parseFloat(cs.borderBottomWidth));
385 }
386 }
387
388 right = width;
389 bottom = height;
390 }
391
392 if (element === context) {
393 return {
394 left: left,
395 top: top,
396 width: right - left,
397 height: bottom - top
398 };
399 }
400
401 matrix = getGlobalMatrix(context, true).multiply(getGlobalMatrix(element));
402 p1 = matrix.apply({
403 x: left,
404 y: top
405 });
406 p2 = matrix.apply({
407 x: right,
408 y: top
409 });
410 p3 = matrix.apply({
411 x: right,
412 y: bottom
413 });
414 p4 = matrix.apply({
415 x: left,
416 y: bottom
417 });
418 left = Math.min(p1.x, p2.x, p3.x, p4.x);
419 top = Math.min(p1.y, p2.y, p3.y, p4.y);
420 contextParent = context.parentNode || {};
421 return {
422 left: left + (contextParent.scrollLeft || 0),
423 top: top + (contextParent.scrollTop || 0),
424 width: Math.max(p1.x, p2.x, p3.x, p4.x) - left,
425 height: Math.max(p1.y, p2.y, p3.y, p4.y) - top
426 };
427},
428 _parseInertia = function _parseInertia(draggable, snap, max, min, factor, forceZeroVelocity) {
429 var vars = {},
430 a,
431 i,
432 l;
433
434 if (snap) {
435 if (factor !== 1 && snap instanceof Array) {
436 //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.
437 vars.end = a = [];
438 l = snap.length;
439
440 if (_isObject(snap[0])) {
441 //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.
442 for (i = 0; i < l; i++) {
443 a[i] = _copy(snap[i], factor);
444 }
445 } else {
446 for (i = 0; i < l; i++) {
447 a[i] = snap[i] * factor;
448 }
449 }
450
451 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.
452
453 min -= 1.1;
454 } else if (_isFunction(snap)) {
455 vars.end = function (value) {
456 var result = snap.call(draggable, value),
457 copy,
458 p;
459
460 if (factor !== 1) {
461 if (_isObject(result)) {
462 copy = {};
463
464 for (p in result) {
465 copy[p] = result[p] * factor;
466 }
467
468 result = copy;
469 } else {
470 result *= factor;
471 }
472 }
473
474 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 y from within that function.
475 };
476 } else {
477 vars.end = snap;
478 }
479 }
480
481 if (max || max === 0) {
482 vars.max = max;
483 }
484
485 if (min || min === 0) {
486 vars.min = min;
487 }
488
489 if (forceZeroVelocity) {
490 vars.velocity = 0;
491 }
492
493 return vars;
494},
495 _isClickable = function _isClickable(element) {
496 //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).
497 var data;
498 return !element || !element.getAttribute || element === _body ? false : (data = element.getAttribute("data-clickable")) === "true" || data !== "false" && (element.onclick || _clickableTagExp.test(element.nodeName + "") || element.getAttribute("contentEditable") === "true") ? true : _isClickable(element.parentNode);
499},
500 _setSelectable = function _setSelectable(elements, selectable) {
501 var i = elements.length,
502 e;
503
504 while (i--) {
505 e = elements[i];
506 e.ondragstart = e.onselectstart = selectable ? null : _emptyFunc; //setStyle(e, "userSelect", (selectable ? "text" : "none"));
507
508 gsap.set(e, {
509 lazy: true,
510 userSelect: selectable ? "text" : "none"
511 });
512 }
513},
514 _isFixed = function _isFixed(element) {
515 if (_getComputedStyle(element).position === "fixed") {
516 return true;
517 }
518
519 element = element.parentNode;
520
521 if (element && element.nodeType === 1) {
522 // avoid document fragments which will throw an error.
523 return _isFixed(element);
524 }
525},
526 _supports3D,
527 _addPaddingBR,
528 //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.
529ScrollProxy = function ScrollProxy(element, vars) {
530 element = gsap.utils.toArray(element)[0];
531 vars = vars || {};
532 var content = document.createElement("div"),
533 style = content.style,
534 node = element.firstChild,
535 offsetTop = 0,
536 offsetLeft = 0,
537 prevTop = element.scrollTop,
538 prevLeft = element.scrollLeft,
539 scrollWidth = element.scrollWidth,
540 scrollHeight = element.scrollHeight,
541 extraPadRight = 0,
542 maxLeft = 0,
543 maxTop = 0,
544 elementWidth,
545 elementHeight,
546 contentHeight,
547 nextNode,
548 transformStart,
549 transformEnd;
550
551 if (_supports3D && vars.force3D !== false) {
552 transformStart = "translate3d(";
553 transformEnd = "px,0px)";
554 } else if (_transformProp) {
555 transformStart = "translate(";
556 transformEnd = "px)";
557 }
558
559 this.scrollTop = function (value, force) {
560 if (!arguments.length) {
561 return -this.top();
562 }
563
564 this.top(-value, force);
565 };
566
567 this.scrollLeft = function (value, force) {
568 if (!arguments.length) {
569 return -this.left();
570 }
571
572 this.left(-value, force);
573 };
574
575 this.left = function (value, force) {
576 if (!arguments.length) {
577 return -(element.scrollLeft + offsetLeft);
578 }
579
580 var dif = element.scrollLeft - prevLeft,
581 oldOffset = offsetLeft;
582
583 if ((dif > 2 || dif < -2) && !force) {
584 //if the user interacts with the scrollbar (or something else scrolls it, like the mouse wheel), we should kill any tweens of the ScrollProxy.
585 prevLeft = element.scrollLeft;
586 gsap.killTweensOf(this, {
587 left: 1,
588 scrollLeft: 1
589 });
590 this.left(-prevLeft);
591
592 if (vars.onKill) {
593 vars.onKill();
594 }
595
596 return;
597 }
598
599 value = -value; //invert because scrolling works in the opposite direction
600
601 if (value < 0) {
602 offsetLeft = value - 0.5 | 0;
603 value = 0;
604 } else if (value > maxLeft) {
605 offsetLeft = value - maxLeft | 0;
606 value = maxLeft;
607 } else {
608 offsetLeft = 0;
609 }
610
611 if (offsetLeft || oldOffset) {
612 if (!this._skip) {
613 style[_transformProp] = transformStart + -offsetLeft + "px," + -offsetTop + transformEnd;
614 }
615
616 if (offsetLeft + extraPadRight >= 0) {
617 style.paddingRight = offsetLeft + extraPadRight + "px";
618 }
619 }
620
621 element.scrollLeft = value | 0;
622 prevLeft = element.scrollLeft; //don't merge this with the line above because some browsers adjust 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.
623 };
624
625 this.top = function (value, force) {
626 if (!arguments.length) {
627 return -(element.scrollTop + offsetTop);
628 }
629
630 var dif = element.scrollTop - prevTop,
631 oldOffset = offsetTop;
632
633 if ((dif > 2 || dif < -2) && !force) {
634 //if the user interacts with the scrollbar (or something else scrolls it, like the mouse wheel), we should kill any tweens of the ScrollProxy.
635 prevTop = element.scrollTop;
636 gsap.killTweensOf(this, {
637 top: 1,
638 scrollTop: 1
639 });
640 this.top(-prevTop);
641
642 if (vars.onKill) {
643 vars.onKill();
644 }
645
646 return;
647 }
648
649 value = -value; //invert because scrolling works in the opposite direction
650
651 if (value < 0) {
652 offsetTop = value - 0.5 | 0;
653 value = 0;
654 } else if (value > maxTop) {
655 offsetTop = value - maxTop | 0;
656 value = maxTop;
657 } else {
658 offsetTop = 0;
659 }
660
661 if (offsetTop || oldOffset) {
662 if (!this._skip) {
663 style[_transformProp] = transformStart + -offsetLeft + "px," + -offsetTop + transformEnd;
664 }
665 }
666
667 element.scrollTop = value | 0;
668 prevTop = element.scrollTop;
669 };
670
671 this.maxScrollTop = function () {
672 return maxTop;
673 };
674
675 this.maxScrollLeft = function () {
676 return maxLeft;
677 };
678
679 this.disable = function () {
680 node = content.firstChild;
681
682 while (node) {
683 nextNode = node.nextSibling;
684 element.appendChild(node);
685 node = nextNode;
686 }
687
688 if (element === content.parentNode) {
689 //in case disable() is called when it's already disabled.
690 element.removeChild(content);
691 }
692 };
693
694 this.enable = function () {
695 node = element.firstChild;
696
697 if (node === content) {
698 return;
699 }
700
701 while (node) {
702 nextNode = node.nextSibling;
703 content.appendChild(node);
704 node = nextNode;
705 }
706
707 element.appendChild(content);
708 this.calibrate();
709 };
710
711 this.calibrate = function (force) {
712 var widthMatches = element.clientWidth === elementWidth,
713 cs,
714 x,
715 y;
716 prevTop = element.scrollTop;
717 prevLeft = element.scrollLeft;
718
719 if (widthMatches && element.clientHeight === elementHeight && content.offsetHeight === contentHeight && scrollWidth === element.scrollWidth && scrollHeight === element.scrollHeight && !force) {
720 return; //no need to recalculate things if the width and height haven't changed.
721 }
722
723 if (offsetTop || offsetLeft) {
724 x = this.left();
725 y = this.top();
726 this.left(-element.scrollLeft);
727 this.top(-element.scrollTop);
728 }
729
730 cs = _getComputedStyle(element); //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 record 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)
731
732 if (!widthMatches || force) {
733 style.display = "block";
734 style.width = "auto";
735 style.paddingRight = "0px";
736 extraPadRight = Math.max(0, element.scrollWidth - element.clientWidth); //if the content is wider than the container, we need to add the paddingLeft and paddingRight in order for things to behave correctly.
737
738 if (extraPadRight) {
739 extraPadRight += parseFloat(cs.paddingLeft) + (_addPaddingBR ? parseFloat(cs.paddingRight) : 0);
740 }
741 }
742
743 style.display = "inline-block";
744 style.position = "relative";
745 style.overflow = "visible";
746 style.verticalAlign = "top";
747 style.boxSizing = "content-box";
748 style.width = "100%";
749 style.paddingRight = extraPadRight + "px"; //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
750
751 if (_addPaddingBR) {
752 style.paddingBottom = cs.paddingBottom;
753 }
754
755 elementWidth = element.clientWidth;
756 elementHeight = element.clientHeight;
757 scrollWidth = element.scrollWidth;
758 scrollHeight = element.scrollHeight;
759 maxLeft = element.scrollWidth - elementWidth;
760 maxTop = element.scrollHeight - elementHeight;
761 contentHeight = content.offsetHeight;
762 style.display = "block";
763
764 if (x || y) {
765 this.left(x);
766 this.top(y);
767 }
768 };
769
770 this.content = content;
771 this.element = element;
772 this._skip = false;
773 this.enable();
774},
775 _initCore = function _initCore(required) {
776 if (_windowExists() && document.body) {
777 var nav = window && window.navigator;
778 _win = window;
779 _doc = document;
780 _docElement = _doc.documentElement;
781 _body = _doc.body;
782 _tempDiv = _createElement("div");
783 _supportsPointer = !!window.PointerEvent;
784 _placeholderDiv = _createElement("div");
785 _placeholderDiv.style.cssText = "visibility:hidden;height:1px;top:-1px;pointer-events:none;position:relative;clear:both;cursor:grab";
786 _defaultCursor = _placeholderDiv.style.cursor === "grab" ? "grab" : "move";
787 _isAndroid = nav && nav.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
788
789 _isTouchDevice = "ontouchstart" in _docElement && "orientation" in _win || nav && (nav.MaxTouchPoints > 0 || nav.msMaxTouchPoints > 0);
790
791 _addPaddingBR = function () {
792 //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).
793 var div = _createElement("div"),
794 child = _createElement("div"),
795 childStyle = child.style,
796 parent = _body,
797 val;
798
799 childStyle.display = "inline-block";
800 childStyle.position = "relative";
801 div.style.cssText = child.innerHTML = "width:90px;height:40px;padding:10px;overflow:auto;visibility:hidden";
802 div.appendChild(child);
803 parent.appendChild(div);
804 val = 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.
805
806 parent.removeChild(div);
807 return val;
808 }();
809
810 _touchEventLookup = function (types) {
811 //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.
812 var standard = types.split(","),
813 converted = ("onpointerdown" in _tempDiv ? "pointerdown,pointermove,pointerup,pointercancel" : "onmspointerdown" in _tempDiv ? "MSPointerDown,MSPointerMove,MSPointerUp,MSPointerCancel" : types).split(","),
814 obj = {},
815 i = 4;
816
817 while (--i > -1) {
818 obj[standard[i]] = converted[i];
819 obj[converted[i]] = standard[i];
820 } //to avoid problems in iOS 9, test to see if the browser supports the "passive" option on addEventListener().
821
822
823 try {
824 _docElement.addEventListener("test", null, Object.defineProperty({}, "passive", {
825 get: function get() {
826 _supportsPassive = 1;
827 }
828 }));
829 } catch (e) {}
830
831 return obj;
832 }("touchstart,touchmove,touchend,touchcancel");
833
834 _addListener(_doc, "touchcancel", _emptyFunc); //some older Android devices intermittently stop dispatching "touchmove" events if we don't listen for "touchcancel" on the document. Very strange indeed.
835
836
837 _addListener(_win, "touchmove", _emptyFunc); //works around Safari bugs that still allow the page to scroll even when we preventDefault() on the touchmove event.
838
839
840 _body && _body.addEventListener("touchstart", _emptyFunc); //works around Safari bug: https://greensock.com/forums/topic/21450-draggable-in-iframe-on-mobile-is-buggy/
841
842 _addListener(_doc, "contextmenu", function () {
843 for (var p in _lookup) {
844 if (_lookup[p].isPressed) {
845 _lookup[p].endDrag();
846 }
847 }
848 });
849
850 gsap = _coreInitted = _getGSAP();
851 }
852
853 if (gsap) {
854 InertiaPlugin = gsap.plugins.inertia;
855 _checkPrefix = gsap.utils.checkPrefix;
856 _transformProp = _checkPrefix(_transformProp);
857 _transformOriginProp = _checkPrefix(_transformOriginProp);
858 _toArray = gsap.utils.toArray;
859 _supports3D = !!_checkPrefix("perspective");
860 } else if (required) {
861 console.warn("Please gsap.registerPlugin(Draggable)");
862 }
863};
864
865var EventDispatcher = /*#__PURE__*/function () {
866 function EventDispatcher(target) {
867 this._listeners = {};
868 this.target = target || this;
869 }
870
871 var _proto = EventDispatcher.prototype;
872
873 _proto.addEventListener = function addEventListener(type, callback) {
874 var list = this._listeners[type] || (this._listeners[type] = []);
875
876 if (!~list.indexOf(callback)) {
877 list.push(callback);
878 }
879 };
880
881 _proto.removeEventListener = function removeEventListener(type, callback) {
882 var list = this._listeners[type],
883 i = list && list.indexOf(callback) || -1;
884 i > -1 && list.splice(i, 1);
885 };
886
887 _proto.dispatchEvent = function dispatchEvent(type) {
888 var _this = this;
889
890 var result;
891 (this._listeners[type] || []).forEach(function (callback) {
892 return callback.call(_this, {
893 type: type,
894 target: _this.target
895 }) === false && (result = false);
896 });
897 return result; //if any of the callbacks return false, pass that along.
898 };
899
900 return EventDispatcher;
901}();
902
903export var Draggable = /*#__PURE__*/function (_EventDispatcher) {
904 _inheritsLoose(Draggable, _EventDispatcher);
905
906 function Draggable(target, vars) {
907 var _this2;
908
909 _this2 = _EventDispatcher.call(this) || this;
910
911 if (!gsap) {
912 _initCore(1);
913 }
914
915 target = _toArray(target)[0]; //in case the target is a selector object or selector text
916
917 if (!InertiaPlugin) {
918 InertiaPlugin = gsap.plugins.inertia;
919 }
920
921 _this2.vars = vars = _copy(vars || {});
922 _this2.target = target;
923 _this2.x = _this2.y = _this2.rotation = 0;
924 _this2.dragResistance = parseFloat(vars.dragResistance) || 0;
925 _this2.edgeResistance = isNaN(vars.edgeResistance) ? 1 : parseFloat(vars.edgeResistance) || 0;
926 _this2.lockAxis = vars.lockAxis;
927 _this2.autoScroll = vars.autoScroll || 0;
928 _this2.lockedAxis = null;
929 _this2.allowEventDefault = !!vars.allowEventDefault;
930 gsap.getProperty(target, "x"); // to ensure that transforms are instantiated.
931
932 var type = (vars.type || "x,y").toLowerCase(),
933 xyMode = ~type.indexOf("x") || ~type.indexOf("y"),
934 rotationMode = type.indexOf("rotation") !== -1,
935 xProp = rotationMode ? "rotation" : xyMode ? "x" : "left",
936 yProp = xyMode ? "y" : "top",
937 allowX = !!(~type.indexOf("x") || ~type.indexOf("left") || type === "scroll"),
938 allowY = !!(~type.indexOf("y") || ~type.indexOf("top") || type === "scroll"),
939 minimumMovement = vars.minimumMovement || 2,
940 self = _assertThisInitialized(_this2),
941 triggers = _toArray(vars.trigger || vars.handle || target),
942 killProps = {},
943 dragEndTime = 0,
944 checkAutoScrollBounds = false,
945 autoScrollMarginTop = vars.autoScrollMarginTop || 40,
946 autoScrollMarginRight = vars.autoScrollMarginRight || 40,
947 autoScrollMarginBottom = vars.autoScrollMarginBottom || 40,
948 autoScrollMarginLeft = vars.autoScrollMarginLeft || 40,
949 isClickable = vars.clickableTest || _isClickable,
950 clickTime = 0,
951 gsCache = target._gsap || gsap.core.getCache(target),
952 isFixed = _isFixed(target),
953 getPropAsNum = function getPropAsNum(property, unit) {
954 return parseFloat(gsCache.get(target, property, unit));
955 },
956 ownerDoc = target.ownerDocument || _doc,
957 enabled,
958 scrollProxy,
959 startPointerX,
960 startPointerY,
961 startElementX,
962 startElementY,
963 hasBounds,
964 hasDragCallback,
965 hasMoveCallback,
966 maxX,
967 minX,
968 maxY,
969 minY,
970 touch,
971 touchID,
972 rotationOrigin,
973 dirty,
974 old,
975 snapX,
976 snapY,
977 snapXY,
978 isClicking,
979 touchEventTarget,
980 matrix,
981 interrupted,
982 allowNativeTouchScrolling,
983 touchDragAxis,
984 isDispatching,
985 clickDispatch,
986 trustedClickDispatch,
987 isPreventingDefault,
988 onContextMenu = function onContextMenu(e) {
989 //used to prevent long-touch from triggering a context menu.
990 // (self.isPressed && e.which < 2) && self.endDrag() // previously ended drag when context menu was triggered, but instead we should just stop propagation and prevent the default event behavior.
991 _preventDefault(e);
992
993 e.stopImmediatePropagation && e.stopImmediatePropagation();
994 return false;
995 },
996 //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.
997 render = function render(suppressEvents) {
998 if (self.autoScroll && self.isDragging && (checkAutoScrollBounds || dirty)) {
999 var e = target,
1000 autoScrollFactor = self.autoScroll * 15,
1001 //multiplying by 15 just gives us a better "feel" speed-wise.
1002 parent,
1003 isRoot,
1004 rect,
1005 pointerX,
1006 pointerY,
1007 changeX,
1008 changeY,
1009 gap;
1010 checkAutoScrollBounds = false;
1011 _windowProxy.scrollTop = _win.pageYOffset != null ? _win.pageYOffset : ownerDoc.documentElement.scrollTop != null ? ownerDoc.documentElement.scrollTop : ownerDoc.body.scrollTop;
1012 _windowProxy.scrollLeft = _win.pageXOffset != null ? _win.pageXOffset : ownerDoc.documentElement.scrollLeft != null ? ownerDoc.documentElement.scrollLeft : ownerDoc.body.scrollLeft;
1013 pointerX = self.pointerX - _windowProxy.scrollLeft;
1014 pointerY = self.pointerY - _windowProxy.scrollTop;
1015
1016 while (e && !isRoot) {
1017 //walk up the chain and sense wherever the pointer is within 40px of an edge that's scrollable.
1018 isRoot = _isRoot(e.parentNode);
1019 parent = isRoot ? _windowProxy : e.parentNode;
1020 rect = isRoot ? {
1021 bottom: Math.max(_docElement.clientHeight, _win.innerHeight || 0),
1022 right: Math.max(_docElement.clientWidth, _win.innerWidth || 0),
1023 left: 0,
1024 top: 0
1025 } : parent.getBoundingClientRect();
1026 changeX = changeY = 0;
1027
1028 if (allowY) {
1029 gap = parent._gsMaxScrollY - parent.scrollTop;
1030
1031 if (gap < 0) {
1032 changeY = gap;
1033 } else if (pointerY > rect.bottom - autoScrollMarginBottom && gap) {
1034 checkAutoScrollBounds = true;
1035 changeY = Math.min(gap, autoScrollFactor * (1 - Math.max(0, rect.bottom - pointerY) / autoScrollMarginBottom) | 0);
1036 } else if (pointerY < rect.top + autoScrollMarginTop && parent.scrollTop) {
1037 checkAutoScrollBounds = true;
1038 changeY = -Math.min(parent.scrollTop, autoScrollFactor * (1 - Math.max(0, pointerY - rect.top) / autoScrollMarginTop) | 0);
1039 }
1040
1041 if (changeY) {
1042 parent.scrollTop += changeY;
1043 }
1044 }
1045
1046 if (allowX) {
1047 gap = parent._gsMaxScrollX - parent.scrollLeft;
1048
1049 if (gap < 0) {
1050 changeX = gap;
1051 } else if (pointerX > rect.right - autoScrollMarginRight && gap) {
1052 checkAutoScrollBounds = true;
1053 changeX = Math.min(gap, autoScrollFactor * (1 - Math.max(0, rect.right - pointerX) / autoScrollMarginRight) | 0);
1054 } else if (pointerX < rect.left + autoScrollMarginLeft && parent.scrollLeft) {
1055 checkAutoScrollBounds = true;
1056 changeX = -Math.min(parent.scrollLeft, autoScrollFactor * (1 - Math.max(0, pointerX - rect.left) / autoScrollMarginLeft) | 0);
1057 }
1058
1059 if (changeX) {
1060 parent.scrollLeft += changeX;
1061 }
1062 }
1063
1064 if (isRoot && (changeX || changeY)) {
1065 _win.scrollTo(parent.scrollLeft, parent.scrollTop);
1066
1067 setPointerPosition(self.pointerX + changeX, self.pointerY + changeY);
1068 }
1069
1070 e = parent;
1071 }
1072 }
1073
1074 if (dirty) {
1075 var x = self.x,
1076 y = self.y;
1077
1078 if (rotationMode) {
1079 self.deltaX = x - parseFloat(gsCache.rotation);
1080 self.rotation = x;
1081 gsCache.rotation = x + "deg";
1082 gsCache.renderTransform(1, gsCache);
1083 } else {
1084 if (scrollProxy) {
1085 if (allowY) {
1086 self.deltaY = y - scrollProxy.top();
1087 scrollProxy.top(y);
1088 }
1089
1090 if (allowX) {
1091 self.deltaX = x - scrollProxy.left();
1092 scrollProxy.left(x);
1093 }
1094 } else if (xyMode) {
1095 if (allowY) {
1096 self.deltaY = y - parseFloat(gsCache.y);
1097 gsCache.y = y + "px";
1098 }
1099
1100 if (allowX) {
1101 self.deltaX = x - parseFloat(gsCache.x);
1102 gsCache.x = x + "px";
1103 }
1104
1105 gsCache.renderTransform(1, gsCache);
1106 } else {
1107 if (allowY) {
1108 self.deltaY = y - parseFloat(target.style.top || 0);
1109 target.style.top = y + "px";
1110 }
1111
1112 if (allowX) {
1113 self.deltaY = x - parseFloat(target.style.left || 0);
1114 target.style.left = x + "px";
1115 }
1116 }
1117 }
1118
1119 if (hasDragCallback && !suppressEvents && !isDispatching) {
1120 isDispatching = true; //in case onDrag has an update() call (avoid endless loop)
1121
1122 if (_dispatchEvent(self, "drag", "onDrag") === false) {
1123 if (allowX) {
1124 self.x -= self.deltaX;
1125 }
1126
1127 if (allowY) {
1128 self.y -= self.deltaY;
1129 }
1130
1131 render(true);
1132 }
1133
1134 isDispatching = false;
1135 }
1136 }
1137
1138 dirty = false;
1139 },
1140 //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 InertiaPlugin tween in an onUpdate to ensure things are synced and snapped.
1141 syncXY = function syncXY(skipOnUpdate, skipSnap) {
1142 var x = self.x,
1143 y = self.y,
1144 snappedValue,
1145 cs;
1146
1147 if (!target._gsap) {
1148 //just in case the _gsap cache got wiped, like if the user called clearProps on the transform or something (very rare).
1149 gsCache = gsap.core.getCache(target);
1150 }
1151
1152 if (xyMode) {
1153 self.x = parseFloat(gsCache.x);
1154 self.y = parseFloat(gsCache.y);
1155 } else if (rotationMode) {
1156 self.x = self.rotation = parseFloat(gsCache.rotation);
1157 } else if (scrollProxy) {
1158 self.y = scrollProxy.top();
1159 self.x = scrollProxy.left();
1160 } else {
1161 self.y = parseInt(target.style.top || (cs = _getComputedStyle(target)) && cs.top, 10) || 0;
1162 self.x = parseInt(target.style.left || (cs || {}).left, 10) || 0;
1163 }
1164
1165 if ((snapX || snapY || snapXY) && !skipSnap && (self.isDragging || self.isThrowing)) {
1166 if (snapXY) {
1167 _temp1.x = self.x;
1168 _temp1.y = self.y;
1169 snappedValue = snapXY(_temp1);
1170
1171 if (snappedValue.x !== self.x) {
1172 self.x = snappedValue.x;
1173 dirty = true;
1174 }
1175
1176 if (snappedValue.y !== self.y) {
1177 self.y = snappedValue.y;
1178 dirty = true;
1179 }
1180 }
1181
1182 if (snapX) {
1183 snappedValue = snapX(self.x);
1184
1185 if (snappedValue !== self.x) {
1186 self.x = snappedValue;
1187
1188 if (rotationMode) {
1189 self.rotation = snappedValue;
1190 }
1191
1192 dirty = true;
1193 }
1194 }
1195
1196 if (snapY) {
1197 snappedValue = snapY(self.y);
1198
1199 if (snappedValue !== self.y) {
1200 self.y = snappedValue;
1201 }
1202
1203 dirty = true;
1204 }
1205 }
1206
1207 if (dirty) {
1208 render(true);
1209 }
1210
1211 if (!skipOnUpdate) {
1212 self.deltaX = self.x - x;
1213 self.deltaY = self.y - y;
1214
1215 _dispatchEvent(self, "throwupdate", "onThrowUpdate");
1216 }
1217 },
1218 buildSnapFunc = function buildSnapFunc(snap, min, max, factor) {
1219 if (min == null) {
1220 min = -_bigNum;
1221 }
1222
1223 if (max == null) {
1224 max = _bigNum;
1225 }
1226
1227 if (_isFunction(snap)) {
1228 return function (n) {
1229 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)
1230
1231 return snap.call(self, n > max ? max + (n - max) * edgeTolerance : n < min ? min + (n - min) * edgeTolerance : n) * factor;
1232 };
1233 }
1234
1235 if (_isArray(snap)) {
1236 return function (n) {
1237 var i = snap.length,
1238 closest = 0,
1239 absDif = _bigNum,
1240 val,
1241 dif;
1242
1243 while (--i > -1) {
1244 val = snap[i];
1245 dif = val - n;
1246
1247 if (dif < 0) {
1248 dif = -dif;
1249 }
1250
1251 if (dif < absDif && val >= min && val <= max) {
1252 closest = i;
1253 absDif = dif;
1254 }
1255 }
1256
1257 return snap[closest];
1258 };
1259 }
1260
1261 return isNaN(snap) ? function (n) {
1262 return n;
1263 } : function () {
1264 return snap * factor;
1265 };
1266 },
1267 buildPointSnapFunc = function buildPointSnapFunc(snap, minX, maxX, minY, maxY, radius, factor) {
1268 radius = radius && radius < _bigNum ? radius * radius : _bigNum; //so we don't have to Math.sqrt() in the functions. Performance optimization.
1269
1270 if (_isFunction(snap)) {
1271 return function (point) {
1272 var edgeTolerance = !self.isPressed ? 1 : 1 - self.edgeResistance,
1273 x = point.x,
1274 y = point.y,
1275 result,
1276 dx,
1277 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)
1278
1279 point.x = x = x > maxX ? maxX + (x - maxX) * edgeTolerance : x < minX ? minX + (x - minX) * edgeTolerance : x;
1280 point.y = y = y > maxY ? maxY + (y - maxY) * edgeTolerance : y < minY ? minY + (y - minY) * edgeTolerance : y;
1281 result = snap.call(self, point);
1282
1283 if (result !== point) {
1284 point.x = result.x;
1285 point.y = result.y;
1286 }
1287
1288 if (factor !== 1) {
1289 point.x *= factor;
1290 point.y *= factor;
1291 }
1292
1293 if (radius < _bigNum) {
1294 dx = point.x - x;
1295 dy = point.y - y;
1296
1297 if (dx * dx + dy * dy > radius) {
1298 point.x = x;
1299 point.y = y;
1300 }
1301 }
1302
1303 return point;
1304 };
1305 }
1306
1307 if (_isArray(snap)) {
1308 return function (p) {
1309 var i = snap.length,
1310 closest = 0,
1311 minDist = _bigNum,
1312 x,
1313 y,
1314 point,
1315 dist;
1316
1317 while (--i > -1) {
1318 point = snap[i];
1319 x = point.x - p.x;
1320 y = point.y - p.y;
1321 dist = x * x + y * y;
1322
1323 if (dist < minDist) {
1324 closest = i;
1325 minDist = dist;
1326 }
1327 }
1328
1329 return minDist <= radius ? snap[closest] : p;
1330 };
1331 }
1332
1333 return function (n) {
1334 return n;
1335 };
1336 },
1337 calculateBounds = function calculateBounds() {
1338 var bounds, targetBounds, snap, snapIsRaw;
1339 hasBounds = false;
1340
1341 if (scrollProxy) {
1342 scrollProxy.calibrate();
1343 self.minX = minX = -scrollProxy.maxScrollLeft();
1344 self.minY = minY = -scrollProxy.maxScrollTop();
1345 self.maxX = maxX = self.maxY = maxY = 0;
1346 hasBounds = true;
1347 } else if (!!vars.bounds) {
1348 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}
1349
1350 if (rotationMode) {
1351 self.minX = minX = bounds.left;
1352 self.maxX = maxX = bounds.left + bounds.width;
1353 self.minY = minY = self.maxY = maxY = 0;
1354 } else if (!_isUndefined(vars.bounds.maxX) || !_isUndefined(vars.bounds.maxY)) {
1355 bounds = vars.bounds;
1356 self.minX = minX = bounds.minX;
1357 self.minY = minY = bounds.minY;
1358 self.maxX = maxX = bounds.maxX;
1359 self.maxY = maxY = bounds.maxY;
1360 } else {
1361 targetBounds = _getBounds(target, target.parentNode);
1362 self.minX = minX = Math.round(getPropAsNum(xProp, "px") + bounds.left - targetBounds.left - 0.5);
1363 self.minY = minY = Math.round(getPropAsNum(yProp, "px") + bounds.top - targetBounds.top - 0.5);
1364 self.maxX = maxX = Math.round(minX + (bounds.width - targetBounds.width));
1365 self.maxY = maxY = Math.round(minY + (bounds.height - targetBounds.height));
1366 }
1367
1368 if (minX > maxX) {
1369 self.minX = maxX;
1370 self.maxX = maxX = minX;
1371 minX = self.minX;
1372 }
1373
1374 if (minY > maxY) {
1375 self.minY = maxY;
1376 self.maxY = maxY = minY;
1377 minY = self.minY;
1378 }
1379
1380 if (rotationMode) {
1381 self.minRotation = minX;
1382 self.maxRotation = maxX;
1383 }
1384
1385 hasBounds = true;
1386 }
1387
1388 if (vars.liveSnap) {
1389 snap = vars.liveSnap === true ? vars.snap || {} : vars.liveSnap;
1390 snapIsRaw = _isArray(snap) || _isFunction(snap);
1391
1392 if (rotationMode) {
1393 snapX = buildSnapFunc(snapIsRaw ? snap : snap.rotation, minX, maxX, 1);
1394 snapY = null;
1395 } else {
1396 if (snap.points) {
1397 snapXY = buildPointSnapFunc(snapIsRaw ? snap : snap.points, minX, maxX, minY, maxY, snap.radius, scrollProxy ? -1 : 1);
1398 } else {
1399 if (allowX) {
1400 snapX = buildSnapFunc(snapIsRaw ? snap : snap.x || snap.left || snap.scrollLeft, minX, maxX, scrollProxy ? -1 : 1);
1401 }
1402
1403 if (allowY) {
1404 snapY = buildSnapFunc(snapIsRaw ? snap : snap.y || snap.top || snap.scrollTop, minY, maxY, scrollProxy ? -1 : 1);
1405 }
1406 }
1407 }
1408 }
1409 },
1410 onThrowComplete = function onThrowComplete() {
1411 self.isThrowing = false;
1412
1413 _dispatchEvent(self, "throwcomplete", "onThrowComplete");
1414 },
1415 onThrowInterrupt = function onThrowInterrupt() {
1416 self.isThrowing = false;
1417 },
1418 animate = function animate(inertia, forceZeroVelocity) {
1419 var snap, snapIsRaw, tween, overshootTolerance;
1420
1421 if (inertia && InertiaPlugin) {
1422 if (inertia === true) {
1423 snap = vars.snap || vars.liveSnap || {};
1424 snapIsRaw = _isArray(snap) || _isFunction(snap);
1425 inertia = {
1426 resistance: (vars.throwResistance || vars.resistance || 1000) / (rotationMode ? 10 : 1)
1427 };
1428
1429 if (rotationMode) {
1430 inertia.rotation = _parseInertia(self, snapIsRaw ? snap : snap.rotation, maxX, minX, 1, forceZeroVelocity);
1431 } else {
1432 if (allowX) {
1433 inertia[xProp] = _parseInertia(self, snapIsRaw ? snap : snap.points || snap.x || snap.left, maxX, minX, scrollProxy ? -1 : 1, forceZeroVelocity || self.lockedAxis === "x");
1434 }
1435
1436 if (allowY) {
1437 inertia[yProp] = _parseInertia(self, snapIsRaw ? snap : snap.points || snap.y || snap.top, maxY, minY, scrollProxy ? -1 : 1, forceZeroVelocity || self.lockedAxis === "y");
1438 }
1439
1440 if (snap.points || _isArray(snap) && _isObject(snap[0])) {
1441 inertia.linkedProps = xProp + "," + yProp;
1442 inertia.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.
1443 }
1444 }
1445 }
1446
1447 self.isThrowing = true;
1448 overshootTolerance = !isNaN(vars.overshootTolerance) ? vars.overshootTolerance : vars.edgeResistance === 1 ? 0 : 1 - self.edgeResistance + 0.2;
1449
1450 if (!inertia.duration) {
1451 inertia.duration = {
1452 max: Math.max(vars.minDuration || 0, "maxDuration" in vars ? vars.maxDuration : 2),
1453 min: !isNaN(vars.minDuration) ? vars.minDuration : overshootTolerance === 0 || _isObject(inertia) && inertia.resistance > 1000 ? 0 : 0.5,
1454 overshoot: overshootTolerance
1455 };
1456 }
1457
1458 self.tween = tween = gsap.to(scrollProxy || target, {
1459 inertia: inertia,
1460 data: "_draggable",
1461 onComplete: onThrowComplete,
1462 onInterrupt: onThrowInterrupt,
1463 onUpdate: vars.fastMode ? _dispatchEvent : syncXY,
1464 onUpdateParams: vars.fastMode ? [self, "onthrowupdate", "onThrowUpdate"] : snap && snap.radius ? [false, true] : []
1465 });
1466
1467 if (!vars.fastMode) {
1468 if (scrollProxy) {
1469 scrollProxy._skip = 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.
1470 }
1471
1472 tween.render(1e9, true, true); // force to the end. Remember, the duration will likely change upon initting because that's when InertiaPlugin calculates it.
1473
1474 syncXY(true, true);
1475 self.endX = self.x;
1476 self.endY = self.y;
1477
1478 if (rotationMode) {
1479 self.endRotation = self.x;
1480 }
1481
1482 tween.play(0);
1483 syncXY(true, true);
1484
1485 if (scrollProxy) {
1486 scrollProxy._skip = false; //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.
1487 }
1488 }
1489 } else if (hasBounds) {
1490 self.applyBounds();
1491 }
1492 },
1493 updateMatrix = function updateMatrix(shiftStart) {
1494 var start = matrix,
1495 p;
1496 matrix = getGlobalMatrix(target.parentNode, true);
1497
1498 if (shiftStart && self.isPressed && !matrix.equals(start || new Matrix2D())) {
1499 //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.
1500 p = start.inverse().apply({
1501 x: startPointerX,
1502 y: startPointerY
1503 });
1504 matrix.apply(p, p);
1505 startPointerX = p.x;
1506 startPointerY = p.y;
1507 }
1508
1509 if (matrix.equals(_identityMatrix)) {
1510 //if there are no transforms, we can optimize performance by not factoring in the matrix
1511 matrix = null;
1512 }
1513 },
1514 recordStartPositions = function recordStartPositions() {
1515 var edgeTolerance = 1 - self.edgeResistance,
1516 offsetX = isFixed ? _getDocScrollLeft(ownerDoc) : 0,
1517 offsetY = isFixed ? _getDocScrollTop(ownerDoc) : 0,
1518 parsedOrigin,
1519 x,
1520 y;
1521 updateMatrix(false);
1522
1523 if (matrix) {
1524 _point1.x = self.pointerX - offsetX;
1525 _point1.y = self.pointerY - offsetY;
1526 matrix.apply(_point1, _point1);
1527 startPointerX = _point1.x; //translate to local coordinate system
1528
1529 startPointerY = _point1.y;
1530 }
1531
1532 if (dirty) {
1533 setPointerPosition(self.pointerX, self.pointerY);
1534 render(true);
1535 }
1536
1537 if (scrollProxy) {
1538 calculateBounds();
1539 startElementY = scrollProxy.top();
1540 startElementX = scrollProxy.left();
1541 } else {
1542 //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.
1543 if (isTweening()) {
1544 syncXY(true, true);
1545 calculateBounds();
1546 } else {
1547 self.applyBounds();
1548 }
1549
1550 if (rotationMode) {
1551 parsedOrigin = target.ownerSVGElement ? [gsCache.xOrigin - target.getBBox().x, gsCache.yOrigin - target.getBBox().y] : (_getComputedStyle(target)[_transformOriginProp] || "0 0").split(" ");
1552 rotationOrigin = self.rotationOrigin = getGlobalMatrix(target).apply({
1553 x: parseFloat(parsedOrigin[0]) || 0,
1554 y: parseFloat(parsedOrigin[1]) || 0
1555 });
1556 syncXY(true, true);
1557 x = self.pointerX - rotationOrigin.x - offsetX;
1558 y = rotationOrigin.y - self.pointerY + offsetY;
1559 startElementX = self.x; //starting rotation (x always refers to rotation in type:"rotation", measured in degrees)
1560
1561 startElementY = self.y = Math.atan2(y, x) * _RAD2DEG;
1562 } else {
1563 //parent = !isFixed && target.parentNode;
1564 //startScrollTop = parent ? parent.scrollTop || 0 : 0;
1565 //startScrollLeft = parent ? parent.scrollLeft || 0 : 0;
1566 startElementY = getPropAsNum(yProp, "px"); //record the starting top and left values so that we can just add the mouse's movement to them later.
1567
1568 startElementX = getPropAsNum(xProp, "px");
1569 }
1570 }
1571
1572 if (hasBounds && edgeTolerance) {
1573 if (startElementX > maxX) {
1574 startElementX = maxX + (startElementX - maxX) / edgeTolerance;
1575 } else if (startElementX < minX) {
1576 startElementX = minX - (minX - startElementX) / edgeTolerance;
1577 }
1578
1579 if (!rotationMode) {
1580 if (startElementY > maxY) {
1581 startElementY = maxY + (startElementY - maxY) / edgeTolerance;
1582 } else if (startElementY < minY) {
1583 startElementY = minY - (minY - startElementY) / edgeTolerance;
1584 }
1585 }
1586 }
1587
1588 self.startX = startElementX;
1589 self.startY = startElementY;
1590 },
1591 isTweening = function isTweening() {
1592 return self.tween && self.tween.isActive();
1593 },
1594 removePlaceholder = function removePlaceholder() {
1595 if (_placeholderDiv.parentNode && !isTweening() && !self.isDragging) {
1596 //_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.
1597 _placeholderDiv.parentNode.removeChild(_placeholderDiv);
1598 }
1599 },
1600 //called when the mouse is pressed (or touch starts)
1601 onPress = function onPress(e, force) {
1602 var i;
1603
1604 if (!enabled || self.isPressed || !e || (e.type === "mousedown" || e.type === "pointerdown") && !force && _getTime() - clickTime < 30 && _touchEventLookup[self.pointerEvent.type]) {
1605 //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.
1606 isPreventingDefault && e && enabled && _preventDefault(e); // in some browsers, we must listen for multiple event types like touchstart, pointerdown, mousedown. The first time this function is called, we record whether or not we _preventDefault() so that on duplicate calls, we can do the same if necessary.
1607
1608 return;
1609 }
1610
1611 interrupted = isTweening();
1612 self.pointerEvent = e;
1613
1614 if (_touchEventLookup[e.type]) {
1615 //note: on iOS, BOTH touchmove and mousemove are dispatched, but the mousemove has pageY and pageX of 0 which would mess up the calculations and needlessly hurt performance.
1616 touchEventTarget = ~e.type.indexOf("touch") ? e.currentTarget || e.target : ownerDoc; //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".
1617
1618 _addListener(touchEventTarget, "touchend", onRelease);
1619
1620 _addListener(touchEventTarget, "touchmove", onMove);
1621
1622 _addListener(touchEventTarget, "touchcancel", onRelease);
1623
1624 _addListener(ownerDoc, "touchstart", _onMultiTouchDocument);
1625 } else {
1626 touchEventTarget = null;
1627
1628 _addListener(ownerDoc, "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.
1629
1630 }
1631
1632 touchDragAxis = null;
1633
1634 if (!_supportsPointer || !touchEventTarget) {
1635 _addListener(ownerDoc, "mouseup", onRelease);
1636
1637 if (e && e.target) {
1638 _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"
1639
1640 }
1641 }
1642
1643 isClicking = isClickable.call(self, e.target) && vars.dragClickables === false && !force;
1644
1645 if (isClicking) {
1646 _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.
1647
1648
1649 _dispatchEvent(self, "pressInit", "onPressInit");
1650
1651 _dispatchEvent(self, "press", "onPress");
1652
1653 _setSelectable(triggers, true); //accommodates things like inputs and elements with contentEditable="true" (otherwise user couldn't drag to select text)
1654
1655
1656 return;
1657 }
1658
1659 allowNativeTouchScrolling = !touchEventTarget || allowX === allowY || self.vars.allowNativeTouchScrolling === false || self.vars.allowContextMenu && e && (e.ctrlKey || e.which > 2) ? false : allowX ? "y" : "x"; //note: in Chrome, right-clicking (for a context menu) fires onPress and it doesn't have the event.which set properly, so we must look for event.ctrlKey. If the user wants to allow context menus we should of course sense it here and not allow native touch scrolling.
1660
1661 isPreventingDefault = !allowNativeTouchScrolling && !self.allowEventDefault;
1662
1663 if (isPreventingDefault) {
1664 _preventDefault(e);
1665
1666 _addListener(_win, "touchforcechange", _preventDefault); //works around safari bug: https://greensock.com/forums/topic/21450-draggable-in-iframe-on-mobile-is-buggy/
1667
1668 }
1669
1670 if (e.changedTouches) {
1671 //touch events store the data slightly differently
1672 e = touch = e.changedTouches[0];
1673 touchID = e.identifier;
1674 } else if (e.pointerId) {
1675 touchID = e.pointerId; //for some Microsoft browsers
1676 } else {
1677 touch = touchID = null;
1678 }
1679
1680 _dragCount++;
1681
1682 _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)
1683
1684
1685 startPointerY = self.pointerY = e.pageY; //record the starting x and y so that we can calculate the movement from the original in _onMouseMove
1686
1687 startPointerX = self.pointerX = e.pageX;
1688
1689 _dispatchEvent(self, "pressInit", "onPressInit");
1690
1691 if (allowNativeTouchScrolling || self.autoScroll) {
1692 _recordMaxScrolls(target.parentNode);
1693 }
1694
1695 if (target.parentNode && self.autoScroll && !scrollProxy && !rotationMode && target.parentNode._gsMaxScrollX && !_placeholderDiv.parentNode && !target.getBBox) {
1696 //add a placeholder div to prevent the parent container from collapsing when the user drags the element left.
1697 _placeholderDiv.style.width = target.parentNode.scrollWidth + "px";
1698 target.parentNode.appendChild(_placeholderDiv);
1699 }
1700
1701 recordStartPositions();
1702 self.tween && self.tween.kill();
1703 self.isThrowing = false;
1704 gsap.killTweensOf(scrollProxy || target, killProps, true); //in case the user tries to drag it before the last tween is done.
1705
1706 scrollProxy && gsap.killTweensOf(target, {
1707 scrollTo: 1
1708 }, true); //just in case the original target's scroll position is being tweened somewhere else.
1709
1710 self.tween = self.lockedAxis = null;
1711
1712 if (vars.zIndexBoost || !rotationMode && !scrollProxy && vars.zIndexBoost !== false) {
1713 target.style.zIndex = Draggable.zIndex++;
1714 }
1715
1716 self.isPressed = true;
1717 hasDragCallback = !!(vars.onDrag || self._listeners.drag);
1718 hasMoveCallback = !!(vars.onMove || self._listeners.move);
1719
1720 if (!rotationMode && (vars.cursor !== false || vars.activeCursor)) {
1721 i = triggers.length;
1722
1723 while (--i > -1) {
1724 //_setStyle(triggers[i], "cursor", vars.activeCursor || vars.cursor || (_defaultCursor === "grab" ? "grabbing" : _defaultCursor));
1725 gsap.set(triggers[i], {
1726 cursor: vars.activeCursor || vars.cursor || (_defaultCursor === "grab" ? "grabbing" : _defaultCursor)
1727 });
1728 }
1729 }
1730
1731 _dispatchEvent(self, "press", "onPress");
1732 },
1733 //called every time the mouse/touch moves
1734 onMove = function onMove(e) {
1735 var originalEvent = e,
1736 touches,
1737 pointerX,
1738 pointerY,
1739 i,
1740 dx,
1741 dy;
1742
1743 if (!enabled || _isMultiTouching || !self.isPressed || !e) {
1744 isPreventingDefault && e && enabled && _preventDefault(e); // in some browsers, we must listen for multiple event types like touchmove, pointermove, mousemove. The first time this function is called, we record whether or not we _preventDefault() so that on duplicate calls, we can do the same if necessary.
1745
1746 return;
1747 }
1748
1749 self.pointerEvent = e;
1750 touches = e.changedTouches;
1751
1752 if (touches) {
1753 //touch events store the data slightly differently
1754 e = touches[0];
1755
1756 if (e !== touch && e.identifier !== touchID) {
1757 //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)
1758 i = touches.length;
1759
1760 while (--i > -1 && (e = touches[i]).identifier !== touchID) {}
1761
1762 if (i < 0) {
1763 return;
1764 }
1765 }
1766 } else if (e.pointerId && touchID && e.pointerId !== touchID) {
1767 //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)
1768 return;
1769 }
1770
1771 if (touchEventTarget && allowNativeTouchScrolling && !touchDragAxis) {
1772 //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).
1773 _point1.x = e.pageX;
1774 _point1.y = e.pageY;
1775 matrix && matrix.apply(_point1, _point1);
1776 pointerX = _point1.x;
1777 pointerY = _point1.y;
1778 dx = Math.abs(pointerX - startPointerX);
1779 dy = Math.abs(pointerY - startPointerY);
1780
1781 if (dx !== dy && (dx > minimumMovement || dy > minimumMovement) || _isAndroid && allowNativeTouchScrolling === touchDragAxis) {
1782 touchDragAxis = dx > dy && allowX ? "x" : "y";
1783
1784 if (allowNativeTouchScrolling && touchDragAxis !== allowNativeTouchScrolling) {
1785 _addListener(_win, "touchforcechange", _preventDefault); // prevents native touch scrolling from taking over if the user started dragging in the other direction in iOS Safari
1786
1787 }
1788
1789 if (self.vars.lockAxisOnTouchScroll !== false && allowX && allowY) {
1790 self.lockedAxis = touchDragAxis === "x" ? "y" : "x";
1791 _isFunction(self.vars.onLockAxis) && self.vars.onLockAxis.call(self, originalEvent);
1792 }
1793
1794 if (_isAndroid && allowNativeTouchScrolling === touchDragAxis) {
1795 onRelease(originalEvent);
1796 return;
1797 }
1798 }
1799 }
1800
1801 if (!self.allowEventDefault && (!allowNativeTouchScrolling || touchDragAxis && allowNativeTouchScrolling !== touchDragAxis) && originalEvent.cancelable !== false) {
1802 _preventDefault(originalEvent);
1803
1804 isPreventingDefault = true;
1805 } else if (isPreventingDefault) {
1806 isPreventingDefault = false;
1807 }
1808
1809 if (self.autoScroll) {
1810 checkAutoScrollBounds = true;
1811 }
1812
1813 setPointerPosition(e.pageX, e.pageY, hasMoveCallback);
1814 },
1815 setPointerPosition = function setPointerPosition(pointerX, pointerY, invokeOnMove) {
1816 var dragTolerance = 1 - self.dragResistance,
1817 edgeTolerance = 1 - self.edgeResistance,
1818 prevPointerX = self.pointerX,
1819 prevPointerY = self.pointerY,
1820 prevStartElementY = startElementY,
1821 prevX = self.x,
1822 prevY = self.y,
1823 prevEndX = self.endX,
1824 prevEndY = self.endY,
1825 prevEndRotation = self.endRotation,
1826 prevDirty = dirty,
1827 xChange,
1828 yChange,
1829 x,
1830 y,
1831 dif,
1832 temp;
1833 self.pointerX = pointerX;
1834 self.pointerY = pointerY;
1835
1836 if (isFixed) {
1837 pointerX -= _getDocScrollLeft(ownerDoc);
1838 pointerY -= _getDocScrollTop(ownerDoc);
1839 }
1840
1841 if (rotationMode) {
1842 y = Math.atan2(rotationOrigin.y - pointerY, pointerX - rotationOrigin.x) * _RAD2DEG;
1843 dif = self.y - y;
1844
1845 if (dif > 180) {
1846 startElementY -= 360;
1847 self.y = y;
1848 } else if (dif < -180) {
1849 startElementY += 360;
1850 self.y = y;
1851 }
1852
1853 if (self.x !== startElementX || Math.abs(startElementY - y) > minimumMovement) {
1854 self.y = y;
1855 x = startElementX + (startElementY - y) * dragTolerance;
1856 } else {
1857 x = startElementX;
1858 }
1859 } else {
1860 if (matrix) {
1861 temp = pointerX * matrix.a + pointerY * matrix.c + matrix.e;
1862 pointerY = pointerX * matrix.b + pointerY * matrix.d + matrix.f;
1863 pointerX = temp;
1864 }
1865
1866 yChange = pointerY - startPointerY;
1867 xChange = pointerX - startPointerX;
1868
1869 if (yChange < minimumMovement && yChange > -minimumMovement) {
1870 yChange = 0;
1871 }
1872
1873 if (xChange < minimumMovement && xChange > -minimumMovement) {
1874 xChange = 0;
1875 }
1876
1877 if ((self.lockAxis || self.lockedAxis) && (xChange || yChange)) {
1878 temp = self.lockedAxis;
1879
1880 if (!temp) {
1881 self.lockedAxis = temp = allowX && Math.abs(xChange) > Math.abs(yChange) ? "y" : allowY ? "x" : null;
1882
1883 if (temp && _isFunction(self.vars.onLockAxis)) {
1884 self.vars.onLockAxis.call(self, self.pointerEvent);
1885 }
1886 }
1887
1888 if (temp === "y") {
1889 yChange = 0;
1890 } else if (temp === "x") {
1891 xChange = 0;
1892 }
1893 }
1894
1895 x = _round(startElementX + xChange * dragTolerance);
1896 y = _round(startElementY + yChange * dragTolerance);
1897 }
1898
1899 if ((snapX || snapY || snapXY) && (self.x !== x || self.y !== y && !rotationMode)) {
1900 if (snapXY) {
1901 _temp1.x = x;
1902 _temp1.y = y;
1903 temp = snapXY(_temp1);
1904 x = _round(temp.x);
1905 y = _round(temp.y);
1906 }
1907
1908 if (snapX) {
1909 x = _round(snapX(x));
1910 }
1911
1912 if (snapY) {
1913 y = _round(snapY(y));
1914 }
1915 } else if (hasBounds) {
1916 if (x > maxX) {
1917 x = maxX + Math.round((x - maxX) * edgeTolerance);
1918 } else if (x < minX) {
1919 x = minX + Math.round((x - minX) * edgeTolerance);
1920 }
1921
1922 if (!rotationMode) {
1923 if (y > maxY) {
1924 y = Math.round(maxY + (y - maxY) * edgeTolerance);
1925 } else if (y < minY) {
1926 y = Math.round(minY + (y - minY) * edgeTolerance);
1927 }
1928 }
1929 }
1930
1931 if (self.x !== x || self.y !== y && !rotationMode) {
1932 if (rotationMode) {
1933 self.endRotation = self.x = self.endX = x;
1934 dirty = true;
1935 } else {
1936 if (allowY) {
1937 self.y = self.endY = y;
1938 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)
1939 }
1940
1941 if (allowX) {
1942 self.x = self.endX = x;
1943 dirty = true;
1944 }
1945 }
1946
1947 if (!invokeOnMove || _dispatchEvent(self, "move", "onMove") !== false) {
1948 if (!self.isDragging && self.isPressed) {
1949 self.isDragging = true;
1950
1951 _dispatchEvent(self, "dragstart", "onDragStart");
1952 }
1953 } else {
1954 //revert because the onMove returned false!
1955 self.pointerX = prevPointerX;
1956 self.pointerY = prevPointerY;
1957 startElementY = prevStartElementY;
1958 self.x = prevX;
1959 self.y = prevY;
1960 self.endX = prevEndX;
1961 self.endY = prevEndY;
1962 self.endRotation = prevEndRotation;
1963 dirty = prevDirty;
1964 }
1965 }
1966 },
1967 //called when the mouse/touch is released
1968 onRelease = function onRelease(e, force) {
1969 if (!enabled || !self.isPressed || e && touchID != null && !force && (e.pointerId && e.pointerId !== touchID || e.changedTouches && !_hasTouchID(e.changedTouches, touchID))) {
1970 //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)
1971 isPreventingDefault && e && enabled && _preventDefault(e); // in some browsers, we must listen for multiple event types like touchend, pointerup, mouseup. The first time this function is called, we record whether or not we _preventDefault() so that on duplicate calls, we can do the same if necessary.
1972
1973 return;
1974 }
1975
1976 self.isPressed = false;
1977 var originalEvent = e,
1978 wasDragging = self.isDragging,
1979 isContextMenuRelease = self.vars.allowContextMenu && e && (e.ctrlKey || e.which > 2),
1980 placeholderDelayedCall = gsap.delayedCall(0.001, removePlaceholder),
1981 touches,
1982 i,
1983 syntheticEvent,
1984 eventTarget,
1985 syntheticClick;
1986
1987 if (touchEventTarget) {
1988 _removeListener(touchEventTarget, "touchend", onRelease);
1989
1990 _removeListener(touchEventTarget, "touchmove", onMove);
1991
1992 _removeListener(touchEventTarget, "touchcancel", onRelease);
1993
1994 _removeListener(ownerDoc, "touchstart", _onMultiTouchDocument);
1995 } else {
1996 _removeListener(ownerDoc, "mousemove", onMove);
1997 }
1998
1999 _removeListener(_win, "touchforcechange", _preventDefault);
2000
2001 if (!_supportsPointer || !touchEventTarget) {
2002 _removeListener(ownerDoc, "mouseup", onRelease);
2003
2004 if (e && e.target) {
2005 _removeListener(e.target, "mouseup", onRelease);
2006 }
2007 }
2008
2009 dirty = false;
2010
2011 if (isClicking && !isContextMenuRelease) {
2012 if (e) {
2013 _removeListener(e.target, "change", onRelease);
2014
2015 self.pointerEvent = originalEvent;
2016 }
2017
2018 _setSelectable(triggers, false);
2019
2020 _dispatchEvent(self, "release", "onRelease");
2021
2022 _dispatchEvent(self, "click", "onClick");
2023
2024 isClicking = false;
2025 return;
2026 }
2027
2028 _removeFromRenderQueue(render);
2029
2030 if (!rotationMode) {
2031 i = triggers.length;
2032
2033 while (--i > -1) {
2034 _setStyle(triggers[i], "cursor", vars.cursor || (vars.cursor !== false ? _defaultCursor : null));
2035 }
2036 }
2037
2038 if (wasDragging) {
2039 dragEndTime = _lastDragTime = _getTime();
2040 self.isDragging = false;
2041 }
2042
2043 _dragCount--;
2044
2045 if (e) {
2046 touches = e.changedTouches;
2047
2048 if (touches) {
2049 //touch events store the data slightly differently
2050 e = touches[0];
2051
2052 if (e !== touch && e.identifier !== touchID) {
2053 //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)
2054 i = touches.length;
2055
2056 while (--i > -1 && (e = touches[i]).identifier !== touchID) {}
2057
2058 if (i < 0) {
2059 return;
2060 }
2061 }
2062 }
2063
2064 self.pointerEvent = originalEvent;
2065 self.pointerX = e.pageX;
2066 self.pointerY = e.pageY;
2067 }
2068
2069 if (isContextMenuRelease && originalEvent) {
2070 _preventDefault(originalEvent);
2071
2072 isPreventingDefault = true;
2073
2074 _dispatchEvent(self, "release", "onRelease");
2075 } else if (originalEvent && !wasDragging) {
2076 isPreventingDefault = false;
2077
2078 if (interrupted && (vars.snap || vars.bounds)) {
2079 //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)
2080 animate(vars.inertia || vars.throwProps);
2081 }
2082
2083 _dispatchEvent(self, "release", "onRelease");
2084
2085 if ((!_isAndroid || originalEvent.type !== "touchmove") && originalEvent.type.indexOf("cancel") === -1) {
2086 //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".
2087 _dispatchEvent(self, "click", "onClick");
2088
2089 if (_getTime() - clickTime < 300) {
2090 _dispatchEvent(self, "doubleclick", "onDoubleClick");
2091 }
2092
2093 eventTarget = originalEvent.target || target; //old IE uses srcElement
2094
2095 clickTime = _getTime();
2096
2097 syntheticClick = function syntheticClick() {
2098 // 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.
2099 if (clickTime !== clickDispatch && self.enabled() && !self.isPressed && !originalEvent.defaultPrevented) {
2100 if (eventTarget.click) {
2101 //some browsers (like mobile Safari) don't properly trigger the click event
2102 eventTarget.click();
2103 } else if (ownerDoc.createEvent) {
2104 syntheticEvent = ownerDoc.createEvent("MouseEvents");
2105 syntheticEvent.initMouseEvent("click", true, true, _win, 1, self.pointerEvent.screenX, self.pointerEvent.screenY, self.pointerX, self.pointerY, false, false, false, false, 0, null);
2106 eventTarget.dispatchEvent(syntheticEvent);
2107 }
2108 }
2109 };
2110
2111 if (!_isAndroid && !originalEvent.defaultPrevented) {
2112 //iOS Safari requires the synthetic click to happen immediately or else it simply won't work, but Android doesn't play nice.
2113 gsap.delayedCall(0.05, 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. Don't fire it too fast (like 0.00001) because we want to give the native event a chance to fire first as it's "trusted".
2114 }
2115 }
2116 } else {
2117 animate(vars.inertia || vars.throwProps); //will skip if inertia/throwProps isn't defined or IntertiaPlugin isn't loaded.
2118
2119 if (!self.allowEventDefault && originalEvent && (vars.dragClickables !== false || !isClickable.call(self, originalEvent.target)) && wasDragging && (!allowNativeTouchScrolling || touchDragAxis && allowNativeTouchScrolling === touchDragAxis) && originalEvent.cancelable !== false) {
2120 isPreventingDefault = true;
2121
2122 _preventDefault(originalEvent);
2123 } else {
2124 isPreventingDefault = false;
2125 }
2126
2127 _dispatchEvent(self, "release", "onRelease");
2128 }
2129
2130 isTweening() && placeholderDelayedCall.duration(self.tween.duration()); //sync the timing so that the placeholder DIV gets
2131
2132 wasDragging && _dispatchEvent(self, "dragend", "onDragEnd");
2133 return true;
2134 },
2135 updateScroll = function updateScroll(e) {
2136 if (e && self.isDragging && !scrollProxy) {
2137 var parent = e.target || target.parentNode,
2138 deltaX = parent.scrollLeft - parent._gsScrollX,
2139 deltaY = parent.scrollTop - parent._gsScrollY;
2140
2141 if (deltaX || deltaY) {
2142 if (matrix) {
2143 startPointerX -= deltaX * matrix.a + deltaY * matrix.c;
2144 startPointerY -= deltaY * matrix.d + deltaX * matrix.b;
2145 } else {
2146 startPointerX -= deltaX;
2147 startPointerY -= deltaY;
2148 }
2149
2150 parent._gsScrollX += deltaX;
2151 parent._gsScrollY += deltaY;
2152 setPointerPosition(self.pointerX, self.pointerY);
2153 }
2154 }
2155 },
2156 onClick = function onClick(e) {
2157 //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.
2158 var time = _getTime(),
2159 recentlyClicked = time - clickTime < 40,
2160 recentlyDragged = time - dragEndTime < 40,
2161 alreadyDispatched = recentlyClicked && clickDispatch === clickTime,
2162 defaultPrevented = self.pointerEvent && self.pointerEvent.defaultPrevented,
2163 alreadyDispatchedTrusted = recentlyClicked && trustedClickDispatch === clickTime,
2164 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?
2165
2166
2167 if ((alreadyDispatched || recentlyDragged && self.vars.suppressClickOnDrag !== false) && e.stopImmediatePropagation) {
2168 e.stopImmediatePropagation();
2169 }
2170
2171 if (recentlyClicked && !(self.pointerEvent && self.pointerEvent.defaultPrevented) && (!alreadyDispatched || trusted && !alreadyDispatchedTrusted)) {
2172 //let the first click pass through unhindered. Let the next one only if it's trusted, then no more (stop quick-succession ones)
2173 if (trusted && alreadyDispatched) {
2174 trustedClickDispatch = clickTime;
2175 }
2176
2177 clickDispatch = clickTime;
2178 return;
2179 }
2180
2181 if (self.isPressed || recentlyDragged || recentlyClicked) {
2182 if (!trusted || !e.detail || !recentlyClicked || defaultPrevented) {
2183 _preventDefault(e);
2184 }
2185 }
2186 },
2187 localizePoint = function localizePoint(p) {
2188 return matrix ? {
2189 x: p.x * matrix.a + p.y * matrix.c + matrix.e,
2190 y: p.x * matrix.b + p.y * matrix.d + matrix.f
2191 } : {
2192 x: p.x,
2193 y: p.y
2194 };
2195 };
2196
2197 old = Draggable.get(target);
2198 old && old.kill(); // avoids duplicates (an element can only be controlled by one Draggable)
2199 //give the user access to start/stop dragging...
2200
2201 _this2.startDrag = function (event, align) {
2202 var r1, r2, p1, p2;
2203 onPress(event || self.pointerEvent, true); //if the pointer isn't on top of the element, adjust things accordingly
2204
2205 if (align && !self.hitTest(event || self.pointerEvent)) {
2206 r1 = _parseRect(event || self.pointerEvent);
2207 r2 = _parseRect(target);
2208 p1 = localizePoint({
2209 x: r1.left + r1.width / 2,
2210 y: r1.top + r1.height / 2
2211 });
2212 p2 = localizePoint({
2213 x: r2.left + r2.width / 2,
2214 y: r2.top + r2.height / 2
2215 });
2216 startPointerX -= p1.x - p2.x;
2217 startPointerY -= p1.y - p2.y;
2218 }
2219
2220 if (!self.isDragging) {
2221 self.isDragging = true;
2222
2223 _dispatchEvent(self, "dragstart", "onDragStart");
2224 }
2225 };
2226
2227 _this2.drag = onMove;
2228
2229 _this2.endDrag = function (e) {
2230 return onRelease(e || self.pointerEvent, true);
2231 };
2232
2233 _this2.timeSinceDrag = function () {
2234 return self.isDragging ? 0 : (_getTime() - dragEndTime) / 1000;
2235 };
2236
2237 _this2.timeSinceClick = function () {
2238 return (_getTime() - clickTime) / 1000;
2239 };
2240
2241 _this2.hitTest = function (target, threshold) {
2242 return Draggable.hitTest(self.target, target, threshold);
2243 };
2244
2245 _this2.getDirection = function (from, diagonalThreshold) {
2246 //from can be "start" (default), "velocity", or an element
2247 var mode = from === "velocity" && InertiaPlugin ? from : _isObject(from) && !rotationMode ? "element" : "start",
2248 xChange,
2249 yChange,
2250 ratio,
2251 direction,
2252 r1,
2253 r2;
2254
2255 if (mode === "element") {
2256 r1 = _parseRect(self.target);
2257 r2 = _parseRect(from);
2258 }
2259
2260 xChange = mode === "start" ? self.x - startElementX : mode === "velocity" ? InertiaPlugin.getVelocity(target, xProp) : r1.left + r1.width / 2 - (r2.left + r2.width / 2);
2261
2262 if (rotationMode) {
2263 return xChange < 0 ? "counter-clockwise" : "clockwise";
2264 } else {
2265 diagonalThreshold = diagonalThreshold || 2;
2266 yChange = mode === "start" ? self.y - startElementY : mode === "velocity" ? InertiaPlugin.getVelocity(target, yProp) : r1.top + r1.height / 2 - (r2.top + r2.height / 2);
2267 ratio = Math.abs(xChange / yChange);
2268 direction = ratio < 1 / diagonalThreshold ? "" : xChange < 0 ? "left" : "right";
2269
2270 if (ratio < diagonalThreshold) {
2271 if (direction !== "") {
2272 direction += "-";
2273 }
2274
2275 direction += yChange < 0 ? "up" : "down";
2276 }
2277 }
2278
2279 return direction;
2280 };
2281
2282 _this2.applyBounds = function (newBounds, sticky) {
2283 var x, y, forceZeroVelocity, e, parent, isRoot;
2284
2285 if (newBounds && vars.bounds !== newBounds) {
2286 vars.bounds = newBounds;
2287 return self.update(true, sticky);
2288 }
2289
2290 syncXY(true);
2291 calculateBounds();
2292
2293 if (hasBounds && !isTweening()) {
2294 x = self.x;
2295 y = self.y;
2296
2297 if (x > maxX) {
2298 x = maxX;
2299 } else if (x < minX) {
2300 x = minX;
2301 }
2302
2303 if (y > maxY) {
2304 y = maxY;
2305 } else if (y < minY) {
2306 y = minY;
2307 }
2308
2309 if (self.x !== x || self.y !== y) {
2310 forceZeroVelocity = true;
2311 self.x = self.endX = x;
2312
2313 if (rotationMode) {
2314 self.endRotation = x;
2315 } else {
2316 self.y = self.endY = y;
2317 }
2318
2319 dirty = true;
2320 render(true);
2321
2322 if (self.autoScroll && !self.isDragging) {
2323 _recordMaxScrolls(target.parentNode);
2324
2325 e = target;
2326 _windowProxy.scrollTop = _win.pageYOffset != null ? _win.pageYOffset : ownerDoc.documentElement.scrollTop != null ? ownerDoc.documentElement.scrollTop : ownerDoc.body.scrollTop;
2327 _windowProxy.scrollLeft = _win.pageXOffset != null ? _win.pageXOffset : ownerDoc.documentElement.scrollLeft != null ? ownerDoc.documentElement.scrollLeft : ownerDoc.body.scrollLeft;
2328
2329 while (e && !isRoot) {
2330 //walk up the chain and sense wherever the scrollTop/scrollLeft exceeds the maximum.
2331 isRoot = _isRoot(e.parentNode);
2332 parent = isRoot ? _windowProxy : e.parentNode;
2333
2334 if (allowY && parent.scrollTop > parent._gsMaxScrollY) {
2335 parent.scrollTop = parent._gsMaxScrollY;
2336 }
2337
2338 if (allowX && parent.scrollLeft > parent._gsMaxScrollX) {
2339 parent.scrollLeft = parent._gsMaxScrollX;
2340 }
2341
2342 e = parent;
2343 }
2344 }
2345 }
2346
2347 if (self.isThrowing && (forceZeroVelocity || self.endX > maxX || self.endX < minX || self.endY > maxY || self.endY < minY)) {
2348 animate(vars.inertia || vars.throwProps, forceZeroVelocity);
2349 }
2350 }
2351
2352 return self;
2353 };
2354
2355 _this2.update = function (applyBounds, sticky, ignoreExternalChanges) {
2356 var x = self.x,
2357 y = self.y;
2358 updateMatrix(!sticky);
2359
2360 if (applyBounds) {
2361 self.applyBounds();
2362 } else {
2363 if (dirty && ignoreExternalChanges) {
2364 render(true);
2365 }
2366
2367 syncXY(true);
2368 }
2369
2370 if (sticky) {
2371 setPointerPosition(self.pointerX, self.pointerY);
2372 dirty && render(true);
2373 }
2374
2375 if (self.isPressed && !sticky && (allowX && Math.abs(x - self.x) > 0.01 || allowY && Math.abs(y - self.y) > 0.01 && !rotationMode)) {
2376 recordStartPositions();
2377 }
2378
2379 if (self.autoScroll) {
2380 _recordMaxScrolls(target.parentNode, self.isDragging);
2381
2382 checkAutoScrollBounds = self.isDragging;
2383 render(true); //in case reparenting occurred.
2384
2385 _removeScrollListener(target, updateScroll);
2386
2387 _addScrollListener(target, updateScroll);
2388 }
2389
2390 return self;
2391 };
2392
2393 _this2.enable = function (type) {
2394 var setVars = {
2395 lazy: true
2396 },
2397 id,
2398 i,
2399 trigger;
2400
2401 if (!rotationMode && vars.cursor !== false) {
2402 setVars.cursor = vars.cursor || _defaultCursor;
2403 }
2404
2405 if (gsap.utils.checkPrefix("touchCallout")) {
2406 setVars.touchCallout = "none";
2407 }
2408
2409 setVars.touchAction = allowX === allowY ? "none" : vars.allowNativeTouchScrolling || vars.allowEventDefault ? "manipulation" : allowX ? "pan-y" : "pan-x";
2410
2411 if (type !== "soft") {
2412 i = triggers.length;
2413
2414 while (--i > -1) {
2415 trigger = triggers[i];
2416 _supportsPointer || _addListener(trigger, "mousedown", onPress);
2417
2418 _addListener(trigger, "touchstart", onPress);
2419
2420 _addListener(trigger, "click", onClick, true); //note: used to pass true for capture but it prevented click-to-play-video functionality in Firefox.
2421
2422
2423 gsap.set(trigger, setVars);
2424
2425 if (trigger.getBBox && trigger.ownerSVGElement) {
2426 // a bug in chrome doesn't respect touch-action on SVG elements - it only works if we set it on the parent SVG.
2427 gsap.set(trigger.ownerSVGElement, {
2428 touchAction: allowX === allowY ? "none" : vars.allowNativeTouchScrolling || vars.allowEventDefault ? "manipulation" : allowX ? "pan-y" : "pan-x"
2429 });
2430 }
2431
2432 vars.allowContextMenu || _addListener(trigger, "contextmenu", onContextMenu);
2433 }
2434
2435 _setSelectable(triggers, false);
2436 }
2437
2438 _addScrollListener(target, updateScroll);
2439
2440 enabled = true;
2441
2442 if (InertiaPlugin && type !== "soft") {
2443 InertiaPlugin.track(scrollProxy || target, xyMode ? "x,y" : rotationMode ? "rotation" : "top,left");
2444 }
2445
2446 target._gsDragID = id = "d" + _lookupCount++;
2447 _lookup[id] = self;
2448
2449 if (scrollProxy) {
2450 scrollProxy.enable();
2451 scrollProxy.element._gsDragID = id;
2452 }
2453
2454 (vars.bounds || rotationMode) && recordStartPositions();
2455 vars.bounds && self.applyBounds();
2456 return self;
2457 };
2458
2459 _this2.disable = function (type) {
2460 var dragging = self.isDragging,
2461 i,
2462 trigger;
2463
2464 if (!rotationMode) {
2465 i = triggers.length;
2466
2467 while (--i > -1) {
2468 _setStyle(triggers[i], "cursor", null);
2469 }
2470 }
2471
2472 if (type !== "soft") {
2473 i = triggers.length;
2474
2475 while (--i > -1) {
2476 trigger = triggers[i];
2477
2478 _setStyle(trigger, "touchCallout", null);
2479
2480 _setStyle(trigger, "touchAction", null);
2481
2482 _removeListener(trigger, "mousedown", onPress);
2483
2484 _removeListener(trigger, "touchstart", onPress);
2485
2486 _removeListener(trigger, "click", onClick);
2487
2488 _removeListener(trigger, "contextmenu", onContextMenu);
2489 }
2490
2491 _setSelectable(triggers, true);
2492
2493 if (touchEventTarget) {
2494 _removeListener(touchEventTarget, "touchcancel", onRelease);
2495
2496 _removeListener(touchEventTarget, "touchend", onRelease);
2497
2498 _removeListener(touchEventTarget, "touchmove", onMove);
2499 }
2500
2501 _removeListener(ownerDoc, "mouseup", onRelease);
2502
2503 _removeListener(ownerDoc, "mousemove", onMove);
2504 }
2505
2506 _removeScrollListener(target, updateScroll);
2507
2508 enabled = false;
2509
2510 if (InertiaPlugin && type !== "soft") {
2511 InertiaPlugin.untrack(scrollProxy || target, xyMode ? "x,y" : rotationMode ? "rotation" : "top,left");
2512 }
2513
2514 if (scrollProxy) {
2515 scrollProxy.disable();
2516 }
2517
2518 _removeFromRenderQueue(render);
2519
2520 self.isDragging = self.isPressed = isClicking = false;
2521
2522 if (dragging) {
2523 _dispatchEvent(self, "dragend", "onDragEnd");
2524 }
2525
2526 return self;
2527 };
2528
2529 _this2.enabled = function (value, type) {
2530 return arguments.length ? value ? self.enable(type) : self.disable(type) : enabled;
2531 };
2532
2533 _this2.kill = function () {
2534 self.isThrowing = false;
2535
2536 if (self.tween) {
2537 self.tween.kill();
2538 }
2539
2540 self.disable();
2541 gsap.set(triggers, {
2542 clearProps: "userSelect"
2543 });
2544 delete _lookup[target._gsDragID];
2545 return self;
2546 };
2547
2548 if (~type.indexOf("scroll")) {
2549 scrollProxy = _this2.scrollProxy = new ScrollProxy(target, _extend({
2550 onKill: function onKill() {
2551 //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.
2552 if (self.isPressed) {
2553 onRelease(null);
2554 }
2555 }
2556 }, vars)); //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)
2557
2558 target.style.overflowY = allowY && !_isTouchDevice ? "auto" : "hidden";
2559 target.style.overflowX = allowX && !_isTouchDevice ? "auto" : "hidden";
2560 target = scrollProxy.content;
2561 }
2562
2563 if (rotationMode) {
2564 killProps.rotation = 1;
2565 } else {
2566 if (allowX) {
2567 killProps[xProp] = 1;
2568 }
2569
2570 if (allowY) {
2571 killProps[yProp] = 1;
2572 }
2573 }
2574
2575 gsCache.force3D = "force3D" in vars ? vars.force3D : true; //otherwise, normal dragging would be in 2D and then as soon as it's released and there's an inertia tween, it'd jump to 3D which can create an initial jump due to the work the browser must to do layerize it.
2576
2577 _this2.enable();
2578
2579 return _this2;
2580 }
2581
2582 Draggable.register = function register(core) {
2583 gsap = core;
2584
2585 _initCore();
2586 };
2587
2588 Draggable.create = function create(targets, vars) {
2589 if (!_coreInitted) {
2590 _initCore(true);
2591 }
2592
2593 return _toArray(targets).map(function (target) {
2594 return new Draggable(target, vars);
2595 });
2596 };
2597
2598 Draggable.get = function get(target) {
2599 return _lookup[(_toArray(target)[0] || {})._gsDragID];
2600 };
2601
2602 Draggable.timeSinceDrag = function timeSinceDrag() {
2603 return (_getTime() - _lastDragTime) / 1000;
2604 };
2605
2606 Draggable.hitTest = function hitTest(obj1, obj2, threshold) {
2607 if (obj1 === obj2) {
2608 return false;
2609 }
2610
2611 var r1 = _parseRect(obj1),
2612 r2 = _parseRect(obj2),
2613 top = r1.top,
2614 left = r1.left,
2615 right = r1.right,
2616 bottom = r1.bottom,
2617 width = r1.width,
2618 height = r1.height,
2619 isOutside = r2.left > right || r2.right < left || r2.top > bottom || r2.bottom < top,
2620 overlap,
2621 area,
2622 isRatio;
2623
2624 if (isOutside || !threshold) {
2625 return !isOutside;
2626 }
2627
2628 isRatio = (threshold + "").indexOf("%") !== -1;
2629 threshold = parseFloat(threshold) || 0;
2630 overlap = {
2631 left: Math.max(left, r2.left),
2632 top: Math.max(top, r2.top)
2633 };
2634 overlap.width = Math.min(right, r2.right) - overlap.left;
2635 overlap.height = Math.min(bottom, r2.bottom) - overlap.top;
2636
2637 if (overlap.width < 0 || overlap.height < 0) {
2638 return false;
2639 }
2640
2641 if (isRatio) {
2642 threshold *= 0.01;
2643 area = overlap.width * overlap.height;
2644 return area >= width * height * threshold || area >= r2.width * r2.height * threshold;
2645 }
2646
2647 return overlap.width > threshold && overlap.height > threshold;
2648 };
2649
2650 return Draggable;
2651}(EventDispatcher);
2652
2653_setDefaults(Draggable.prototype, {
2654 pointerX: 0,
2655 pointerY: 0,
2656 startX: 0,
2657 startY: 0,
2658 deltaX: 0,
2659 deltaY: 0,
2660 isDragging: false,
2661 isPressed: false
2662});
2663
2664Draggable.zIndex = 1000;
2665Draggable.version = "3.5.1";
2666_getGSAP() && gsap.registerPlugin(Draggable);
2667export { Draggable as default };
\No newline at end of file