diff --git a/dist/iwmlib.pixi.js b/dist/iwmlib.pixi.js index 8d0f0a8..db33c04 100644 --- a/dist/iwmlib.pixi.js +++ b/dist/iwmlib.pixi.js @@ -13690,6 +13690,8 @@ * left, center and right. * @param {string} [opts.verticalAlign=middle] - The vertical position of the items. Possible values are * top, middle and bottom. + * @param {PIXI.Application} [opts.app] - The PixiJS Application. Must be set if you want to use the mousewheel to + * scroll your list. */ constructor(items = [], opts = {}) { @@ -13702,7 +13704,8 @@ align: 'left', verticalAlign: 'middle', width: null, - height: null + height: null, + app: null }, opts); this.__items = items; @@ -13748,6 +13751,22 @@ this.on('pointercancel', this.onEnd.bind(this)); this.on('pointerout', this.onEnd.bind(this)); this.on('pointerupoutside', this.onEnd.bind(this)); + this.on('scroll', this.onScroll.bind(this)); + + // mousewheel + //-------------------- + if (this.opts.app) { + const app = this.opts.app; + app.view.addEventListener('mousewheel', event => { + event.preventDefault(); + const bounds = this.mask ? this.mask.getBounds() : this.getBounds(); + const x = event.clientX - app.view.getBoundingClientRect().left; + const y = event.clientY - app.view.getBoundingClientRect().top; + if (bounds.contains(x, y)) { + this.emit('scroll', event); + } + }); + } this.layout(); @@ -13975,13 +13994,40 @@ } } + /** + * + * @private + * @param {*} event + */ + onScroll(event) { + + this.capture(event); + + if (this.opts.orientation === 'horizontal') { + this.container.position.x -= event.deltaX; + if (this.container.position.x > 0) { + this.container.position.x = 0; + } else if (this.container.position.x + this.innerWidth < this.opts.width) { + this.container.position.x = this.opts.width - this.innerWidth; + } + } else { + this.container.position.y -= event.deltaY; + if (this.container.position.y > 0) { + this.container.position.y = 0; + } else if (this.container.position.y + this.innerHeight < this.opts.height) { + this.container.position.y = this.opts.height - this.innerHeight; + } + } + } + /** * Captures an event to inform InteractionMapper about processed events. * * @param {event|PIXI.InteractionEvent} event - The PIXI event to capture. */ capture(event) { - Events$1.capturedBy(event.data.originalEvent, this); + const originalEvent = event.data && event.data.originalEvent ? event.data.originalEvent : event; + Events$1.capturedBy(originalEvent, this); } } diff --git a/lib/3rdparty/gsap/docs.html b/lib/3rdparty/gsap/docs.html new file mode 100755 index 0000000..6d2c482 --- /dev/null +++ b/lib/3rdparty/gsap/docs.html @@ -0,0 +1,10 @@ + + + + + +JavaScript Documentation + + + + diff --git a/lib/3rdparty/gsap/getting_started.html b/lib/3rdparty/gsap/getting_started.html new file mode 100755 index 0000000..bd9e646 --- /dev/null +++ b/lib/3rdparty/gsap/getting_started.html @@ -0,0 +1,11 @@ + + + + +Getting Started with the JavaScript Version of the GreenSock Animation Platform + + +
REDIRECTING...
+View the most recent "getting started" guide at greensock.com + + diff --git a/lib/3rdparty/gsap/src/bonus-files-for-npm-users/CustomBounce.js b/lib/3rdparty/gsap/src/bonus-files-for-npm-users/CustomBounce.js new file mode 100644 index 0000000..9dce2ab --- /dev/null +++ b/lib/3rdparty/gsap/src/bonus-files-for-npm-users/CustomBounce.js @@ -0,0 +1,152 @@ +/*! + * VERSION: 0.2.1 + * DATE: 2018-08-27 + * UPDATES AND DOCS AT: http://greensock.com + * + * @license Copyright (c) 2008-2019, GreenSock. All rights reserved. + * This work is subject to the terms at http://greensock.com/standard-license or for + * Club GreenSock members, the software agreement that was issued with your membership. + * + * @author: Jack Doyle, jack@greensock.com + **/ +/* eslint-disable */ + +import { _gsScope, globals } from "gsap/TweenLite.js"; +import CustomEase from "./CustomEase.js"; + +_gsScope._gsDefine("easing.CustomBounce", ["easing.CustomEase"], function() { + + + var _normalizeX = function (a) { //scales all the x values in an array [x, y, x, y...] AND rounds them to the closest hundredth (decimal) + var l = a.length, + s = 1 / a[l - 2], + rnd = 1000, + i; + for (i = 2; i < l; i += 2) { + a[i] = ((a[i] * s * rnd) | 0) / rnd; + } + a[l - 2] = 1; //in case there are any rounding errors. x should always end at 1. + }, + + CustomBounce = function(id, vars) { + this.vars = vars = vars || {}; + if (vars.squash) { + this.squash = new CustomEase(vars.squashID || (id + "-squash")); + } + CustomEase.call(this, id); + this.bounce = this; + this.update(vars); + }, + p; + + CustomBounce.prototype = p = new CustomEase(); + p.constructor = CustomBounce; + + p.update = function(vars) { + vars = vars || this.vars; + var max = 0.999, + decay = Math.min(max, vars.strength || 0.7), // Math.min(0.999, 1 - 0.3 / (vars.strength || 1)), + decayX = decay, + gap = (vars.squash || 0) / 100, + originalGap = gap, + slope = 1 / 0.03, + w = 0.2, + h = 1, + prevX = 0.1, + path = [0, 0, 0.07, 0, 0.1, 1, 0.1, 1], + squashPath = [0, 0, 0, 0, 0.1, 0, 0.1, 0], + cp1, cp2, x, y, i, nextX, squishMagnitude; + for (i = 0; i < 200; i++) { + w *= decayX * ((decayX + 1) / 2); + h *= decay * decay; + nextX = prevX + w; + x = prevX + w * 0.49; + y = 1 - h; + cp1 = prevX + h / slope; + cp2 = x + (x - cp1) * 0.8; + + if (gap) { + prevX += gap; + cp1 += gap; + x += gap; + cp2 += gap; + nextX += gap; + squishMagnitude = gap / originalGap; + squashPath.push( + prevX - gap, 0, + prevX - gap, squishMagnitude, + prevX - gap / 2, squishMagnitude, //center peak anchor + prevX, squishMagnitude, + prevX, 0, + prevX, 0, //base anchor + prevX, squishMagnitude * -0.6, + prevX + (nextX - prevX) / 6, 0, + nextX, 0 + ); + path.push(prevX - gap, 1, + prevX, 1, + prevX, 1); + gap *= decay * decay; + } + + path.push(prevX, 1, + cp1, y, + x, y, + cp2, y, + nextX, 1, + nextX, 1); + + decay *= 0.95; + slope = h / (nextX - cp2); + prevX = nextX; + if (y > max) { + break; + } + } + + if (vars.endAtStart) { + x = -0.1; + path.unshift(x, 1, x, 1, -0.07, 0); + if (originalGap) { + gap = originalGap * 2.5; //make the initial anticipation squash longer (more realistic) + x -= gap; + path.unshift(x, 1, x, 1, x, 1); + squashPath.splice(0, 6); + squashPath.unshift(x, 0, x, 0, x, 1, x + gap / 2, 1, x + gap, 1, x + gap, 0, x + gap, 0, x + gap, -0.6, x + gap + 0.033, 0); + for (i = 0; i < squashPath.length; i+=2) { + squashPath[i] -= x; + } + } + for (i = 0; i < path.length; i+=2) { + path[i] -= x; + path[i+1] = 1 - path[i+1]; + } + } + + if (gap) { + _normalizeX(squashPath); + squashPath[2] = "C" + squashPath[2]; + if (!this.squash) { + this.squash = new CustomEase(vars.squashID || (this.id + "-squash")); + } + this.squash.setData("M" + squashPath.join(",")); + } + + _normalizeX(path); + path[2] = "C" + path[2]; + return this.setData("M" + path.join(",")); + }; + + CustomBounce.create = function(id, vars) { + return new CustomBounce(id, vars); + }; + + CustomBounce.version = "0.2.1"; + + return CustomBounce; + + }, true); + + +export var CustomBounce = globals.CustomBounce; +export { CustomBounce as default }; \ No newline at end of file diff --git a/lib/3rdparty/gsap/src/bonus-files-for-npm-users/CustomEase.js b/lib/3rdparty/gsap/src/bonus-files-for-npm-users/CustomEase.js new file mode 100644 index 0000000..29f89f8 --- /dev/null +++ b/lib/3rdparty/gsap/src/bonus-files-for-npm-users/CustomEase.js @@ -0,0 +1,379 @@ +/*! + * VERSION: 0.2.2 + * DATE: 2018-08-27 + * UPDATES AND DOCS AT: http://greensock.com + * + * @license Copyright (c) 2008-2019, GreenSock. All rights reserved. + * This work is subject to the terms at http://greensock.com/standard-license or for + * Club GreenSock members, the software agreement that was issued with your membership. + * + * @author: Jack Doyle, jack@greensock.com + **/ +/* eslint-disable */ + +import { _gsScope, globals, Ease } from "gsap/TweenLite.js"; + +_gsScope._gsDefine("easing.CustomEase", ["easing.Ease"], function() { + + var _numbersExp = /(?:(-|-=|\+=)?\d*\.?\d*(?:e[\-+]?\d+)?)[0-9]/ig, + _svgPathExp = /[achlmqstvz]|(-?\d*\.?\d*(?:e[\-+]?\d+)?)[0-9]/ig, + _scientific = /[\+\-]?\d*\.?\d+e[\+\-]?\d+/ig, + _needsParsingExp = /[cLlsS]/g, + _bezierError = "CustomEase only accepts Cubic Bezier data.", + _bezierToPoints = function (x1, y1, x2, y2, x3, y3, x4, y4, threshold, points, index) { + var x12 = (x1 + x2) / 2, + y12 = (y1 + y2) / 2, + x23 = (x2 + x3) / 2, + y23 = (y2 + y3) / 2, + x34 = (x3 + x4) / 2, + y34 = (y3 + y4) / 2, + x123 = (x12 + x23) / 2, + y123 = (y12 + y23) / 2, + x234 = (x23 + x34) / 2, + y234 = (y23 + y34) / 2, + x1234 = (x123 + x234) / 2, + y1234 = (y123 + y234) / 2, + dx = x4 - x1, + dy = y4 - y1, + d2 = Math.abs((x2 - x4) * dy - (y2 - y4) * dx), + d3 = Math.abs((x3 - x4) * dy - (y3 - y4) * dx), + length; + if (!points) { + points = [{x: x1, y: y1}, {x: x4, y: y4}]; + index = 1; + } + points.splice(index || points.length - 1, 0, {x: x1234, y: y1234}); + if ((d2 + d3) * (d2 + d3) > threshold * (dx * dx + dy * dy)) { + length = points.length; + _bezierToPoints(x1, y1, x12, y12, x123, y123, x1234, y1234, threshold, points, index); + _bezierToPoints(x1234, y1234, x234, y234, x34, y34, x4, y4, threshold, points, index + 1 + (points.length - length)); + } + return points; + }, + + _pathDataToBezier = function (d) { + var a = (d + "").replace(_scientific, function (m) { + var n = +m; + return (n < 0.0001 && n > -0.0001) ? 0 : n; + }).match(_svgPathExp) || [], //some authoring programs spit out very small numbers in scientific notation like "1e-5", so make sure we round that down to 0 first. + path = [], + relativeX = 0, + relativeY = 0, + elements = a.length, + l = 2, + i, x, y, command, isRelative, segment, startX, startY, prevCommand, difX, difY; + for (i = 0; i < elements; i++) { + prevCommand = command; + if (isNaN(a[i])) { + command = a[i].toUpperCase(); + isRelative = (command !== a[i]); //lower case means relative + } else { //commands like "C" can be strung together without any new command characters between. + i--; + } + x = +a[i + 1]; + y = +a[i + 2]; + if (isRelative) { + x += relativeX; + y += relativeY; + } + if (!i) { + startX = x; + startY = y; + } + if (command === "M") { + if (segment && segment.length < 8) { //if the path data was funky and just had a M with no actual drawing anywhere, skip it. + path.length -= 1; + l = 0; + } + relativeX = startX = x; + relativeY = startY = y; + segment = [x, y]; + l = 2; + path.push(segment); + i += 2; + command = "L"; //an "M" with more than 2 values gets interpreted as "lineTo" commands ("L"). + + } else if (command === "C") { + if (!segment) { + segment = [0, 0]; + } + segment[l++] = x; + segment[l++] = y; + if (!isRelative) { + relativeX = relativeY = 0; + } + segment[l++] = relativeX + a[i + 3] * 1; //note: "*1" is just a fast/short way to cast the value as a Number. WAAAY faster in Chrome, slightly slower in Firefox. + segment[l++] = relativeY + a[i + 4] * 1; + segment[l++] = relativeX = relativeX + a[i + 5] * 1; + segment[l++] = relativeY = relativeY + a[i + 6] * 1; + i += 6; + + } else if (command === "S") { + if (prevCommand === "C" || prevCommand === "S") { + difX = relativeX - segment[l - 4]; + difY = relativeY - segment[l - 3]; + segment[l++] = relativeX + difX; + segment[l++] = relativeY + difY; + } else { + segment[l++] = relativeX; + segment[l++] = relativeY; + } + segment[l++] = x; + segment[l++] = y; + if (!isRelative) { + relativeX = relativeY = 0; + } + segment[l++] = relativeX = relativeX + a[i + 3] * 1; + segment[l++] = relativeY = relativeY + a[i + 4] * 1; + i += 4; + + } else if (command === "L" || command === "Z") { + if (command === "Z") { + x = startX; + y = startY; + segment.closed = true; + } + if (command === "L" || Math.abs(relativeX - x) > 0.5 || Math.abs(relativeY - y) > 0.5) { + segment[l++] = relativeX + (x - relativeX) / 3; + segment[l++] = relativeY + (y - relativeY) / 3; + segment[l++] = relativeX + (x - relativeX) * 2 / 3; + segment[l++] = relativeY + (y - relativeY) * 2 / 3; + segment[l++] = x; + segment[l++] = y; + if (command === "L") { + i += 2; + } + } + relativeX = x; + relativeY = y; + } else { + throw _bezierError; + } + + } + return path[0]; + }, + + _findMinimum = function (values) { + var l = values.length, + min = 999999999999, + i; + for (i = 1; i < l; i += 6) { + if (+values[i] < min) { + min = +values[i]; + } + } + return min; + }, + + _normalize = function (values, height, originY) { //takes all the points and translates/scales them so that the x starts at 0 and ends at 1. + if (!originY && originY !== 0) { + originY = Math.max(+values[values.length-1], +values[1]); + } + var tx = +values[0] * -1, + ty = -originY, + l = values.length, + sx = 1 / (+values[l - 2] + tx), + sy = -height || ((Math.abs(+values[l - 1] - +values[1]) < 0.01 * (+values[l - 2] - +values[0])) ? _findMinimum(values) + ty : +values[l - 1] + ty), + i; + if (sy) { //typically y ends at 1 (so that the end values are reached) + sy = 1 / sy; + } else { //in case the ease returns to its beginning value, scale everything proportionally + sy = -sx; + } + for (i = 0; i < l; i += 2) { + values[i] = (+values[i] + tx) * sx; + values[i + 1] = (+values[i + 1] + ty) * sy; + } + }, + + _getRatio = function (p) { + var point = this.lookup[(p * this.l) | 0] || this.lookup[this.l - 1]; + if (point.nx < p) { + point = point.n; + } + return point.y + ((p - point.x) / point.cx) * point.cy; + }, + + + CustomEase = function (id, data, config) { + this._calcEnd = true; + this.id = id; + if (id) { + Ease.map[id] = this; + } + this.getRatio = _getRatio; //speed optimization, faster lookups. + this.setData(data, config); + }, + p = CustomEase.prototype = new Ease(); + + p.constructor = CustomEase; + + p.setData = function(data, config) { + data = data || "0,0,1,1"; + var values = data.match(_numbersExp), + closest = 1, + points = [], + l, a1, a2, i, inc, j, point, prevPoint, p, precision; + config = config || {}; + precision = config.precision || 1; + this.data = data; + this.lookup = []; + this.points = points; + this.fast = (precision <= 1); + if (_needsParsingExp.test(data) || (data.indexOf("M") !== -1 && data.indexOf("C") === -1)) { + values = _pathDataToBezier(data); + } + l = values.length; + if (l === 4) { + values.unshift(0, 0); + values.push(1, 1); + l = 8; + } else if ((l - 2) % 6) { + throw _bezierError; + } + if (+values[0] !== 0 || +values[l - 2] !== 1) { + _normalize(values, config.height, config.originY); + } + + this.rawBezier = values; + + for (i = 2; i < l; i += 6) { + a1 = {x: +values[i - 2], y: +values[i - 1]}; + a2 = {x: +values[i + 4], y: +values[i + 5]}; + points.push(a1, a2); + _bezierToPoints(a1.x, a1.y, +values[i], +values[i + 1], +values[i + 2], +values[i + 3], a2.x, a2.y, 1 / (precision * 200000), points, points.length - 1); + } + l = points.length; + for (i = 0; i < l; i++) { + point = points[i]; + prevPoint = points[i - 1] || point; + if (point.x > prevPoint.x || (prevPoint.y !== point.y && prevPoint.x === point.x) || point === prevPoint) { //if a point goes BACKWARD in time or is a duplicate, just drop it. + prevPoint.cx = point.x - prevPoint.x; //change in x between this point and the next point (performance optimization) + prevPoint.cy = point.y - prevPoint.y; + prevPoint.n = point; + prevPoint.nx = point.x; //next point's x value (performance optimization, making lookups faster in getRatio()). Remember, the lookup will always land on a spot where it's either this point or the very next one (never beyond that) + if (this.fast && i > 1 && Math.abs(prevPoint.cy / prevPoint.cx - points[i - 2].cy / points[i - 2].cx) > 2) { //if there's a sudden change in direction, prioritize accuracy over speed. Like a bounce ease - you don't want to risk the sampling chunks landing on each side of the bounce anchor and having it clipped off. + this.fast = false; + } + if (prevPoint.cx < closest) { + if (!prevPoint.cx) { + prevPoint.cx = 0.001; //avoids math problems in getRatio() (dividing by zero) + if (i === l - 1) { //in case the final segment goes vertical RIGHT at the end, make sure we end at the end. + prevPoint.x -= 0.001; + closest = Math.min(closest, 0.001); + this.fast = false; + } + } else { + closest = prevPoint.cx; + } + } + } else { + points.splice(i--, 1); + l--; + } + } + l = (1 / closest + 1) | 0; + this.l = l; //record for speed optimization + inc = 1 / l; + j = 0; + point = points[0]; + if (this.fast) { + for (i = 0; i < l; i++) { //for fastest lookups, we just sample along the path at equal x (time) distance. Uses more memory and is slightly less accurate for anchors that don't land on the sampling points, but for the vast majority of eases it's excellent (and fast). + p = i * inc; + if (point.nx < p) { + point = points[++j]; + } + a1 = point.y + ((p - point.x) / point.cx) * point.cy; + this.lookup[i] = {x: p, cx: inc, y: a1, cy: 0, nx: 9}; + if (i) { + this.lookup[i - 1].cy = a1 - this.lookup[i - 1].y; + } + } + this.lookup[l - 1].cy = points[points.length - 1].y - a1; + } else { //this option is more accurate, ensuring that EVERY anchor is hit perfectly. Clipping across a bounce, for example, would never happen. + for (i = 0; i < l; i++) { //build a lookup table based on the smallest distance so that we can instantly find the appropriate point (well, it'll either be that point or the very next one). We'll look up based on the linear progress. So it's it's 0.5 and the lookup table has 100 elements, it'd be like lookup[Math.floor(0.5 * 100)] + if (point.nx < i * inc) { + point = points[++j]; + } + this.lookup[i] = point; + } + + if (j < points.length - 1) { + this.lookup[i-1] = points[points.length-2]; + } + } + this._calcEnd = (points[points.length-1].y !== 1 || points[0].y !== 0); //ensures that we don't run into floating point errors. As long as we're starting at 0 and ending at 1, tell GSAP to skip the final calculation and use 0/1 as the factor. + return this; + }; + + p.getRatio = _getRatio; + + p.getSVGData = function(config) { + return CustomEase.getSVGData(this, config); + }; + + CustomEase.create = function (id, data, config) { + return new CustomEase(id, data, config); + }; + + CustomEase.version = "0.2.2"; + + CustomEase.bezierToPoints = _bezierToPoints; + CustomEase.get = function (id) { + return Ease.map[id]; + }; + CustomEase.getSVGData = function(ease, config) { + config = config || {}; + var rnd = 1000, + width = config.width || 100, + height = config.height || 100, + x = config.x || 0, + y = (config.y || 0) + height, + e = config.path, + a, slope, i, inc, tx, ty, precision, threshold, prevX, prevY; + if (config.invert) { + height = -height; + y = 0; + } + ease = ease.getRatio ? ease : Ease.map[ease] || console.log("No ease found: ", ease); + if (!ease.rawBezier) { + a = ["M" + x + "," + y]; + precision = Math.max(5, (config.precision || 1) * 200); + inc = 1 / precision; + precision += 2; + threshold = 5 / precision; + prevX = (((x + inc * width) * rnd) | 0) / rnd; + prevY = (((y + ease.getRatio(inc) * -height) * rnd) | 0) / rnd; + slope = (prevY - y) / (prevX - x); + for (i = 2; i < precision; i++) { + tx = (((x + i * inc * width) * rnd) | 0) / rnd; + ty = (((y + ease.getRatio(i * inc) * -height) * rnd) | 0) / rnd; + if (Math.abs((ty - prevY) / (tx - prevX) - slope) > threshold || i === precision - 1) { //only add points when the slope changes beyond the threshold + a.push(prevX + "," + prevY); + slope = (ty - prevY) / (tx - prevX); + } + prevX = tx; + prevY = ty; + } + } else { + a = []; + precision = ease.rawBezier.length; + for (i = 0; i < precision; i += 2) { + a.push((((x + ease.rawBezier[i] * width) * rnd) | 0) / rnd + "," + (((y + ease.rawBezier[i + 1] * -height) * rnd) | 0) / rnd); + } + a[0] = "M" + a[0]; + a[1] = "C" + a[1]; + } + if (e) { + (typeof(e) === "string" ? document.querySelector(e) : e).setAttribute("d", a.join(" ")); + } + return a.join(" "); + }; + + return CustomEase; + + }, true); + +export var CustomEase = globals.CustomEase; +export { CustomEase as default }; \ No newline at end of file diff --git a/lib/3rdparty/gsap/src/bonus-files-for-npm-users/CustomWiggle.js b/lib/3rdparty/gsap/src/bonus-files-for-npm-users/CustomWiggle.js new file mode 100644 index 0000000..8010764 --- /dev/null +++ b/lib/3rdparty/gsap/src/bonus-files-for-npm-users/CustomWiggle.js @@ -0,0 +1,113 @@ +/*! + * VERSION: 0.2.1 + * DATE: 2018-08-27 + * UPDATES AND DOCS AT: http://greensock.com + * + * @license Copyright (c) 2008-2019, GreenSock. All rights reserved. + * This work is subject to the terms at http://greensock.com/standard-license or for + * Club GreenSock members, the software agreement that was issued with your membership. + * + * @author: Jack Doyle, jack@greensock.com + **/ +/* eslint-disable */ + +import { _gsScope, globals, Ease } from "gsap/TweenLite.js"; +import CustomEase from "./CustomEase.js"; + +_gsScope._gsDefine("easing.CustomWiggle", ["easing.CustomEase", "easing.Ease"], function() { + + + var eases = { + easeOut: new CustomEase("", "M0,1,C0.7,1,0.6,0,1,0"), + easeInOut: new CustomEase("", "M0,0,C0.104,0,0.242,1,0.444,1,0.644,1,0.608,0,1,0"), + anticipate: new CustomEase("", "M0,0,C0,0.222,0.024,0.386,0.06,0.402,0.181,0.455,0.647,0.646,0.7,0.67,0.9,0.76,1,0.846,1,1"), + uniform: new CustomEase("", "M0,0,C0,0.95,0.01,1,0.01,1,0.01,1,1,1,1,1,1,1,1,0.01,1,0") + }, + _linearEase = new CustomEase(), //linear + _parseEase = function(ease, invertNonCustomEases) { + ease = ease.getRatio ? ease : Ease.map[ease] || new CustomEase("", ease); + return (ease.rawBezier || !invertNonCustomEases) ? ease : {getRatio:function(n) { return 1 - ease.getRatio(n); }}; + }, + + + CustomWiggle = function(id, vars) { + this.vars = vars || {}; + CustomEase.call(this, id); + this.update(this.vars); + }, + p; + + CustomWiggle.prototype = p = new CustomEase(); + p.constructor = CustomWiggle; + + p.update = function(vars) { + vars = vars || this.vars; + var wiggles = (vars.wiggles || 10) | 0, + inc = 1 / wiggles, + x = inc / 2, + anticipate = (vars.type === "anticipate"), + yEase = eases[vars.type] || eases.easeOut, + xEase = _linearEase, + rnd = 1000, + nextX, nextY, angle, handleX, handleY, easedX, y, path, i; + if (anticipate) { //the anticipate ease is actually applied on the x-axis (timing) and uses easeOut for amplitude. + xEase = yEase; + yEase = eases.easeOut; + } + if (vars.timingEase) { + xEase = _parseEase(vars.timingEase); + } + if (vars.amplitudeEase) { + yEase = _parseEase(vars.amplitudeEase, true); + } + easedX = xEase.getRatio(x); + y = anticipate ? -yEase.getRatio(x) : yEase.getRatio(x); + path = [0, 0, easedX / 4, 0, easedX / 2, y, easedX, y]; + + if (vars.type === "random") { //if we just select random values on the y-axis and plug them into the "normal" algorithm, since the control points are always straight horizontal, it creates a bit of a slowdown at each anchor which just didn't seem as desirable, so we switched to an algorithm that bends the control points to be more in line with their context. + path.length = 4; + nextX = xEase.getRatio(inc); + nextY = Math.random() * 2 - 1; + for (i = 2; i < wiggles; i++) { + x = nextX; + y = nextY; + nextX = xEase.getRatio(inc * i); + nextY = Math.random() * 2 - 1; + angle = Math.atan2(nextY - path[path.length - 3], nextX - path[path.length - 4]); + handleX = Math.cos(angle) * inc; + handleY = Math.sin(angle) * inc; + path.push(x - handleX, y - handleY, x, y, x + handleX, y + handleY); + } + path.push(nextX, 0, 1, 0); + } else { + for (i = 1; i < wiggles; i++) { + path.push(xEase.getRatio(x + inc / 2), y); + x += inc; + y = ((y > 0) ? -1 : 1) * (yEase.getRatio(i * inc)); + easedX = xEase.getRatio(x); + path.push(xEase.getRatio(x - inc / 2), y, easedX, y); + } + path.push(xEase.getRatio(x + inc / 4), y, xEase.getRatio(x + inc / 4), 0, 1, 0); + } + i = path.length; + while (--i > -1) { + path[i] = ((path[i] * rnd) | 0) / rnd; //round values to avoid odd strings for super tiny values + } + path[2] = "C" + path[2]; + this.setData("M" + path.join(",")); + }; + + CustomWiggle.create = function (id, vars) { + return new CustomWiggle(id, vars); + }; + + CustomWiggle.version = "0.2.1"; + CustomWiggle.eases = eases; + + return CustomWiggle; + + }, true); + + +export var CustomWiggle = globals.CustomWiggle; +export { CustomWiggle as default }; \ No newline at end of file diff --git a/lib/3rdparty/gsap/src/bonus-files-for-npm-users/DrawSVGPlugin.js b/lib/3rdparty/gsap/src/bonus-files-for-npm-users/DrawSVGPlugin.js new file mode 100644 index 0000000..78ea6e3 --- /dev/null +++ b/lib/3rdparty/gsap/src/bonus-files-for-npm-users/DrawSVGPlugin.js @@ -0,0 +1,230 @@ +/*! + * VERSION: 0.2.1 + * DATE: 2019-02-07 + * UPDATES AND DOCS AT: http://greensock.com + * + * @license Copyright (c) 2008-2019, GreenSock. All rights reserved. + * DrawSVGPlugin is a Club GreenSock membership benefit; You must have a valid membership to use + * this code without violating the terms of use. Visit http://greensock.com/club/ to sign up or get more details. + * This work is subject to the software agreement that was issued with your membership. + * + * @author: Jack Doyle, jack@greensock.com + */ +/* eslint-disable */ + +import { _gsScope } from "gsap/TweenLite.js"; + +var _doc = _gsScope.document, + _computedStyleScope = (typeof(window) !== "undefined" ? window : _doc.defaultView || {getComputedStyle:function() {}}), + _getComputedStyle = function(e) { + return _computedStyleScope.getComputedStyle(e); //to avoid errors in Microsoft Edge, we need to call getComputedStyle() from a specific scope, typically window. + }, + _numbersExp = /(?:(-|-=|\+=)?\d*\.?\d*(?:e[\-+]?\d+)?)[0-9]/ig, + _isEdge = (((_gsScope.navigator || {}).userAgent || "").indexOf("Edge") !== -1), //Microsoft Edge has a bug that causes it not to redraw the path correctly if the stroke-linecap is anything other than "butt" (like "round") and it doesn't match the stroke-linejoin. A way to trigger it is to change the stroke-miterlimit, so we'll only do that if/when we have to (to maximize performance) + _types = {rect:["width","height"], circle:["r","r"], ellipse:["rx","ry"], line:["x2","y2"]}, + DrawSVGPlugin; + + function getDistance(x1, y1, x2, y2, scaleX, scaleY) { + x2 = (parseFloat(x2 || 0) - parseFloat(x1 || 0)) * scaleX; + y2 = (parseFloat(y2 || 0) - parseFloat(y1 || 0)) * scaleY; + return Math.sqrt(x2 * x2 + y2 * y2); + } + + function unwrap(element) { + if (typeof(element) === "string" || !element.nodeType) { + element = _gsScope.TweenLite.selector(element); + if (element.length) { + element = element[0]; + } + } + return element; + } + + //accepts values like "100%" or "20% 80%" or "20 50" and parses it into an absolute start and end position on the line/stroke based on its length. Returns an an array with the start and end values, like [0, 243] + function parse(value, length, defaultStart) { + var i = value.indexOf(" "), + s, e; + if (i === -1) { + s = defaultStart !== undefined ? defaultStart + "" : value; + e = value; + } else { + s = value.substr(0, i); + e = value.substr(i+1); + } + s = (s.indexOf("%") !== -1) ? (parseFloat(s) / 100) * length : parseFloat(s); + e = (e.indexOf("%") !== -1) ? (parseFloat(e) / 100) * length : parseFloat(e); + return (s > e) ? [e, s] : [s, e]; + } + + function getLength(element) { + if (!element) { + return 0; + } + element = unwrap(element); + var type = element.tagName.toLowerCase(), + scaleX = 1, + scaleY = 1, + length, bbox, points, prevPoint, i, rx, ry; + if (element.getAttribute("vector-effect") === "non-scaling-stroke") { //non-scaling-stroke basically scales the shape and then strokes it at the screen-level (after transforms), thus we need to adjust the length accordingly. + scaleY = element.getScreenCTM(); + scaleX = Math.sqrt(scaleY.a * scaleY.a + scaleY.b * scaleY.b); + scaleY = Math.sqrt(scaleY.d * scaleY.d + scaleY.c * scaleY.c); + } + try { //IE bug: calling .getTotalLength() locks the repaint area of the stroke to whatever its current dimensions are on that frame/tick. To work around that, we must call getBBox() to force IE to recalculate things. + bbox = element.getBBox(); //solely for fixing bug in IE - we don't actually use the bbox. + } catch (e) { + //firefox has a bug that throws an error if the element isn't visible. + console.log("Error: Some browsers like Firefox won't report measurements of invisible elements (like display:none or masks inside defs)."); + } + if ((!bbox || (!bbox.width && !bbox.height)) && _types[type]) { //if the element isn't visible, try to discern width/height using its attributes. + bbox = { + width: parseFloat( element.getAttribute(_types[type][0]) ), + height: parseFloat( element.getAttribute(_types[type][1]) ) + }; + if (type !== "rect" && type !== "line") { //double the radius for circles and ellipses + bbox.width *= 2; + bbox.height *= 2; + } + if (type === "line") { + bbox.x = parseFloat( element.getAttribute("x1") ); + bbox.y = parseFloat( element.getAttribute("y1") ); + bbox.width = Math.abs(bbox.width - bbox.x); + bbox.height = Math.abs(bbox.height - bbox.y); + } + } + if (type === "path") { + prevPoint = element.style.strokeDasharray; + element.style.strokeDasharray = "none"; + length = element.getTotalLength() || 0; + if (scaleX !== scaleY) { + console.log("Warning: length cannot be measured accurately when vector-effect is non-scaling-stroke and the element isn't proportionally scaled."); + } + length *= (scaleX + scaleY) / 2; + element.style.strokeDasharray = prevPoint; + } else if (type === "rect") { + length = bbox.width * 2 * scaleX + bbox.height * 2 * scaleY; + } else if (type === "line") { + length = getDistance(bbox.x, bbox.y, bbox.x + bbox.width, bbox.y + bbox.height, scaleX, scaleY); + } else if (type === "polyline" || type === "polygon") { + points = element.getAttribute("points").match(_numbersExp) || []; + if (type === "polygon") { + points.push(points[0], points[1]); + } + length = 0; + for (i = 2; i < points.length; i+=2) { + length += getDistance(points[i-2], points[i-1], points[i], points[i+1], scaleX, scaleY) || 0; + } + } else if (type === "circle" || type === "ellipse") { + rx = (bbox.width / 2) * scaleX; + ry = (bbox.height / 2) * scaleY; + length = Math.PI * ( 3 * (rx + ry) - Math.sqrt((3 * rx + ry) * (rx + 3 * ry)) ); + } + return length || 0; + } + + function getPosition(element, length) { + if (!element) { + return [0, 0]; + } + element = unwrap(element); + length = length || (getLength(element) + 1); + var cs = _getComputedStyle(element), + dash = cs.strokeDasharray || "", + offset = parseFloat(cs.strokeDashoffset), + i = dash.indexOf(","); + if (i < 0) { + i = dash.indexOf(" "); + } + dash = (i < 0) ? length : parseFloat(dash.substr(0, i)) || 0.00001; + if (dash > length) { + dash = length; + } + return [Math.max(0, -offset), Math.max(0, dash - offset)]; + } + + DrawSVGPlugin = _gsScope._gsDefine.plugin({ + propName: "drawSVG", + API: 2, + version: "0.2.1", + global: true, + overwriteProps: ["drawSVG"], + + init: function(target, value, tween, index) { + if (!target.getBBox) { + return false; + } + var length = getLength(target) + 1, + start, end, overage, cs; + this._style = target.style; + this._target = target; + if (typeof(value) === "function") { + value = value(index, target); + } + if (value === true || value === "true") { + value = "0 100%"; + } else if (!value) { + value = "0 0"; + } else if ((value + "").indexOf(" ") === -1) { + value = "0 " + value; + } + start = getPosition(target, length); + end = parse(value, length, start[0]); + this._length = length + 10; + if (start[0] === 0 && end[0] === 0) { + overage = Math.max(0.00001, end[1] - length); //allow people to go past the end, like values of 105% because for some paths, Firefox doesn't return an accurate getTotalLength(), so it could end up coming up short. + this._dash = length + overage; + this._offset = length - start[1] + overage; + this._offsetPT = this._addTween(this, "_offset", this._offset, length - end[1] + overage, "drawSVG"); + } else { + this._dash = (start[1] - start[0]) || 0.000001; //some browsers render artifacts if dash is 0, so we use a very small number in that case. + this._offset = -start[0]; + this._dashPT = this._addTween(this, "_dash", this._dash, (end[1] - end[0]) || 0.00001, "drawSVG"); + this._offsetPT = this._addTween(this, "_offset", this._offset, -end[0], "drawSVG"); + } + if (_isEdge) { //to work around a bug in Microsoft Edge, animate the stroke-miterlimit by 0.0001 just to trigger the repaint (unnecessary if it's "round" and stroke-linejoin is also "round"). Imperceptible, relatively high-performance, and effective. Another option was to set the "d" attribute to its current value on every tick, but that seems like it'd be much less performant. + cs = _getComputedStyle(target); + if (cs.strokeLinecap !== cs.strokeLinejoin) { + end = parseFloat(cs.strokeMiterlimit); + this._addTween(target.style, "strokeMiterlimit", end, end + 0.0001, "strokeMiterlimit"); + } + } + this._live = (target.getAttribute("vector-effect") === "non-scaling-stroke" || (value + "").indexOf("live") !== -1); + return true; + }, + + //called each time the values should be updated, and the ratio gets passed as the only parameter (typically it's a value between 0 and 1, but it can exceed those when using an ease like Elastic.easeOut or Back.easeOut, etc.) + set: function(ratio) { + if (this._firstPT) { + //when the element has vector-effect="non-scaling-stroke" and the SVG is resized (like on a window resize), it actually changes the length of the stroke! So we must sense that and make the proper adjustments. + if (this._live) { + var length = getLength(this._target) + 11, + lengthRatio; + if (length !== this._length) { + lengthRatio = length / this._length; + this._length = length; + this._offsetPT.s *= lengthRatio; + this._offsetPT.c *= lengthRatio; + if (this._dashPT) { + this._dashPT.s *= lengthRatio; + this._dashPT.c *= lengthRatio; + } else { + this._dash *= lengthRatio; + } + } + } + this._super.setRatio.call(this, ratio); + this._style.strokeDashoffset = this._offset; + if (ratio === 1 || ratio === 0) { + this._style.strokeDasharray = (this._offset < 0.001 && this._length - this._dash <= 10) ? "none" : (this._offset === this._dash) ? "0px, 999999px" : this._dash + "px," + this._length + "px"; + } else { + this._style.strokeDasharray = this._dash + "px," + this._length + "px"; + } + } + } + + }); + + DrawSVGPlugin.getLength = getLength; + DrawSVGPlugin.getPosition = getPosition; + +export { DrawSVGPlugin, DrawSVGPlugin as default }; \ No newline at end of file diff --git a/lib/3rdparty/gsap/src/bonus-files-for-npm-users/GSDevTools.js b/lib/3rdparty/gsap/src/bonus-files-for-npm-users/GSDevTools.js new file mode 100644 index 0000000..e9b6493 --- /dev/null +++ b/lib/3rdparty/gsap/src/bonus-files-for-npm-users/GSDevTools.js @@ -0,0 +1,1226 @@ +/*! + * VERSION: 0.1.9 + * DATE: 2019-02-07 + * UPDATES AND DOCS AT: http://greensock.com + * + * @license Copyright (c) 2008-2019, GreenSock. All rights reserved. + * GSDevTools is a Club GreenSock membership benefit; You must have a valid membership to use + * this code without violating the terms of use. Visit http://greensock.com/club/ to sign up or get more details. + * This work is subject to the software agreement that was issued with your membership. + * + * @author: Jack Doyle, jack@greensock.com + **/ +/* eslint-disable */ + +import { TweenLite, _gsScope, globals, Animation, SimpleTimeline, TweenPlugin, Power2, Power3, Linear } from "gsap/TweenLite.js"; +import TimelineLite from "gsap/TimelineLite.js"; +import Draggable from "gsap/Draggable.js"; +import CSSPlugin from "gsap/CSSPlugin.js"; +import AttrPlugin from "gsap/AttrPlugin.js"; + +TweenPlugin.activate([CSSPlugin, AttrPlugin]); // to ensure treeshaking doesn't dump it. + + _gsScope._gsDefine("GSDevTools", ["TweenLite", "core.Animation", "core.SimpleTimeline", "TimelineLite", "utils.Draggable", "plugins.CSSPlugin"], function() { + + var _doc = document, + _docElement = _doc.documentElement, + _svgNS = "http://www.w3.org/2000/svg", + _domNS = "http://www.w3.org/1999/xhtml", + _idSeed = 0, //we assign an ID to each GSDevTools instance so that we can segregate the sessionStorage data accordingly. + _lookup = {}, + _createElement = function(type, container, cssText) { + var element = _doc.createElementNS ? _doc.createElementNS(type === "svg" ? _svgNS : _domNS, type) : _doc.createElement(type); + if (container) { + if (typeof(container) === "string") { + container = _doc.querySelector(container); + } + container.appendChild(element); + } + if (type === "svg") { + element.setAttribute("xmlns", _svgNS); + element.setAttribute("xmlns:xlink", _domNS); + } + if (cssText) { + element.style.cssText = cssText; + } + return element; + }, + _clearSelection = function() { + if (_doc.selection) { + _doc.selection.empty() + } else if (window.getSelection) { + window.getSelection().removeAllRanges(); + } + }, + _globalTimeline = Animation._rootTimeline, + _getChildrenOf = function(timeline, includeTimelines) { + var a = [], + cnt = 0, + tween = timeline._first; + while (tween) { + if (tween instanceof TweenLite) { + if (tween.vars.id) { + a[cnt++] = tween; + } + } else { + if (includeTimelines && tween.vars.id) { + a[cnt++] = tween; + } + a = a.concat(_getChildrenOf(tween, includeTimelines)); + cnt = a.length; + } + tween = tween._next; + } + return a; + }, + //eliminates any very long repeating children (direct children, not deeply nested) and returns the duration accordingly. + _getClippedDuration = function(animation, excludeRootRepeats) { + var max = 0, + repeat = Math.max(0, animation._repeat), + t = animation._first; + if (!t) { + max = animation.duration(); + } + while (t) { + max = Math.max(max, t.totalDuration() > 999 ? t.endTime(false) : t._startTime + t._totalDuration / t._timeScale); + t = t._next; + } + return (!excludeRootRepeats && repeat) ? max * (repeat + 1) + (animation._repeatDelay * repeat) : max; + }, + _getAnimationById = function(id) { + if (!id) { + return null; + } + if (id instanceof Animation) { + return id; + } + var a = _getChildrenOf(_globalTimeline, true), + i = a.length; + while (--i > -1) { + if (a[i].vars.id === id) { + return a[i]; + } + } + }, + _timeToProgress = function(time, animation, defaultValue, relativeProgress) { + var add, i, a; + if (typeof(time) === "string") { + if (time.charAt(1) === "=") { + add = parseInt(time.charAt(0) + "1", 10) * parseFloat(time.substr(2)); + if (add < 0 && relativeProgress === 0) { //if something like inTime:"-=2", we measure it from the END, not the beginning + relativeProgress = 100; + } + time = (relativeProgress / 100 * animation.duration()) + add; + } else if (isNaN(time) && animation.getLabelTime && animation.getLabelTime(time) !== -1) { + time = animation.getLabelTime(time); + } else if (animation === _recordedRoot) { //perhaps they defined an id of an animation, like "myAnimation+=2" + i = time.indexOf("="); + if (i > 0) { + add = parseInt(time.charAt(i-1) + "1", 10) * parseFloat(time.substr(i+1)); + time = time.substr(0, i-1); + } else { + add = 0; + } + a = _getAnimationById(time); + if (a) { + time = _getGlobalTime(a, defaultValue / 100 * a.duration()) + add; + } + } + } + + time = isNaN(time) ? defaultValue : parseFloat(time); + return Math.min(100, Math.max(0, time / animation.duration() * 100)); + }, + + _getGlobalTime = function(animation, time) { + var tl = animation; + time = time || 0; + if (tl.timeline) { + while (tl.timeline.timeline) { + time = (time / tl._timeScale) + tl._startTime; + tl = tl.timeline; + } + } + return time; + }, + + + _addedCSS, + _createRootElement = function(element, minimal, css) { + if (!_addedCSS) { + _createElement("style", _docElement).innerHTML = '.gs-dev-tools{height:51px;bottom:0;left:0;right:0;display:block;position:fixed;overflow:visible;padding:0}.gs-dev-tools *{box-sizing:content-box;visibility:visible}.gs-dev-tools .gs-top{position:relative;z-index:499}.gs-dev-tools .gs-bottom{display:flex;align-items:center;justify-content:space-between;background-color:rgba(0,0,0,.6);height:42px;border-top:1px solid #999;position:relative}.gs-dev-tools .timeline{position:relative;height:8px;margin-left:15px;margin-right:15px;overflow:visible}.gs-dev-tools .progress-bar,.gs-dev-tools .timeline-track{height:8px;width:100%;position:absolute;top:0;left:0}.gs-dev-tools .timeline-track{background-color:#999;opacity:.6}.gs-dev-tools .progress-bar{background-color:#91e600;height:8px;top:0;width:0;pointer-events:none}.gs-dev-tools .seek-bar{width:100%;position:absolute;height:24px;top:-12px;left:0;background-color:transparent}.gs-dev-tools .in-point,.gs-dev-tools .out-point{width:15px;height:26px;position:absolute;top:-18px}.gs-dev-tools .in-point-shape{fill:#6d9900;stroke:rgba(0,0,0,.5);stroke-width:1}.gs-dev-tools .out-point-shape{fill:#994242;stroke:rgba(0,0,0,.5);stroke-width:1}.gs-dev-tools .in-point{transform:translateX(-100%)}.gs-dev-tools .out-point{left:100%}.gs-dev-tools .grab{stroke:rgba(255,255,255,.3);stroke-width:1}.gs-dev-tools .playhead{position:absolute;top:-5px;transform:translate(-50%,0);left:0;border-radius:50%;width:16px;height:16px;border:1px solid #6d9900;background-color:#91e600}.gs-dev-tools .gs-btn-white{fill:#fff}.gs-dev-tools .pause{opacity:0}.gs-dev-tools .select-animation{vertical-align:middle;position:relative;padding:6px 10px}.gs-dev-tools .select-animation-container{flex-grow:4;width:40%}.gs-dev-tools .select-arrow{display:inline-block;width:12px;height:7px;margin:0 7px;transform:translate(0,-2px)}.gs-dev-tools .select-arrow-shape{stroke:rgba(255,255,255,.6);stroke-width:2px;fill:none}.gs-dev-tools .rewind{height:16px;width:19px;padding:10px 4px;min-width:24px}.gs-dev-tools .rewind-path{opacity:.6}.gs-dev-tools .play-pause{width:24px;height:24px;padding:6px 10px;min-width:24px}.gs-dev-tools .ease{width:30px;height:30px;padding:10px;min-width:30px;display:none}.gs-dev-tools .ease-path{fill:none;stroke:rgba(255,255,255,.6);stroke-width:2px}.gs-dev-tools .ease-border{fill:rgba(255,255,255,.25)}.gs-dev-tools .time-scale{font-family:monospace;font-size:18px;text-align:center;color:rgba(255,255,255,.6);padding:4px 4px 4px 0;min-width:30px;margin-left:7px}.gs-dev-tools .loop{width:20px;padding:5px;min-width:20px}.gs-dev-tools .loop-path{fill:rgba(255,255,255,.6)}.gs-dev-tools label span{color:#fff;font-family:monospace;text-decoration:none;font-size:16px;line-height:18px}.gs-dev-tools .time-scale span{color:rgba(255,255,255,.6)}.gs-dev-tools button:focus,.gs-dev-tools select:focus{outline:0}.gs-dev-tools label{position:relative;cursor:pointer}.gs-dev-tools label.locked{text-decoration:none;cursor:auto}.gs-dev-tools label input,.gs-dev-tools label select{position:absolute;left:0;top:0;z-index:1;font:inherit;font-size:inherit;line-height:inherit;height:100%;width:100%;color:#000!important;opacity:0;background:0 0;border:none;padding:0;margin:0;-webkit-appearance:none;-moz-appearance:none;appearance:none;cursor:pointer}.gs-dev-tools label input+.display{position:relative;z-index:2}.gs-dev-tools .gs-bottom-right{vertical-align:middle;display:flex;align-items:center;flex-grow:4;width:40%;justify-content:flex-end}.gs-dev-tools .time-container{font-size:18px;font-family:monospace;color:rgba(255,255,255,.6);margin:0 5px}.gs-dev-tools .logo{width:32px;height:32px;position:relative;top:2px;margin:0 12px}.gs-dev-tools .gs-hit-area{background-color:transparent;width:100%;height:100%;top:0;position:absolute}.gs-dev-tools.minimal{height:auto;display:flex;align-items:stretch}.gs-dev-tools.minimal .gs-top{order:2;flex-grow:4;background-color:rgba(0,0,0,1)}.gs-dev-tools.minimal .gs-bottom{background-color:rgba(0,0,0,1);border-top:none}.gs-dev-tools.minimal .timeline{top:50%;transform:translate(0,-50%)}.gs-dev-tools.minimal .in-point,.gs-dev-tools.minimal .out-point{display:none}.gs-dev-tools.minimal .select-animation-container{display:none}.gs-dev-tools.minimal .rewind{display:none}.gs-dev-tools.minimal .play-pause{width:20px;height:20px;padding:4px 6px;margin-left:14px}.gs-dev-tools.minimal .time-scale{min-width:26px}.gs-dev-tools.minimal .loop{width:18px;min-width:18px;display:none}.gs-dev-tools.minimal .gs-bottom-right{display:none}@media only screen and (max-width:600px){.gs-dev-tools{height:auto;display:flex;align-items:stretch}.gs-dev-tools .gs-top{order:2;flex-grow:4;background-color:rgba(0,0,0,1);height:42px}.gs-dev-tools .gs-bottom{background-color:rgba(0,0,0,1);border-top:none}.gs-dev-tools .timeline{top:50%;transform:translate(0,-50%)}.gs-dev-tools .in-point,.gs-dev-tools .out-point{display:none}.gs-dev-tools .select-animation-container{display:none}.gs-dev-tools .rewind{display:none}.gs-dev-tools .play-pause{width:20px;height:20px;padding:4px 6px;margin-left:14px}.gs-dev-tools .time-scale{min-width:26px}.gs-dev-tools .loop{width:18px;min-width:18px;display:none}.gs-dev-tools .gs-bottom-right{display:none}}'; + _addedCSS = true; + } + if (typeof(element) === "string") { + element = document.querySelector(element); + } + var root = _createElement("div", element || _docElement.getElementsByTagName("body")[0] || _docElement); + root.setAttribute("class", "gs-dev-tools" + (minimal ? " minimal" : "")); + root.innerHTML = '
0.00 / 0.00
'; + if (element) { + root.style.position = "absolute"; + root.style.top = minimal ? "calc(100% - 42px)" : "calc(100% - 51px)"; + } + if (css) { + if (typeof(css) === "string") { + root.style.cssText = css; + } else if (typeof(css) === "object") { + css.data = "root"; + TweenLite.set(root, css).kill(); + } + if (root.style.top) { + root.style.bottom = "auto"; + } + if (root.style.width) { + TweenLite.set(root, {xPercent: -50, left: "50%", right: "auto", data:"root"}).kill(); + } + } + if (!minimal && root.offsetWidth < 600) { + root.setAttribute("class", "gs-dev-tools minimal"); + if (element) { + root.style.top = "calc(100% - 42px)"; + } + } + return root; + }, + _clickedOnce, //we don't preventDefault() on the first mousedown/touchstart/pointerdown so that iframes get focus properly. + _addListener = function(e, type, callback, capture) { + var handler, altType; + if (type === "mousedown" || type === "mouseup") { + e.style.cursor = "pointer"; + } + if (type === "mousedown") { + //some browsers call BOTH mousedown AND touchstart, for example, on a single interaction so we need to skip one of them if both are called within 100ms. + altType = e.onpointerdown !== undefined ? "pointerdown" : e.ontouchstart !== undefined ? "touchstart" : null; + if (altType) { + handler = function(event) { + if (event.target.nodeName.toLowerCase() !== "select" && event.type === altType) { //don't preventDefault() on a up or down by a certain amount. + _shiftSelectedValue = function(element, amount, label) { + var options = element.options, + i = Math.min(options.length - 1, Math.max(0, element.selectedIndex + amount)); + element.selectedIndex = i; + if (label) { + label.innerHTML = options[i].innerHTML; + } + return options[i].value; + }, + _recordedRoot = new TimelineLite({data:"root", id:"Global Timeline", autoRemoveChildren:false, smoothChildTiming:true}), + _recordedTemp = new TimelineLite({data:"root", id:"Global Temp", autoRemoveChildren:false, smoothChildTiming:true}), + _rootTween = TweenLite.to(_recordedRoot, 1, {time:1, ease:Linear.easeNone, data:"root", id:"_rootTween", paused:true, immediateRender:false}), + _rootInstance, _rootIsDirty, _keyboardInstance, + //moves everything from _recordedTemp into _recordedRoot and updates the _rootTween if it is currently controlling the Global timeline (_recordedRoot). _recordedTemp is just a temporary recording area for anything that happens while _recordedRoot is paused. Returns true if the _recordedRoot's duration changed due to the merge. + _merge = function() { + var t = _recordedTemp._first, + duration, next; + if (t) { + if (_rootInstance && _rootInstance.animation() === _recordedRoot) { + duration = _recordedRoot._duration; + while (t) { + next = t._next; + if (!(typeof(t.target) === "function" && t.target === t.vars.onComplete && !t._duration) && !(t.target && t.target._gsIgnore)) { //typically, delayedCalls aren't included in the _recordedTemp, but since the hijacked add() below fires BEFORE TweenLite's constructor sets the target, we couldn't check that target === vars.onComplete there. And Draggable creates a tween with just an onComplete (no onReverseComplete), thus it fails that test. Therefore, we test again here to avoid merging that in. + _recordedRoot.add(t, t._startTime - t._delay); + } else { + SimpleTimeline.prototype.add.call(_globalTimeline, t, t._startTime - t._delay); + } + t = next; + } + return (duration !== _recordedRoot.duration()); + } else { + //if it's not the _recordedRoot that's currently playing ("global timeline"), merge the temp tweens back into the real global timeline instead and kill any that are already completed. No need to keep them recorded. + while (t) { + next = t._next; + if (t._gc || t._totalTime === t._totalDuration) { + t.kill(); + } else { + //SimpleTimeline.prototype.add.call(_globalTimeline, t, t._recordedTime); + //_globalTimeline.add(t, t._startTime - t._delay - _recordedTemp._startTime); + SimpleTimeline.prototype.add.call(_globalTimeline, t, t._startTime - t._delay); + } + t = next; + } + } + } + }, + _updateRootDuration = function() { + if (_rootInstance) { + _rootInstance.update(); + _rootIsDirty = false; + } + TweenLite.ticker.removeEventListener("tick", _updateRootDuration); + }, + + _buildPlayPauseMorph = function(svg) { + var tl = new TimelineLite({data:"root", onComplete:function() { tl.kill(); }}); + tl.to(svg.querySelector(".play-1"), 0.5, {attr:{d:"M5.75,3.13 C5.75,9.79 5.75,16.46 5.75,23.13 4.08,23.13 2.41,23.13 0.75,23.13 0.75,16.46 0.75,9.79 0.75,3.12 2.41,3.12 4.08,3.12 5.75,3.12"}, ease:Power3.easeInOut, rotation:360, transformOrigin:"50% 50%"}) + .to(svg.querySelector(".play-2"), 0.5, {attr:{d:"M16.38,3.13 C16.38,9.79 16.38,16.46 16.38,23.13 14.71,23.13 13.04,23.13 11.38,23.13 11.38,16.46 11.38,9.79 11.38,3.12 13.04,3.12 14.71,3.12 16.38,3.12"}, ease:Power3.easeInOut, rotation:360, transformOrigin:"50% 50%"}, 0.05); + return tl; + }, + + _buildLoopAnimation = function(svg) { + var tl = new TimelineLite({data:"root", paused:true, onComplete:function() { tl.kill(); }}); + tl.to(svg, 0.5, {rotation:360, ease:Power3.easeInOut, transformOrigin:"50% 50%"}) + .to(svg.querySelectorAll(".loop-path"), 0.5, {fill:"#91e600", ease:Linear.easeNone}, 0); + return tl; + }, + + + + + + GSDevTools = function(vars) { + this.vars = vars = vars || {}; + vars.id = vars.id || (typeof(vars.animation) === "string" ? vars.animation : _idSeed++); //try to find a unique ID so that sessionStorage can be mapped to it (otherwise, for example, all the embedded codepens on a page would share the same settings). So if no id is defined, see if there's a string-based "animation" defined. Last of all, we default to a numeric counter that we increment. + _lookup[vars.id + ""] = this; + + if (vars.animation && !_recording && vars.globalSync !== true) { //if the user calls create() and passes in an animation AFTER the initial recording time has elapsed, there's a good chance the animation won't be in the recordedRoot, so we change the default globalSync to false because that's the most intuitive behavior. + vars.globalSync = false; + } + + //GENERAL/UTILITY + var _self = this, + root = _createRootElement(vars.container, vars.minimal, vars.css), + find = function(s) { + return root.querySelector(s); + }, + record = function(key, value) { + if (vars.persist !== false && typeof(sessionStorage) !== "undefined") { + sessionStorage.setItem("gs-dev-" + key + vars.id, value); + } + return value; + }, + recall = function(key) { + var value; + if (vars.persist !== false && typeof(sessionStorage) !== "undefined") { + value = sessionStorage.getItem("gs-dev-" + key + vars.id); + return (key === "animation") ? value : (key === "loop") ? (value === "true") : parseFloat(value); // handle data typing too. + } + }, + + + //SCRUBBER/PROGRESS + playhead = find(".playhead"), + timelineTrack = find(".timeline-track"), + progressBar = find(".progress-bar"), + timeLabel = find(".time"), + durationLabel = find(".duration"), + pixelToTimeRatio, timeAtDragStart, dragged, skipDragUpdates, + progress = 0, + //spits back a common onPress function for anything that's dragged along the timeline (playhead, inPoint, outPoint). The originRatio is a value from 0-1 indicating how far along the x-axis the origin is located (0.5 is in the center, 0 is left, 1 is on right side). limitElement is optional, and sets the bounds such that the element can't be dragged past the limitElement. + onPressTimeline = function(element, originRatio, limitToInOut) { + return function(e) { + var trackBounds = timelineTrack.getBoundingClientRect(), + elementBounds = element.getBoundingClientRect(), + left = elementBounds.width * originRatio, + x = element._gsTransform.x, + minX = trackBounds.left - elementBounds.left - left + x, + maxX = trackBounds.right - elementBounds.right + (elementBounds.width - left) + x, + unlimitedMinX = minX, + limitBounds; + if (limitToInOut) { + if (element !== inPoint) { + limitBounds = inPoint.getBoundingClientRect(); + if (limitBounds.left) { //if inPoint is hidden (like display:none), ignore. + minX += (limitBounds.left + limitBounds.width) - trackBounds.left; + } + } + if (element !== outPoint) { + limitBounds = outPoint.getBoundingClientRect(); + if (limitBounds.left) { //if outPoint is hidden (like display:none), ignore. + maxX -= (trackBounds.left + trackBounds.width) - limitBounds.left; + } + } + } + pausedWhenDragStarted = paused; + this.applyBounds({minX:minX, maxX:maxX}); + //_merge(); + pixelToTimeRatio = linkedAnimation.duration() / trackBounds.width; + timeAtDragStart = -unlimitedMinX * pixelToTimeRatio; + + if (!skipDragUpdates) { + linkedAnimation.pause(timeAtDragStart + pixelToTimeRatio * this.x); + } else { + linkedAnimation.pause(); + } + if (this.target === playhead) { + if (this.activated) { + this.allowEventDefault = false; + } + this.activated = true; + } + dragged = true; + }; + }, + progressDrag = Draggable.create(playhead, { + type:"x", + cursor: "ew-resize", + allowNativeTouchScrolling: false, + allowEventDefault:true, //otherwise, when dragged outside an iframe, the mouseup doesn't bubble up so it could seem "stuck" to the mouse. + onPress: onPressTimeline(playhead, 0.5, true), + onDrag: function() { + var time = timeAtDragStart + pixelToTimeRatio * this.x; + if (time < 0) { + time = 0; + } else if (time > linkedAnimation._duration) { + time = linkedAnimation._duration; + } + if (!skipDragUpdates) { + linkedAnimation.time(time); + } + progressBar.style.width = Math.min(outProgress - inProgress, Math.max(0, (time / linkedAnimation._duration) * 100 - inProgress)) + "%"; + timeLabel.innerHTML = time.toFixed(2); + }, + onRelease: function(e) { + if (!paused) { + linkedAnimation.resume(); + } + } + })[0], + inPoint = find(".in-point"), + outPoint = find(".out-point"), + resetInOut = function() { + inProgress = 0; + outProgress = 100; + inPoint.style.left = "0%"; + outPoint.style.left = "100%"; + record("in", inProgress); + record("out", outProgress); + updateProgress(true); + }, + inProgress = 0, + outProgress = 100, + pausedWhenDragStarted, + inDrag = Draggable.create(inPoint, { + type:"x", + cursor:"ew-resize", + zIndexBoost:false, + allowNativeTouchScrolling: false, + allowEventDefault:true, //otherwise, when dragged outside an iframe, the mouseup doesn't bubble up so it could seem "stuck" to the mouse. + onPress:onPressTimeline(inPoint, 1, true), + onDoubleClick: resetInOut, + onDrag: function() { + inProgress = (timeAtDragStart + pixelToTimeRatio * this.x) / linkedAnimation.duration() * 100; + linkedAnimation.progress(inProgress / 100); + updateProgress(true); + }, + onRelease: function() { + if (inProgress < 0) { + inProgress = 0; + } + _clearSelection(); + //for responsiveness, convert the px-based transform into %-based left position. + inPoint.style.left = inProgress + "%"; + record("in", inProgress); + TweenLite.set(inPoint, {x:0, data:"root", display:"block"}); //set display:block so that it remains visible even when the minimal skin is enabled. + if (!paused) { + linkedAnimation.resume(); + } + } + })[0], + outDrag = Draggable.create(outPoint, { + type:"x", + cursor:"ew-resize", + allowNativeTouchScrolling: false, + allowEventDefault:true, //otherwise, when dragged outside an iframe, the mouseup doesn't bubble up so it could seem "stuck" to the mouse. + zIndexBoost:false, + onPress:onPressTimeline(outPoint, 0, true), + onDoubleClick: resetInOut, + onDrag: function() { + outProgress = (timeAtDragStart + pixelToTimeRatio * this.x) / linkedAnimation.duration() * 100; + linkedAnimation.progress(outProgress / 100); + updateProgress(true); + }, + onRelease: function() { + if (outProgress > 100) { + outProgress = 100; + } + _clearSelection(); + //for responsiveness, convert the px-based transform into %-based left position. + outPoint.style.left = outProgress + "%"; + record("out", outProgress); + TweenLite.set(outPoint, {x:0, data:"root", display:"block"}); //set display:block so that it remains visible even when the minimal skin is enabled. + if (!pausedWhenDragStarted) { + play(); + linkedAnimation.resume(); + } + } + })[0], + updateProgress = function(force) { + if (progressDrag.isPressed && !force) { + return; + } + var p = (!loopEnabled && selectedAnimation._repeat === -1) ? selectedAnimation.totalTime() / selectedAnimation.duration() * 100 : (linkedAnimation.progress() * 100) || 0, + repeatDelayPhase = (selectedAnimation._repeat && selectedAnimation._repeatDelay && selectedAnimation.totalTime() % (selectedAnimation.duration() + selectedAnimation._repeatDelay) > selectedAnimation.duration()); + if (p > 100) { + p = 100; + } + if (p >= outProgress) { + if (loopEnabled && !linkedAnimation.paused() && !progressDrag.isDragging) { + if (!repeatDelayPhase) { + p = inProgress; + if (linkedAnimation.target === selectedAnimation) { //in case there are callbacks on the timeline, when we jump back to the start we should seek() so that the playhead doesn't drag [backward] past those and trigger them. + linkedAnimation.target.seek(startTime + ((endTime - startTime) * inProgress / 100)); + } + if (selectedAnimation._repeat > 0 && !inProgress && outProgress === 100) { + if (selectedAnimation.totalProgress() === 1) { + linkedAnimation.totalProgress(0, true).resume(); + } + } else { + linkedAnimation.progress(p / 100, true).resume(); + } + } + } else { + if (p !== outProgress || selectedAnimation._repeat === -1) { + p = outProgress; + linkedAnimation.progress(p / 100); + } + if (!paused && (selectedAnimation.totalProgress() === 1 || selectedAnimation._repeat === -1)) { + pause(); + } + } + + } else if (p < inProgress) { + p = inProgress; + linkedAnimation.progress(p / 100, true); + } + if (p !== progress || force) { + progressBar.style.left = inProgress + "%"; + progressBar.style.width = Math.max(0, p - inProgress) + "%"; + playhead.style.left = p + "%"; + timeLabel.innerHTML = linkedAnimation._time.toFixed(2); + durationLabel.innerHTML = linkedAnimation._duration.toFixed(2); + if (dragged) { + playhead.style.transform = "translate(-50%,0)"; + playhead._gsTransform.x = 0; + playhead._gsTransform.xPercent = -50; + dragged = false; + } + progress = p; + } else if (linkedAnimation._paused !== paused) { //like if the user has an addPause() in the middle of the animation. + togglePlayPause(); + } + }, + onPressSeekBar = function(e) { + if (progressDrag.isPressed) { + return; + } + var bounds = e.target.getBoundingClientRect(), + x = (e.changedTouches ? e.changedTouches[0] : e).clientX, + p = ((x - bounds.left) / bounds.width) * 100; + if (p < inProgress) { + inProgress = p = Math.max(0, p); + inPoint.style.left = inProgress + "%"; + inDrag.startDrag(e); + return; + } else if (p > outProgress) { + outProgress = p = Math.min(100, p); + outPoint.style.left = outProgress + "%"; + outDrag.startDrag(e); + return; + } + linkedAnimation.progress(p / 100).pause(); + updateProgress(true); + progressDrag.startDrag(e); + }, + + + + //PLAY/PAUSE button + playPauseButton = find(".play-pause"), + playPauseMorph = _buildPlayPauseMorph(playPauseButton), + paused = false, + play = function() { + if (linkedAnimation.progress() >= outProgress / 100) { + if (linkedAnimation.target === selectedAnimation) { //in case there are callbacks on the timeline, when we jump back to the start we should seek() so that the playhead doesn't drag [backward] past those and trigger them. + linkedAnimation.target.seek(startTime + ((endTime - startTime) * inProgress / 100)); + } + if (linkedAnimation._repeat && !inProgress) { + linkedAnimation.totalProgress(0, true); //for repeating animations, don't get stuck in the last iteration - jump all the way back to the start. + } else { + linkedAnimation.progress(inProgress / 100, true); + } + } + playPauseMorph.play(); + linkedAnimation.resume(); + if (paused) { + _self.update(); + } + paused = false; + }, + pause = function() { + playPauseMorph.reverse(); + if (linkedAnimation) { + linkedAnimation.pause(); + } + paused = true; + }, + togglePlayPause = function() { + if (paused) { + play(); + } else { + pause(); + } + }, + + + + //REWIND button + onPressRewind = function(e) { + if (progressDrag.isPressed) { + return; + } + //_self.update(); + if (linkedAnimation.target === selectedAnimation) { //in case there are callbacks on the timeline, when we jump back to the start we should seek() so that the playhead doesn't drag [backward] past those and trigger them. + linkedAnimation.target.seek(startTime + ((endTime - startTime) * inProgress / 100)); + } + linkedAnimation.progress(inProgress / 100, true); + if (!paused) { + linkedAnimation.resume(); + } + }, + + + + //LOOP button + loopButton = find(".loop"), + loopAnimation = _buildLoopAnimation(loopButton), + loopEnabled, + loop = function(value) { + loopEnabled = value; + record("loop", loopEnabled); + if (loopEnabled) { + loopAnimation.play(); + if (linkedAnimation.progress() >= outProgress / 100) { + if (linkedAnimation.target === selectedAnimation) { //in case there are callbacks on the timeline, when we jump back to the start we should seek() so that the playhead doesn't drag [backward] past those and trigger them. + linkedAnimation.target.seek(startTime + ((endTime - startTime) * inProgress / 100)); + } + if (selectedAnimation._repeat && !inProgress && outProgress === 100) { + linkedAnimation.totalProgress(0, true); + } else { + linkedAnimation.progress(inProgress / 100, true); + } + play(); + } + } else { + loopAnimation.reverse(); + } + }, + + toggleLoop = function() { + loop(!loopEnabled); + }, + + + + //ANIMATIONS list + list = find(".animation-list"), + animationLabel = find(".animation-label"), + selectedAnimation, //the currently selected animation + linkedAnimation, //the animation that's linked to all the controls and scrubber. This is always _rootTween if globalSync is true, so it can be different than the selectedAnimation! + declaredAnimation, //whatever the user defines in the config object initially (often this will be null). If the user defines a string, it'll be resolved to a real Animation instance for this variable. + startTime, endTime, + updateList = function() { + var animations = _getChildrenOf((declaredAnimation && vars.globalSync === false) ? declaredAnimation : _recordedRoot, true), + options = list.children, + matches = 0, + option, i; + if (declaredAnimation && vars.globalSync === false) { + animations.unshift(declaredAnimation); + } else if (!vars.hideGlobalTimeline) { + animations.unshift(_recordedRoot); + } + for (i = 0; i < animations.length; i++) { + option = options[i] || _createElement("option", list); + option.animation = animations[i]; + matches = (i && animations[i].vars.id === animations[i-1].vars.id) ? matches + 1 : 0; + option.setAttribute("value", (option.innerHTML = animations[i].vars.id + (matches ? " [" + matches + "]" : (animations[i+1] && animations[i+1].vars.id === animations[i].vars.id) ? " [0]" : ""))); + } + for (; i < options.length; i++) { + list.removeChild(options[i]); + } + }, + animation = function(anim) { + var ts = parseFloat(timeScale.options[timeScale.selectedIndex].value) || 1, + tl, maxDuration; + if (!arguments.length) { + return selectedAnimation; + } + if (typeof(anim) === "string") { + anim = _getAnimationById(anim); + } + //console.log("animation() ", anim.vars.id); + if (!(anim instanceof Animation)) { + console.log("GSDevTools error: invalid animation."); + } + if (anim === selectedAnimation) { + return; + } + if (selectedAnimation) { + selectedAnimation._inProgress = inProgress; + selectedAnimation._outProgress = outProgress; + } + selectedAnimation = anim; + if (linkedAnimation) { + ts = linkedAnimation.timeScale(); + if (linkedAnimation.target === declaredAnimation) { + declaredAnimation.resume(); + linkedAnimation.kill(); + } + } + inProgress = selectedAnimation._inProgress || 0; + outProgress = selectedAnimation._outProgress || 100; + inPoint.style.left = inProgress + "%"; + outPoint.style.left = outProgress + "%"; + if (_fullyInitialized) { //don't record inProgress/outProgress unless we're fully instantiated because people may call GSDevTools.create() before creating/defining their animations, thus the inTime/outTime may not exist yet. + record("animation", selectedAnimation.vars.id); + record("in", inProgress); + record("out", outProgress); + } + startTime = 0; + maxDuration = Math.min(1000, vars.maxDuration || 1000, _getClippedDuration(selectedAnimation)); + if (selectedAnimation === _recordedRoot || vars.globalSync !== false) { + _merge(); + linkedAnimation = _rootTween; + if (_rootInstance && _rootInstance !== _self) { + console.log("Error: GSDevTools can only have one instance that's globally synchronized."); + } + _rootInstance = _self; + //_recording = true; + if (selectedAnimation !== _recordedRoot) { + tl = selectedAnimation; + endTime = tl.totalDuration(); + if (endTime > 99999999) { //in the case of an infinitely repeating animation, just use a single iteration's duration instead. + endTime = tl.duration(); + } + while (tl.timeline.timeline) { + startTime = (startTime / tl._timeScale) + tl._startTime; + endTime = (endTime / tl._timeScale) + tl._startTime; + tl = tl.timeline; + } + } else { + endTime = _recordedRoot.duration(); + } + if (endTime - startTime > maxDuration) { //cap end time at 1000 because it doesn't seem reasonable to accommodate super long stuff. + endTime = startTime + maxDuration; + } + _recordedRoot.pause(startTime); + _rootTween.vars.time = endTime; + _rootTween.invalidate(); + _rootTween.duration(endTime - startTime).timeScale(ts); + //wait for a tick before starting because some browsers freeze things immediately following a . + e.target.blur(); + } + if (paused) { + play(); + } + }, + + + + //TIMESCALE button + timeScale = find(".time-scale select"), + timeScaleLabel = find(".time-scale-label"), + onChangeTimeScale = function(e) { + var ts = parseFloat(timeScale.options[timeScale.selectedIndex].value) || 1; + linkedAnimation.timeScale(ts); + record("timeScale", ts); + if (!paused) { + if (linkedAnimation.progress() >= outProgress / 100) { + if (linkedAnimation.target === selectedAnimation) { //in case there are callbacks on the timeline, when we jump back to the start we should seek() so that the playhead doesn't drag [backward] past those and trigger them. + linkedAnimation.target.seek(startTime + ((endTime - startTime) * inProgress / 100)); + } + linkedAnimation.progress(inProgress / 100, true).pause(); + } else { + linkedAnimation.pause(); + } + TweenLite.delayedCall(0.01, function() { + linkedAnimation.resume(); + }); + } + timeScaleLabel.innerHTML = ts + "x"; + if (timeScale.blur) { //so that if an option is selected, and then the user tries to hit the up/down arrow, it doesn't just try selecting something else in the