UNPKG

omim

Version:

Material Design for Omi.

1,645 lines (1,374 loc) 83.3 kB
(function webpackUniversalModuleDefinition(root, factory) { if(typeof exports === 'object' && typeof module === 'object') module.exports = factory(require("omi")); else if(typeof define === 'function' && define.amd) define(["omi"], factory); else if(typeof exports === 'object') exports["MPathProgress"] = factory(require("omi")); else root["MPathProgress"] = factory(root["Omi"]); })(window, function(__WEBPACK_EXTERNAL_MODULE_omi__) { return /******/ (function(modules) { // webpackBootstrap /******/ // The module cache /******/ var installedModules = {}; /******/ /******/ // The require function /******/ function __webpack_require__(moduleId) { /******/ /******/ // Check if module is in cache /******/ if(installedModules[moduleId]) { /******/ return installedModules[moduleId].exports; /******/ } /******/ // Create a new module (and put it into the cache) /******/ var module = installedModules[moduleId] = { /******/ i: moduleId, /******/ l: false, /******/ exports: {} /******/ }; /******/ /******/ // Execute the module function /******/ modules[moduleId].call(module.exports, module, module.exports, __webpack_require__); /******/ /******/ // Flag the module as loaded /******/ module.l = true; /******/ /******/ // Return the exports of the module /******/ return module.exports; /******/ } /******/ /******/ /******/ // expose the modules object (__webpack_modules__) /******/ __webpack_require__.m = modules; /******/ /******/ // expose the module cache /******/ __webpack_require__.c = installedModules; /******/ /******/ // define getter function for harmony exports /******/ __webpack_require__.d = function(exports, name, getter) { /******/ if(!__webpack_require__.o(exports, name)) { /******/ Object.defineProperty(exports, name, { enumerable: true, get: getter }); /******/ } /******/ }; /******/ /******/ // define __esModule on exports /******/ __webpack_require__.r = function(exports) { /******/ if(typeof Symbol !== 'undefined' && Symbol.toStringTag) { /******/ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' }); /******/ } /******/ Object.defineProperty(exports, '__esModule', { value: true }); /******/ }; /******/ /******/ // create a fake namespace object /******/ // mode & 1: value is a module id, require it /******/ // mode & 2: merge all properties of value into the ns /******/ // mode & 4: return value when already ns object /******/ // mode & 8|1: behave like require /******/ __webpack_require__.t = function(value, mode) { /******/ if(mode & 1) value = __webpack_require__(value); /******/ if(mode & 8) return value; /******/ if((mode & 4) && typeof value === 'object' && value && value.__esModule) return value; /******/ var ns = Object.create(null); /******/ __webpack_require__.r(ns); /******/ Object.defineProperty(ns, 'default', { enumerable: true, value: value }); /******/ if(mode & 2 && typeof value != 'string') for(var key in value) __webpack_require__.d(ns, key, function(key) { return value[key]; }.bind(null, key)); /******/ return ns; /******/ }; /******/ /******/ // getDefaultExport function for compatibility with non-harmony modules /******/ __webpack_require__.n = function(module) { /******/ var getter = module && module.__esModule ? /******/ function getDefault() { return module['default']; } : /******/ function getModuleExports() { return module; }; /******/ __webpack_require__.d(getter, 'a', getter); /******/ return getter; /******/ }; /******/ /******/ // Object.prototype.hasOwnProperty.call /******/ __webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); }; /******/ /******/ // __webpack_public_path__ /******/ __webpack_require__.p = ""; /******/ /******/ /******/ // Load entry module and return exports /******/ return __webpack_require__(__webpack_require__.s = "./src/path-progress/index.tsx"); /******/ }) /************************************************************************/ /******/ ({ /***/ "./node_modules/progressbar.js/src/circle.js": /*!***************************************************!*\ !*** ./node_modules/progressbar.js/src/circle.js ***! \***************************************************/ /*! no static exports found */ /***/ (function(module, exports, __webpack_require__) { // Circle shaped progress bar var Shape = __webpack_require__(/*! ./shape */ "./node_modules/progressbar.js/src/shape.js"); var utils = __webpack_require__(/*! ./utils */ "./node_modules/progressbar.js/src/utils.js"); var Circle = function Circle(container, options) { // Use two arcs to form a circle // See this answer http://stackoverflow.com/a/10477334/1446092 this._pathTemplate = 'M 50,50 m 0,-{radius}' + ' a {radius},{radius} 0 1 1 0,{2radius}' + ' a {radius},{radius} 0 1 1 0,-{2radius}'; this.containerAspectRatio = 1; Shape.apply(this, arguments); }; Circle.prototype = new Shape(); Circle.prototype.constructor = Circle; Circle.prototype._pathString = function _pathString(opts) { var widthOfWider = opts.strokeWidth; if (opts.trailWidth && opts.trailWidth > opts.strokeWidth) { widthOfWider = opts.trailWidth; } var r = 50 - widthOfWider / 2; return utils.render(this._pathTemplate, { radius: r, '2radius': r * 2 }); }; Circle.prototype._trailString = function _trailString(opts) { return this._pathString(opts); }; module.exports = Circle; /***/ }), /***/ "./node_modules/progressbar.js/src/line.js": /*!*************************************************!*\ !*** ./node_modules/progressbar.js/src/line.js ***! \*************************************************/ /*! no static exports found */ /***/ (function(module, exports, __webpack_require__) { // Line shaped progress bar var Shape = __webpack_require__(/*! ./shape */ "./node_modules/progressbar.js/src/shape.js"); var utils = __webpack_require__(/*! ./utils */ "./node_modules/progressbar.js/src/utils.js"); var Line = function Line(container, options) { this._pathTemplate = 'M 0,{center} L 100,{center}'; Shape.apply(this, arguments); }; Line.prototype = new Shape(); Line.prototype.constructor = Line; Line.prototype._initializeSvg = function _initializeSvg(svg, opts) { svg.setAttribute('viewBox', '0 0 100 ' + opts.strokeWidth); svg.setAttribute('preserveAspectRatio', 'none'); }; Line.prototype._pathString = function _pathString(opts) { return utils.render(this._pathTemplate, { center: opts.strokeWidth / 2 }); }; Line.prototype._trailString = function _trailString(opts) { return this._pathString(opts); }; module.exports = Line; /***/ }), /***/ "./node_modules/progressbar.js/src/main.js": /*!*************************************************!*\ !*** ./node_modules/progressbar.js/src/main.js ***! \*************************************************/ /*! no static exports found */ /***/ (function(module, exports, __webpack_require__) { module.exports = { // Higher level API, different shaped progress bars Line: __webpack_require__(/*! ./line */ "./node_modules/progressbar.js/src/line.js"), Circle: __webpack_require__(/*! ./circle */ "./node_modules/progressbar.js/src/circle.js"), SemiCircle: __webpack_require__(/*! ./semicircle */ "./node_modules/progressbar.js/src/semicircle.js"), // Lower level API to use any SVG path Path: __webpack_require__(/*! ./path */ "./node_modules/progressbar.js/src/path.js"), // Base-class for creating new custom shapes // to be in line with the API of built-in shapes // Undocumented. Shape: __webpack_require__(/*! ./shape */ "./node_modules/progressbar.js/src/shape.js"), // Internal utils, undocumented. utils: __webpack_require__(/*! ./utils */ "./node_modules/progressbar.js/src/utils.js") }; /***/ }), /***/ "./node_modules/progressbar.js/src/path.js": /*!*************************************************!*\ !*** ./node_modules/progressbar.js/src/path.js ***! \*************************************************/ /*! no static exports found */ /***/ (function(module, exports, __webpack_require__) { // Lower level API to animate any kind of svg path var Tweenable = __webpack_require__(/*! shifty */ "./node_modules/shifty/dist/shifty.js"); var utils = __webpack_require__(/*! ./utils */ "./node_modules/progressbar.js/src/utils.js"); var EASING_ALIASES = { easeIn: 'easeInCubic', easeOut: 'easeOutCubic', easeInOut: 'easeInOutCubic' }; var Path = function Path(path, opts) { // Throw a better error if not initialized with `new` keyword if (!(this instanceof Path)) { throw new Error('Constructor was called without new keyword'); } // Default parameters for animation opts = utils.extend({ duration: 800, easing: 'linear', from: {}, to: {}, step: function() {} }, opts); var element; if (utils.isString(path)) { element = document.querySelector(path); } else { element = path; } // Reveal .path as public attribute this.path = element; this._opts = opts; this._tweenable = null; // Set up the starting positions var length = this.path.getTotalLength(); this.path.style.strokeDasharray = length + ' ' + length; this.set(0); }; Path.prototype.value = function value() { var offset = this._getComputedDashOffset(); var length = this.path.getTotalLength(); var progress = 1 - offset / length; // Round number to prevent returning very small number like 1e-30, which // is practically 0 return parseFloat(progress.toFixed(6), 10); }; Path.prototype.set = function set(progress) { this.stop(); this.path.style.strokeDashoffset = this._progressToOffset(progress); var step = this._opts.step; if (utils.isFunction(step)) { var easing = this._easing(this._opts.easing); var values = this._calculateTo(progress, easing); var reference = this._opts.shape || this; step(values, reference, this._opts.attachment); } }; Path.prototype.stop = function stop() { this._stopTween(); this.path.style.strokeDashoffset = this._getComputedDashOffset(); }; // Method introduced here: // http://jakearchibald.com/2013/animated-line-drawing-svg/ Path.prototype.animate = function animate(progress, opts, cb) { opts = opts || {}; if (utils.isFunction(opts)) { cb = opts; opts = {}; } var passedOpts = utils.extend({}, opts); // Copy default opts to new object so defaults are not modified var defaultOpts = utils.extend({}, this._opts); opts = utils.extend(defaultOpts, opts); var shiftyEasing = this._easing(opts.easing); var values = this._resolveFromAndTo(progress, shiftyEasing, passedOpts); this.stop(); // Trigger a layout so styles are calculated & the browser // picks up the starting position before animating this.path.getBoundingClientRect(); var offset = this._getComputedDashOffset(); var newOffset = this._progressToOffset(progress); var self = this; this._tweenable = new Tweenable(); this._tweenable.tween({ from: utils.extend({ offset: offset }, values.from), to: utils.extend({ offset: newOffset }, values.to), duration: opts.duration, easing: shiftyEasing, step: function(state) { self.path.style.strokeDashoffset = state.offset; var reference = opts.shape || self; opts.step(state, reference, opts.attachment); }, finish: function(state) { if (utils.isFunction(cb)) { cb(); } } }); }; Path.prototype._getComputedDashOffset = function _getComputedDashOffset() { var computedStyle = window.getComputedStyle(this.path, null); return parseFloat(computedStyle.getPropertyValue('stroke-dashoffset'), 10); }; Path.prototype._progressToOffset = function _progressToOffset(progress) { var length = this.path.getTotalLength(); return length - progress * length; }; // Resolves from and to values for animation. Path.prototype._resolveFromAndTo = function _resolveFromAndTo(progress, easing, opts) { if (opts.from && opts.to) { return { from: opts.from, to: opts.to }; } return { from: this._calculateFrom(easing), to: this._calculateTo(progress, easing) }; }; // Calculate `from` values from options passed at initialization Path.prototype._calculateFrom = function _calculateFrom(easing) { return Tweenable.interpolate(this._opts.from, this._opts.to, this.value(), easing); }; // Calculate `to` values from options passed at initialization Path.prototype._calculateTo = function _calculateTo(progress, easing) { return Tweenable.interpolate(this._opts.from, this._opts.to, progress, easing); }; Path.prototype._stopTween = function _stopTween() { if (this._tweenable !== null) { this._tweenable.stop(); this._tweenable = null; } }; Path.prototype._easing = function _easing(easing) { if (EASING_ALIASES.hasOwnProperty(easing)) { return EASING_ALIASES[easing]; } return easing; }; module.exports = Path; /***/ }), /***/ "./node_modules/progressbar.js/src/semicircle.js": /*!*******************************************************!*\ !*** ./node_modules/progressbar.js/src/semicircle.js ***! \*******************************************************/ /*! no static exports found */ /***/ (function(module, exports, __webpack_require__) { // Semi-SemiCircle shaped progress bar var Shape = __webpack_require__(/*! ./shape */ "./node_modules/progressbar.js/src/shape.js"); var Circle = __webpack_require__(/*! ./circle */ "./node_modules/progressbar.js/src/circle.js"); var utils = __webpack_require__(/*! ./utils */ "./node_modules/progressbar.js/src/utils.js"); var SemiCircle = function SemiCircle(container, options) { // Use one arc to form a SemiCircle // See this answer http://stackoverflow.com/a/10477334/1446092 this._pathTemplate = 'M 50,50 m -{radius},0' + ' a {radius},{radius} 0 1 1 {2radius},0'; this.containerAspectRatio = 2; Shape.apply(this, arguments); }; SemiCircle.prototype = new Shape(); SemiCircle.prototype.constructor = SemiCircle; SemiCircle.prototype._initializeSvg = function _initializeSvg(svg, opts) { svg.setAttribute('viewBox', '0 0 100 50'); }; SemiCircle.prototype._initializeTextContainer = function _initializeTextContainer( opts, container, textContainer ) { if (opts.text.style) { // Reset top style textContainer.style.top = 'auto'; textContainer.style.bottom = '0'; if (opts.text.alignToBottom) { utils.setStyle(textContainer, 'transform', 'translate(-50%, 0)'); } else { utils.setStyle(textContainer, 'transform', 'translate(-50%, 50%)'); } } }; // Share functionality with Circle, just have different path SemiCircle.prototype._pathString = Circle.prototype._pathString; SemiCircle.prototype._trailString = Circle.prototype._trailString; module.exports = SemiCircle; /***/ }), /***/ "./node_modules/progressbar.js/src/shape.js": /*!**************************************************!*\ !*** ./node_modules/progressbar.js/src/shape.js ***! \**************************************************/ /*! no static exports found */ /***/ (function(module, exports, __webpack_require__) { // Base object for different progress bar shapes var Path = __webpack_require__(/*! ./path */ "./node_modules/progressbar.js/src/path.js"); var utils = __webpack_require__(/*! ./utils */ "./node_modules/progressbar.js/src/utils.js"); var DESTROYED_ERROR = 'Object is destroyed'; var Shape = function Shape(container, opts) { // Throw a better error if progress bars are not initialized with `new` // keyword if (!(this instanceof Shape)) { throw new Error('Constructor was called without new keyword'); } // Prevent calling constructor without parameters so inheritance // works correctly. To understand, this is how Shape is inherited: // // Line.prototype = new Shape(); // // We just want to set the prototype for Line. if (arguments.length === 0) { return; } // Default parameters for progress bar creation this._opts = utils.extend({ color: '#555', strokeWidth: 1.0, trailColor: null, trailWidth: null, fill: null, text: { style: { color: null, position: 'absolute', left: '50%', top: '50%', padding: 0, margin: 0, transform: { prefix: true, value: 'translate(-50%, -50%)' } }, autoStyleContainer: true, alignToBottom: true, value: null, className: 'progressbar-text' }, svgStyle: { display: 'block', width: '100%' }, warnings: false }, opts, true); // Use recursive extend // If user specifies e.g. svgStyle or text style, the whole object // should replace the defaults to make working with styles easier if (utils.isObject(opts) && opts.svgStyle !== undefined) { this._opts.svgStyle = opts.svgStyle; } if (utils.isObject(opts) && utils.isObject(opts.text) && opts.text.style !== undefined) { this._opts.text.style = opts.text.style; } var svgView = this._createSvgView(this._opts); var element; if (utils.isString(container)) { element = document.querySelector(container); } else { element = container; } if (!element) { throw new Error('Container does not exist: ' + container); } this._container = element; this._container.appendChild(svgView.svg); if (this._opts.warnings) { this._warnContainerAspectRatio(this._container); } if (this._opts.svgStyle) { utils.setStyles(svgView.svg, this._opts.svgStyle); } // Expose public attributes before Path initialization this.svg = svgView.svg; this.path = svgView.path; this.trail = svgView.trail; this.text = null; var newOpts = utils.extend({ attachment: undefined, shape: this }, this._opts); this._progressPath = new Path(svgView.path, newOpts); if (utils.isObject(this._opts.text) && this._opts.text.value !== null) { this.setText(this._opts.text.value); } }; Shape.prototype.animate = function animate(progress, opts, cb) { if (this._progressPath === null) { throw new Error(DESTROYED_ERROR); } this._progressPath.animate(progress, opts, cb); }; Shape.prototype.stop = function stop() { if (this._progressPath === null) { throw new Error(DESTROYED_ERROR); } // Don't crash if stop is called inside step function if (this._progressPath === undefined) { return; } this._progressPath.stop(); }; Shape.prototype.destroy = function destroy() { if (this._progressPath === null) { throw new Error(DESTROYED_ERROR); } this.stop(); this.svg.parentNode.removeChild(this.svg); this.svg = null; this.path = null; this.trail = null; this._progressPath = null; if (this.text !== null) { this.text.parentNode.removeChild(this.text); this.text = null; } }; Shape.prototype.set = function set(progress) { if (this._progressPath === null) { throw new Error(DESTROYED_ERROR); } this._progressPath.set(progress); }; Shape.prototype.value = function value() { if (this._progressPath === null) { throw new Error(DESTROYED_ERROR); } if (this._progressPath === undefined) { return 0; } return this._progressPath.value(); }; Shape.prototype.setText = function setText(newText) { if (this._progressPath === null) { throw new Error(DESTROYED_ERROR); } if (this.text === null) { // Create new text node this.text = this._createTextContainer(this._opts, this._container); this._container.appendChild(this.text); } // Remove previous text and add new if (utils.isObject(newText)) { utils.removeChildren(this.text); this.text.appendChild(newText); } else { this.text.innerHTML = newText; } }; Shape.prototype._createSvgView = function _createSvgView(opts) { var svg = document.createElementNS('http://www.w3.org/2000/svg', 'svg'); this._initializeSvg(svg, opts); var trailPath = null; // Each option listed in the if condition are 'triggers' for creating // the trail path if (opts.trailColor || opts.trailWidth) { trailPath = this._createTrail(opts); svg.appendChild(trailPath); } var path = this._createPath(opts); svg.appendChild(path); return { svg: svg, path: path, trail: trailPath }; }; Shape.prototype._initializeSvg = function _initializeSvg(svg, opts) { svg.setAttribute('viewBox', '0 0 100 100'); }; Shape.prototype._createPath = function _createPath(opts) { var pathString = this._pathString(opts); return this._createPathElement(pathString, opts); }; Shape.prototype._createTrail = function _createTrail(opts) { // Create path string with original passed options var pathString = this._trailString(opts); // Prevent modifying original var newOpts = utils.extend({}, opts); // Defaults for parameters which modify trail path if (!newOpts.trailColor) { newOpts.trailColor = '#eee'; } if (!newOpts.trailWidth) { newOpts.trailWidth = newOpts.strokeWidth; } newOpts.color = newOpts.trailColor; newOpts.strokeWidth = newOpts.trailWidth; // When trail path is set, fill must be set for it instead of the // actual path to prevent trail stroke from clipping newOpts.fill = null; return this._createPathElement(pathString, newOpts); }; Shape.prototype._createPathElement = function _createPathElement(pathString, opts) { var path = document.createElementNS('http://www.w3.org/2000/svg', 'path'); path.setAttribute('d', pathString); path.setAttribute('stroke', opts.color); path.setAttribute('stroke-width', opts.strokeWidth); if (opts.fill) { path.setAttribute('fill', opts.fill); } else { path.setAttribute('fill-opacity', '0'); } return path; }; Shape.prototype._createTextContainer = function _createTextContainer(opts, container) { var textContainer = document.createElement('div'); textContainer.className = opts.text.className; var textStyle = opts.text.style; if (textStyle) { if (opts.text.autoStyleContainer) { container.style.position = 'relative'; } utils.setStyles(textContainer, textStyle); // Default text color to progress bar's color if (!textStyle.color) { textContainer.style.color = opts.color; } } this._initializeTextContainer(opts, container, textContainer); return textContainer; }; // Give custom shapes possibility to modify text element Shape.prototype._initializeTextContainer = function(opts, container, element) { // By default, no-op // Custom shapes should respect API options, such as text.style }; Shape.prototype._pathString = function _pathString(opts) { throw new Error('Override this function for each progress bar'); }; Shape.prototype._trailString = function _trailString(opts) { throw new Error('Override this function for each progress bar'); }; Shape.prototype._warnContainerAspectRatio = function _warnContainerAspectRatio(container) { if (!this.containerAspectRatio) { return; } var computedStyle = window.getComputedStyle(container, null); var width = parseFloat(computedStyle.getPropertyValue('width'), 10); var height = parseFloat(computedStyle.getPropertyValue('height'), 10); if (!utils.floatEquals(this.containerAspectRatio, width / height)) { console.warn( 'Incorrect aspect ratio of container', '#' + container.id, 'detected:', computedStyle.getPropertyValue('width') + '(width)', '/', computedStyle.getPropertyValue('height') + '(height)', '=', width / height ); console.warn( 'Aspect ratio of should be', this.containerAspectRatio ); } }; module.exports = Shape; /***/ }), /***/ "./node_modules/progressbar.js/src/utils.js": /*!**************************************************!*\ !*** ./node_modules/progressbar.js/src/utils.js ***! \**************************************************/ /*! no static exports found */ /***/ (function(module, exports) { // Utility functions var PREFIXES = 'Webkit Moz O ms'.split(' '); var FLOAT_COMPARISON_EPSILON = 0.001; // Copy all attributes from source object to destination object. // destination object is mutated. function extend(destination, source, recursive) { destination = destination || {}; source = source || {}; recursive = recursive || false; for (var attrName in source) { if (source.hasOwnProperty(attrName)) { var destVal = destination[attrName]; var sourceVal = source[attrName]; if (recursive && isObject(destVal) && isObject(sourceVal)) { destination[attrName] = extend(destVal, sourceVal, recursive); } else { destination[attrName] = sourceVal; } } } return destination; } // Renders templates with given variables. Variables must be surrounded with // braces without any spaces, e.g. {variable} // All instances of variable placeholders will be replaced with given content // Example: // render('Hello, {message}!', {message: 'world'}) function render(template, vars) { var rendered = template; for (var key in vars) { if (vars.hasOwnProperty(key)) { var val = vars[key]; var regExpString = '\\{' + key + '\\}'; var regExp = new RegExp(regExpString, 'g'); rendered = rendered.replace(regExp, val); } } return rendered; } function setStyle(element, style, value) { var elStyle = element.style; // cache for performance for (var i = 0; i < PREFIXES.length; ++i) { var prefix = PREFIXES[i]; elStyle[prefix + capitalize(style)] = value; } elStyle[style] = value; } function setStyles(element, styles) { forEachObject(styles, function(styleValue, styleName) { // Allow disabling some individual styles by setting them // to null or undefined if (styleValue === null || styleValue === undefined) { return; } // If style's value is {prefix: true, value: '50%'}, // Set also browser prefixed styles if (isObject(styleValue) && styleValue.prefix === true) { setStyle(element, styleName, styleValue.value); } else { element.style[styleName] = styleValue; } }); } function capitalize(text) { return text.charAt(0).toUpperCase() + text.slice(1); } function isString(obj) { return typeof obj === 'string' || obj instanceof String; } function isFunction(obj) { return typeof obj === 'function'; } function isArray(obj) { return Object.prototype.toString.call(obj) === '[object Array]'; } // Returns true if `obj` is object as in {a: 1, b: 2}, not if it's function or // array function isObject(obj) { if (isArray(obj)) { return false; } var type = typeof obj; return type === 'object' && !!obj; } function forEachObject(object, callback) { for (var key in object) { if (object.hasOwnProperty(key)) { var val = object[key]; callback(val, key); } } } function floatEquals(a, b) { return Math.abs(a - b) < FLOAT_COMPARISON_EPSILON; } // https://coderwall.com/p/nygghw/don-t-use-innerhtml-to-empty-dom-elements function removeChildren(el) { while (el.firstChild) { el.removeChild(el.firstChild); } } module.exports = { extend: extend, render: render, setStyle: setStyle, setStyles: setStyles, capitalize: capitalize, isString: isString, isFunction: isFunction, isObject: isObject, forEachObject: forEachObject, floatEquals: floatEquals, removeChildren: removeChildren }; /***/ }), /***/ "./node_modules/shifty/dist/shifty.js": /*!********************************************!*\ !*** ./node_modules/shifty/dist/shifty.js ***! \********************************************/ /*! no static exports found */ /***/ (function(module, exports, __webpack_require__) { /* shifty - v1.5.3 - 2016-11-29 - http://jeremyckahn.github.io/shifty */ ;(function () { var root = this || Function('return this')(); /** * Shifty Core * By Jeremy Kahn - jeremyckahn@gmail.com */ var Tweenable = (function () { 'use strict'; // Aliases that get defined later in this function var formula; // CONSTANTS var DEFAULT_SCHEDULE_FUNCTION; var DEFAULT_EASING = 'linear'; var DEFAULT_DURATION = 500; var UPDATE_TIME = 1000 / 60; var _now = Date.now ? Date.now : function () {return +new Date();}; var now = typeof SHIFTY_DEBUG_NOW !== 'undefined' ? SHIFTY_DEBUG_NOW : _now; if (typeof window !== 'undefined') { // requestAnimationFrame() shim by Paul Irish (modified for Shifty) // http://paulirish.com/2011/requestanimationframe-for-smart-animating/ DEFAULT_SCHEDULE_FUNCTION = window.requestAnimationFrame || window.webkitRequestAnimationFrame || window.oRequestAnimationFrame || window.msRequestAnimationFrame || (window.mozCancelRequestAnimationFrame && window.mozRequestAnimationFrame) || setTimeout; } else { DEFAULT_SCHEDULE_FUNCTION = setTimeout; } function noop () { // NOOP! } /** * Handy shortcut for doing a for-in loop. This is not a "normal" each * function, it is optimized for Shifty. The iterator function only receives * the property name, not the value. * @param {Object} obj * @param {Function(string)} fn * @private */ function each (obj, fn) { var key; for (key in obj) { if (Object.hasOwnProperty.call(obj, key)) { fn(key); } } } /** * Perform a shallow copy of Object properties. * @param {Object} targetObject The object to copy into * @param {Object} srcObject The object to copy from * @return {Object} A reference to the augmented `targetObj` Object * @private */ function shallowCopy (targetObj, srcObj) { each(srcObj, function (prop) { targetObj[prop] = srcObj[prop]; }); return targetObj; } /** * Copies each property from src onto target, but only if the property to * copy to target is undefined. * @param {Object} target Missing properties in this Object are filled in * @param {Object} src * @private */ function defaults (target, src) { each(src, function (prop) { if (typeof target[prop] === 'undefined') { target[prop] = src[prop]; } }); } /** * Calculates the interpolated tween values of an Object for a given * timestamp. * @param {Number} forPosition The position to compute the state for. * @param {Object} currentState Current state properties. * @param {Object} originalState: The original state properties the Object is * tweening from. * @param {Object} targetState: The destination state properties the Object * is tweening to. * @param {number} duration: The length of the tween in milliseconds. * @param {number} timestamp: The UNIX epoch time at which the tween began. * @param {Object} easing: This Object's keys must correspond to the keys in * targetState. * @private */ function tweenProps (forPosition, currentState, originalState, targetState, duration, timestamp, easing) { var normalizedPosition = forPosition < timestamp ? 0 : (forPosition - timestamp) / duration; var prop; var easingObjectProp; var easingFn; for (prop in currentState) { if (currentState.hasOwnProperty(prop)) { easingObjectProp = easing[prop]; easingFn = typeof easingObjectProp === 'function' ? easingObjectProp : formula[easingObjectProp]; currentState[prop] = tweenProp( originalState[prop], targetState[prop], easingFn, normalizedPosition ); } } return currentState; } /** * Tweens a single property. * @param {number} start The value that the tween started from. * @param {number} end The value that the tween should end at. * @param {Function} easingFunc The easing curve to apply to the tween. * @param {number} position The normalized position (between 0.0 and 1.0) to * calculate the midpoint of 'start' and 'end' against. * @return {number} The tweened value. * @private */ function tweenProp (start, end, easingFunc, position) { return start + (end - start) * easingFunc(position); } /** * Applies a filter to Tweenable instance. * @param {Tweenable} tweenable The `Tweenable` instance to call the filter * upon. * @param {String} filterName The name of the filter to apply. * @private */ function applyFilter (tweenable, filterName) { var filters = Tweenable.prototype.filter; var args = tweenable._filterArgs; each(filters, function (name) { if (typeof filters[name][filterName] !== 'undefined') { filters[name][filterName].apply(tweenable, args); } }); } var timeoutHandler_endTime; var timeoutHandler_currentTime; var timeoutHandler_isEnded; var timeoutHandler_offset; /** * Handles the update logic for one step of a tween. * @param {Tweenable} tweenable * @param {number} timestamp * @param {number} delay * @param {number} duration * @param {Object} currentState * @param {Object} originalState * @param {Object} targetState * @param {Object} easing * @param {Function(Object, *, number)} step * @param {Function(Function,number)}} schedule * @param {number=} opt_currentTimeOverride Needed for accurate timestamp in * Tweenable#seek. * @private */ function timeoutHandler (tweenable, timestamp, delay, duration, currentState, originalState, targetState, easing, step, schedule, opt_currentTimeOverride) { timeoutHandler_endTime = timestamp + delay + duration; timeoutHandler_currentTime = Math.min(opt_currentTimeOverride || now(), timeoutHandler_endTime); timeoutHandler_isEnded = timeoutHandler_currentTime >= timeoutHandler_endTime; timeoutHandler_offset = duration - ( timeoutHandler_endTime - timeoutHandler_currentTime); if (tweenable.isPlaying()) { if (timeoutHandler_isEnded) { step(targetState, tweenable._attachment, timeoutHandler_offset); tweenable.stop(true); } else { tweenable._scheduleId = schedule(tweenable._timeoutHandler, UPDATE_TIME); applyFilter(tweenable, 'beforeTween'); // If the animation has not yet reached the start point (e.g., there was // delay that has not yet completed), just interpolate the starting // position of the tween. if (timeoutHandler_currentTime < (timestamp + delay)) { tweenProps(1, currentState, originalState, targetState, 1, 1, easing); } else { tweenProps(timeoutHandler_currentTime, currentState, originalState, targetState, duration, timestamp + delay, easing); } applyFilter(tweenable, 'afterTween'); step(currentState, tweenable._attachment, timeoutHandler_offset); } } } /** * Creates a usable easing Object from a string, a function or another easing * Object. If `easing` is an Object, then this function clones it and fills * in the missing properties with `"linear"`. * @param {Object.<string|Function>} fromTweenParams * @param {Object|string|Function} easing * @return {Object.<string|Function>} * @private */ function composeEasingObject (fromTweenParams, easing) { var composedEasing = {}; var typeofEasing = typeof easing; if (typeofEasing === 'string' || typeofEasing === 'function') { each(fromTweenParams, function (prop) { composedEasing[prop] = easing; }); } else { each(fromTweenParams, function (prop) { if (!composedEasing[prop]) { composedEasing[prop] = easing[prop] || DEFAULT_EASING; } }); } return composedEasing; } /** * Tweenable constructor. * @class Tweenable * @param {Object=} opt_initialState The values that the initial tween should * start at if a `from` object is not provided to `{{#crossLink * "Tweenable/tween:method"}}{{/crossLink}}` or `{{#crossLink * "Tweenable/setConfig:method"}}{{/crossLink}}`. * @param {Object=} opt_config Configuration object to be passed to * `{{#crossLink "Tweenable/setConfig:method"}}{{/crossLink}}`. * @module Tweenable * @constructor */ function Tweenable (opt_initialState, opt_config) { this._currentState = opt_initialState || {}; this._configured = false; this._scheduleFunction = DEFAULT_SCHEDULE_FUNCTION; // To prevent unnecessary calls to setConfig do not set default // configuration here. Only set default configuration immediately before // tweening if none has been set. if (typeof opt_config !== 'undefined') { this.setConfig(opt_config); } } /** * Configure and start a tween. * @method tween * @param {Object=} opt_config Configuration object to be passed to * `{{#crossLink "Tweenable/setConfig:method"}}{{/crossLink}}`. * @chainable */ Tweenable.prototype.tween = function (opt_config) { if (this._isTweening) { return this; } // Only set default config if no configuration has been set previously and // none is provided now. if (opt_config !== undefined || !this._configured) { this.setConfig(opt_config); } this._timestamp = now(); this._start(this.get(), this._attachment); return this.resume(); }; /** * Configure a tween that will start at some point in the future. * * @method setConfig * @param {Object} config The following values are valid: * - __from__ (_Object=_): Starting position. If omitted, `{{#crossLink * "Tweenable/get:method"}}get(){{/crossLink}}` is used. * - __to__ (_Object=_): Ending position. * - __duration__ (_number=_): How many milliseconds to animate for. * - __delay__ (_delay=_): How many milliseconds to wait before starting the * tween. * - __start__ (_Function(Object, *)_): Function to execute when the tween * begins. Receives the state of the tween as the first parameter and * `attachment` as the second parameter. * - __step__ (_Function(Object, *, number)_): Function to execute on every * tick. Receives `{{#crossLink * "Tweenable/get:method"}}get(){{/crossLink}}` as the first parameter, * `attachment` as the second parameter, and the time elapsed since the * start of the tween as the third. This function is not called on the * final step of the animation, but `finish` is. * - __finish__ (_Function(Object, *)_): Function to execute upon tween * completion. Receives the state of the tween as the first parameter and * `attachment` as the second parameter. * - __easing__ (_Object.<string|Function>|string|Function=_): Easing curve * name(s) or function(s) to use for the tween. * - __attachment__ (_*_): Cached value that is passed to the * `step`/`start`/`finish` methods. * @chainable */ Tweenable.prototype.setConfig = function (config) { config = config || {}; this._configured = true; // Attach something to this Tweenable instance (e.g.: a DOM element, an // object, a string, etc.); this._attachment = config.attachment; // Init the internal state this._pausedAtTime = null; this._scheduleId = null; this._delay = config.delay || 0; this._start = config.start || noop; this._step = config.step || noop; this._finish = config.finish || noop; this._duration = config.duration || DEFAULT_DURATION; this._currentState = shallowCopy({}, config.from || this.get()); this._originalState = this.get(); this._targetState = shallowCopy({}, config.to || this.get()); var self = this; this._timeoutHandler = function () { timeoutHandler(self, self._timestamp, self._delay, self._duration, self._currentState, self._originalState, self._targetState, self._easing, self._step, self._scheduleFunction ); }; // Aliases used below var currentState = this._currentState; var targetState = this._targetState; // Ensure that there is always something to tween to. defaults(targetState, currentState); this._easing = composeEasingObject( currentState, config.easing || DEFAULT_EASING); this._filterArgs = [currentState, this._originalState, targetState, this._easing]; applyFilter(this, 'tweenCreated'); return this; }; /** * @method get * @return {Object} The current state. */ Tweenable.prototype.get = function () { return shallowCopy({}, this._currentState); }; /** * @method set * @param {Object} state The current state. */ Tweenable.prototype.set = function (state) { this._currentState = state; }; /** * Pause a tween. Paused tweens can be resumed from the point at which they * were paused. This is different from `{{#crossLink * "Tweenable/stop:method"}}{{/crossLink}}`, as that method * causes a tween to start over when it is resumed. * @method pause * @chainable */ Tweenable.prototype.pause = function () { this._pausedAtTime = now(); this._isPaused = true; return this; }; /** * Resume a paused tween. * @method resume * @chainable */ Tweenable.prototype.resume = function () { if (this._isPaused) { this._timestamp += now() - this._pausedAtTime; } this._isPaused = false; this._isTweening = true; this._timeoutHandler(); return this; }; /** * Move the state of the animation to a specific point in the tween's * timeline. If the animation is not running, this will cause the `step` * handlers to be called. * @method seek * @param {millisecond} millisecond The millisecond of the animation to seek * to. This must not be less than `0`. * @chainable */ Tweenable.prototype.seek = function (millisecond) { millisecond = Math.max(millisecond, 0); var currentTime = now(); if ((this._timestamp + millisecond) === 0) { return this; } this._timestamp = currentTime - millisecond; if (!this.isPlaying()) { this._isTweening = true; this._isPaused = false; // If the animation is not running, call timeoutHandler to make sure that // any step handlers are run. timeoutHandler(this, this._timestamp, this._delay, this._duration, this._currentState, this._originalState, this._targetState, this._easing, this._step, this._scheduleFunction, currentTime ); this.pause(); } return this; }; /** * Stops and cancels a tween. * @param {boolean=} gotoEnd If `false` or omitted, the tween just stops at * its current state, and the `finish` handler is not invoked. If `true`, * the tweened object's values are instantly set to the target values, and * `finish` is invoked. * @method stop * @chainable */ Tweenable.prototype.stop = function (gotoEnd) { this._isTweening = false; this._isPaused = false; this._timeoutHandler = noop; (root.cancelAnimationFrame || root.webkitCancelAnimationFrame || root.oCancelAnimationFrame || root.msCancelAnimationFrame || root.mozCancelRequestAnimationFrame || root.clearTimeout)(this._scheduleId); if (gotoEnd) { applyFilter(this, 'beforeTween'); tweenProps( 1, this._currentState, this._originalState, this._targetState, 1, 0, this._easing ); applyFilter(this, 'afterTween'); applyFilter(this, 'afterTweenEnd'); this._finish.call(this, this._currentState, this._attachment); } return this; }; /** * @method isPlaying * @return {boolean} Whether or not a tween is running. */ Tweenable.prototype.isPlaying = function () { return this._isTweening && !this._isPaused; }; /** * Set a custom schedule function. * * If a custom function is not set, * [`requestAnimationFrame`](https://developer.mozilla.org/en-US/docs/Web/API/window.requestAnimationFrame) * is used if available, otherwise * [`setTimeout`](https://developer.mozilla.org/en-US/docs/Web/API/Window.setTimeout) * is used. * @method setScheduleFunction * @param {Function(Function,number)} scheduleFunction The function to be * used to schedule the next frame to be rendered. */ Tweenable.prototype.setScheduleFunction = function (scheduleFunction) { this._scheduleFunction = scheduleFunction; }; /** * `delete` all "own" properties. Call this when the `Tweenable` instance * is no longer needed to free memory. * @method dispose */ Tweenable.prototype.dispose = function () { var prop; for (prop in this) { if (this.hasOwnProperty(prop)) { delete this[prop]; } } }; /** * Filters are used for transforming the properties of a tween at various * points in a Tweenable's life cycle. See the README for more info on this. * @private */ Tweenable.prototype.filter = {}; /** * This object contains all of the tweens available to Shifty. It is * extensible - simply attach properties to the `Tweenable.prototype.formula` * Object following the same format as `linear`. * * `pos` should be a normalized `number` (between 0 and 1). * @property formula * @type {Object(function)} */ Tweenable.prototype.formula = { linear: function (pos) { return pos; } }; formula = Tweenable.prototype.formula; shallowCopy(Tweenable, { 'now': now ,'each': each ,'tweenProps': tweenProps ,'tweenProp': tweenProp ,'applyFilter': applyFilter ,'shallowCopy': shallowCopy ,'defaults': defaults ,'composeEasingObject': composeEasingObject }); // `root` is provided in the intro/outro files. // A hook used for unit testing. if (typeof SHIFTY_DEBUG_NOW === 'function') { root.timeoutHandler = timeoutHandler; } // Bootstrap Tweenable appropriately for the environment. if (true) { // CommonJS module.exports = Tweenable; } else {} return Tweenable; } ()); /*! * All equations are adapted from Thomas Fuchs' * [Scripty2](https://github.com/madrobby/scripty2/blob/master/src/effects/transitions/penner.js). * * Based on Easing Equations (c) 2003 [Robert * Penner](http://www.robertpenner.com/), all rights reserved. This work is * [subject to terms](http://www.robertpenner.com/easing_terms_of_use.html). */ /*! * TERMS OF USE - EASING EQUATIONS * Open source under the BSD License. * Easing Equations (c) 2003 Robert Penner, all rights reserved. */ ;(function () { Tweenable.shallowCopy(Tweenable.prototype.formula, { easeInQuad: function (pos) { return Math.pow(pos, 2); }, easeOutQuad: function (pos) { return -(Math.pow((pos - 1), 2) - 1); }, easeInOutQuad: function (pos) { if ((pos /= 0.5) < 1) {return 0.5 * Math.pow(pos,2);} return -0.5 * ((pos -= 2) * pos - 2); }, easeInCubic: function (pos) { return Math.pow(pos, 3); }, easeOutCubic: function (pos) { return (Math.pow((pos - 1), 3) + 1); }, easeInOutCubic: function (pos) { if ((pos /= 0.5) < 1) {return 0.5 * Math.pow(pos,3);} return 0.5 * (Math.pow((pos - 2),3) + 2); }, easeInQuart: function (pos) { return Math.pow(pos, 4); }, easeOutQuart: function (pos) { return -(Math.pow((pos - 1), 4) - 1); }, easeInOutQuart: function (pos) { if ((pos /= 0.5) < 1) {return 0.5 * Math.pow(pos,4);} return -0.5 * ((pos -= 2) * Math.pow(pos,3) - 2); }, easeInQuint: function (pos) { return Math.pow(pos, 5); }, easeOutQuint: function (pos) { return (Math.pow((pos - 1), 5) + 1); }, easeInOutQuint: function (pos) { if ((pos /= 0.5) < 1) {return 0.5 * Math.pow(pos,5);} return 0.5 * (Math.pow((pos - 2),5) + 2); }, easeInSine: function (pos) { return -Math.cos(pos * (Math.PI / 2)) + 1; }, easeOutSine: function (pos) { return Math.sin(pos * (Math.PI / 2)); }, easeInOutSine: function (pos) { return (-0.5 * (Math.cos(Math.PI * pos) - 1)); }, easeInExpo: function (pos) { return (pos === 0) ? 0 : Math.pow(2, 10 * (pos - 1)); }, easeOutExpo: function (pos) { return (pos === 1) ? 1 : -Math.pow(2, -10 * pos) + 1; }, easeInOutExpo: function (pos) { if