├── .gitignore ├── .babelrc ├── postcss.config.js ├── src ├── sass │ ├── themes.scss │ ├── themes │ │ ├── bulma.scss │ │ ├── material.scss │ │ ├── alive.scss │ │ ├── venice.scss │ │ ├── bootstrap.scss │ │ └── colombo.scss │ ├── mixins.scss │ └── toast.scss ├── index.js └── js │ ├── animations.js │ ├── toasted.js │ └── toast.js ├── .npmignore ├── LICENSE ├── package.json ├── dist ├── toasted.min.css ├── toasted.min.js └── toasted.js └── README.md /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | node_modules/ 3 | npm-debug.log 4 | yarn-error.log 5 | .idea/ 6 | .vscode/ 7 | *.map -------------------------------------------------------------------------------- /.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": [ 3 | ["env", { 4 | "es2015": { "modules": false } 5 | }] 6 | ] 7 | } 8 | -------------------------------------------------------------------------------- /postcss.config.js: -------------------------------------------------------------------------------- 1 | var autoprefixer = require('autoprefixer') 2 | 3 | module.exports = { 4 | plugins: [ 5 | autoprefixer({browsers: ['last 7 versions']}) 6 | ] 7 | } -------------------------------------------------------------------------------- /src/sass/themes.scss: -------------------------------------------------------------------------------- 1 | @import "themes/alive"; 2 | @import "themes/material"; 3 | @import "themes/colombo"; 4 | @import "themes/bootstrap"; 5 | @import "themes/venice"; 6 | @import "themes/bulma"; -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | node_modules/ 3 | npm-debug.log 4 | yarn-error.log 5 | .idea/ 6 | .vscode/ 7 | build/ 8 | .babelrc 9 | webpack.config.js 10 | README.md 11 | *.map 12 | examples 13 | test/ -------------------------------------------------------------------------------- /src/sass/themes/bulma.scss: -------------------------------------------------------------------------------- 1 | @include toasted-theme('bulma') { 2 | 3 | background-color: #00d1b2; 4 | color: #fff; 5 | border-radius: 3px; 6 | font-weight: 700; 7 | 8 | @include toasted-theme-type('success', #fff, #23d160); 9 | 10 | @include toasted-theme-type('error', #a94442, #ff3860); 11 | 12 | @include toasted-theme-type('info', #fff, #3273dc); 13 | 14 | } -------------------------------------------------------------------------------- /src/sass/themes/material.scss: -------------------------------------------------------------------------------- 1 | @include toasted-theme('material') { 2 | 3 | background-color: #353535; 4 | border-radius: 2px; 5 | font-weight: 300; 6 | color: #fff; 7 | box-shadow: 0 1px 3px rgba(0, 0, 0, 0.12), 0 1px 2px rgba(0, 0, 0, 0.24); 8 | 9 | @include toasted-theme-type('success', #4CAF50); 10 | @include toasted-theme-type('error', #F44336); 11 | @include toasted-theme-type('info',#3F51B5); 12 | 13 | @include toasted-theme-part('action', #a1c2fa); 14 | 15 | } -------------------------------------------------------------------------------- /src/sass/themes/alive.scss: -------------------------------------------------------------------------------- 1 | @include toasted-theme('alive') { 2 | 3 | font-weight: 700; 4 | border-radius: 2px; 5 | background-color: white; 6 | color: #007FFF; 7 | box-shadow: 0 12px 44px 0 rgba(10, 21, 84, 0.24); 8 | 9 | @include toasted-theme-type('success', #4CAF50); 10 | @include toasted-theme-type('error', #F44336); 11 | @include toasted-theme-type('info',#3F51B5); 12 | 13 | @include toasted-theme-part('action', #007FFF); 14 | @include toasted-theme-part('material-icons', #FFC107); 15 | 16 | } -------------------------------------------------------------------------------- /src/sass/themes/venice.scss: -------------------------------------------------------------------------------- 1 | @include toasted-theme('venice') { 2 | 3 | border-radius: 30px; 4 | color: white; 5 | background: linear-gradient(85deg, rgb(88, 97, 191), rgb(165, 107, 226)); 6 | font-weight: 700; 7 | box-shadow: 0 12px 44px 0 rgba(10, 21, 84, 0.24); 8 | 9 | @include toasted-theme-type('success', #4CAF50); 10 | @include toasted-theme-type('error', #F44336); 11 | @include toasted-theme-type('info',#3F51B5); 12 | 13 | @include toasted-theme-part('action', #007FFF); 14 | @include toasted-theme-part('material-icons', white); 15 | 16 | } -------------------------------------------------------------------------------- /src/index.js: -------------------------------------------------------------------------------- 1 | import {Toasted, Extender} from './js/toasted'; 2 | 3 | Toasted.extend = Extender.hook; 4 | Toasted.utils = Extender.utils; 5 | 6 | (function (root, factory) { 7 | if(typeof define === "function" && define.amd) { 8 | define([], function(){ 9 | return (root.Toasted = factory()); 10 | }); 11 | } else if(typeof module === "object" && module.exports) { 12 | module.exports = (root.Toasted = factory()); 13 | } else { 14 | root.Toasted = factory(); 15 | } 16 | }(window, function() { 17 | return Toasted; 18 | })); 19 | 20 | export default Toasted -------------------------------------------------------------------------------- /src/sass/themes/bootstrap.scss: -------------------------------------------------------------------------------- 1 | @include toasted-theme('bootstrap') { 2 | 3 | color: #31708f; 4 | background-color: #f9fbfd; 5 | border: 1px solid transparent; 6 | border-color: #d9edf7; 7 | border-radius: .25rem; 8 | font-weight: 700; 9 | box-shadow: 0 1px 3px rgba(0, 0, 0, 0.07); 10 | 11 | @include toasted-theme-type('success', #3c763d, #dff0d8) { 12 | border-color: #d0e9c6; 13 | }; 14 | 15 | @include toasted-theme-type('error', #a94442, #f2dede) { 16 | border-color: #f2dede; 17 | }; 18 | 19 | @include toasted-theme-type('info', #31708f, #d9edf7) { 20 | border-color: #d9edf7; 21 | }; 22 | 23 | } -------------------------------------------------------------------------------- /src/sass/themes/colombo.scss: -------------------------------------------------------------------------------- 1 | @include toasted-theme('colombo') { 2 | 3 | border-radius: 6px; 4 | color: #7492b1; 5 | border: 2px solid #7492b1; 6 | background: white; 7 | font-weight: 700; 8 | 9 | &:after { 10 | content: ""; 11 | width: 8px; 12 | height: 8px; 13 | background-color: #5e7b9a; 14 | position: absolute; 15 | top: -4px; 16 | left: -5px; 17 | border-radius: 100%; 18 | } 19 | 20 | @include toasted-theme-type('success', #4CAF50); 21 | @include toasted-theme-type('error', #F44336); 22 | @include toasted-theme-type('info',#3F51B5); 23 | 24 | @include toasted-theme-part('action', #007FFF); 25 | @include toasted-theme-part('material-icons', #5dcccd); 26 | 27 | } -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 Shakeeb Sadikeen 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /src/js/animations.js: -------------------------------------------------------------------------------- 1 | import anime from 'animejs' 2 | 3 | let duration = 300; 4 | 5 | export default { 6 | animateIn : (el) => { 7 | anime({ 8 | targets : el, 9 | translateY: '-35px', 10 | opacity: 1, 11 | duration: duration, 12 | easing: 'easeOutCubic' 13 | }) 14 | }, 15 | animateOut : (el, onComplete) => { 16 | anime({ 17 | targets : el, 18 | opacity : 0, 19 | marginTop : '-40px', 20 | duration: duration, 21 | easing: 'easeOutExpo', 22 | complete: onComplete 23 | }) 24 | }, 25 | animateReset : (el) => { 26 | anime({ 27 | targets : el, 28 | left: 0, 29 | opacity: 1, 30 | duration: duration, 31 | easing: 'easeOutExpo', 32 | }) 33 | }, 34 | animatePanning : (el, left, opacity) => { 35 | anime({ 36 | targets : el, 37 | duration : 10, 38 | easing: 'easeOutQuad', 39 | left: left, 40 | opacity: opacity 41 | }) 42 | }, 43 | animatePanEnd : (el, onComplete) => { 44 | anime({ 45 | targets : el, 46 | opacity : 0, 47 | duration: duration, 48 | easing: 'easeOutExpo', 49 | complete: onComplete 50 | }) 51 | }, 52 | clearAnimation : (toasts) => { 53 | 54 | let timeline = anime.timeline(); 55 | 56 | toasts.forEach((t) => { 57 | timeline.add({ 58 | targets : t.el, 59 | opacity : 0, 60 | right : '-40px', 61 | duration: 300, 62 | offset : '-=150', 63 | easing: 'easeOutExpo', 64 | complete: () => { 65 | t.destroy(); 66 | } 67 | }); 68 | }) 69 | 70 | } 71 | } -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "toastedjs", 3 | "description": "Javascript Toast Plugin - Easy, Responsive and Touch Compatible", 4 | "version": "0.0.2", 5 | "author": "Shakeeb Sadikeen ", 6 | "repository": { 7 | "type": "git", 8 | "url": "https://github.com/shakee93/toastedjs.git" 9 | }, 10 | "main": "./src/index.js", 11 | "scripts": { 12 | "dev": "cross-env NODE_ENV=development webpack-dev-server --open --hot", 13 | "build": "cross-env NODE_ENV=production webpack --config ./build/webpack.release.js --progress --hide-modules", 14 | "build-watch": "cross-env NODE_ENV=production webpack --config ./build/webpack.release.js --progress --hide-modules --watch", 15 | "css": "cross-env NODE_ENV=production webpack --config ./build/webpack.release.css.js --progress --hide-modules --watch", 16 | "release": "npm run build && npm run css", 17 | "es": "babel src --presets babel-preset-es2015 --out-dir dist" 18 | }, 19 | "keywords": [ 20 | "toast", 21 | "toasted", 22 | "toastedjs", 23 | "vue-toasted" 24 | ], 25 | "dependencies": { 26 | "animejs": "^2.0.2", 27 | "hammerjs": "^2.0.8", 28 | "shortid": "^2.2.8", 29 | "es6-object-assign": "^1.1.0" 30 | }, 31 | "devDependencies": { 32 | "autoprefixer": "^7.1.3", 33 | "babel-cli": "^6.23.0", 34 | "babel-core": "^6.0.0", 35 | "babel-loader": "^6.0.0", 36 | "babel-preset-env": "^1.6.0", 37 | "cross-env": "^3.0.0", 38 | "css-loader": "^0.25.0", 39 | "extract-text-webpack-plugin": "^2.1.2", 40 | "file-loader": "^0.9.0", 41 | "node-sass": "^4.5.0", 42 | "postcss-loader": "^2.0.6", 43 | "sass-loader": "^6.0.3", 44 | "style-loader": "^0.18.2", 45 | "webpack": "^3.5.6" 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /src/sass/mixins.scss: -------------------------------------------------------------------------------- 1 | /** 2 | $part : success, info, error 3 | $color : css color of the part 4 | $background : background color of the part 5 | */ 6 | @mixin toasted-theme-type($type, $color : null, $background : null){ 7 | &.#{$type} { 8 | @if($color) { 9 | color: $color; 10 | } 11 | @if($background) { 12 | background-color: $background; 13 | } 14 | @content; 15 | } 16 | } 17 | 18 | 19 | /** 20 | $part : action, material-icons 21 | $color : css color of the part 22 | $background : background color of the part 23 | */ 24 | @mixin toasted-theme-part($part, $color : null, $background : null){ 25 | .#{$part} { 26 | @if($color) { 27 | color: $color; 28 | } 29 | @if($background) { 30 | background-color: $background; 31 | } 32 | @content; 33 | } 34 | } 35 | 36 | 37 | /** 38 | $name : name of the theme 39 | $defaults : have the default values 40 | */ 41 | @mixin toasted-theme($name, $defaults : true){ 42 | 43 | .toasted { 44 | &.#{$name} { 45 | @if($defaults) { 46 | padding: 0 20px; 47 | min-height: 38px; 48 | font-size: 100%; 49 | line-height: 1.1em; 50 | } 51 | @content; 52 | } 53 | 54 | } 55 | 56 | } 57 | 58 | 59 | @mixin fit-to-screen() { 60 | min-width: 100%; 61 | 62 | .toasted:first-child { 63 | margin-top: 0; 64 | } 65 | 66 | &.top-right { 67 | top: 0; 68 | right: 0; 69 | } 70 | 71 | &.top-left { 72 | top: 0; 73 | left: 0; 74 | } 75 | 76 | &.top-center { 77 | top: 0; 78 | left: 0; 79 | transform: translateX(0); 80 | } 81 | 82 | &.bottom-right { 83 | right: 0; 84 | bottom: 0; 85 | } 86 | 87 | &.bottom-left { 88 | left: 0; 89 | bottom: 0; 90 | } 91 | 92 | &.bottom-center { 93 | left: 0; 94 | bottom: 0; 95 | transform: translateX(0); 96 | } 97 | } -------------------------------------------------------------------------------- /src/sass/toast.scss: -------------------------------------------------------------------------------- 1 | @import "mixins"; 2 | @import "themes"; 3 | 4 | .toasted-container { 5 | // fix for min-height bug in IE 6 | display: flex; 7 | flex-direction: column; 8 | position: fixed; 9 | z-index: 10000; 10 | 11 | &.full-width { 12 | display: flex; 13 | flex-direction: column; 14 | max-width: 86%; 15 | width: 100%; 16 | 17 | // check if user needs this to be fitted into screen 18 | &.fit-to-screen { 19 | @include fit-to-screen 20 | } 21 | } 22 | 23 | // Positioning 24 | 25 | &.top-right { 26 | top: 10%; 27 | right: 7%; 28 | 29 | &:not(.full-width) { 30 | align-items: flex-end; 31 | } 32 | } 33 | 34 | &.top-left { 35 | top: 10%; 36 | left: 7%; 37 | 38 | &:not(.full-width) { 39 | align-items: flex-start; 40 | } 41 | } 42 | 43 | &.top-center { 44 | top: 10%; 45 | left: 50%; 46 | align-items: center; 47 | transform: translateX(-50%); 48 | } 49 | 50 | &.bottom-right { 51 | right: 5%; 52 | bottom: 7%; 53 | 54 | &:not(.full-width) { 55 | align-items: flex-end; 56 | } 57 | } 58 | 59 | &.bottom-left { 60 | left: 5%; 61 | bottom: 7%; 62 | 63 | &:not(.full-width) { 64 | align-items: flex-start; 65 | } 66 | } 67 | 68 | &.bottom-center { 69 | left: 50%; 70 | bottom: 7%; 71 | align-items: center; 72 | transform: translateX(-50%); 73 | } 74 | 75 | // fix positioning floating 76 | &.top-left .toasted, &.bottom-left .toasted { 77 | float: left; 78 | } 79 | 80 | &.top-right .toasted, &.bottom-right .toasted { 81 | float: right; 82 | } 83 | 84 | // toast element styling 85 | .toasted { 86 | top: 35px; 87 | width: auto; 88 | clear: both; 89 | margin-top: .8em; 90 | position: relative; 91 | max-width: 100%; 92 | height: auto; 93 | word-break: break-all; 94 | display: flex; 95 | align-items: center; 96 | justify-content: space-between; 97 | box-sizing: inherit; 98 | 99 | .material-icons { 100 | margin-right: .5rem; 101 | margin-left: -.4rem; 102 | 103 | &.after { 104 | margin-left: .5rem; 105 | margin-right: -.4rem; 106 | } 107 | } 108 | 109 | // Toast Action Styling 110 | .actions-wrapper { 111 | margin-left: .4em; 112 | margin-right: -1.2em; 113 | 114 | .action { 115 | text-decoration: none; 116 | font-size: 0.9rem; 117 | padding: 8px; 118 | border-radius: 3px; 119 | text-transform: uppercase; 120 | letter-spacing: .03em; 121 | font-weight: 600; 122 | cursor: pointer; 123 | margin-right: .2rem; 124 | 125 | &.icon { 126 | padding: 4px; 127 | display: flex; 128 | align-items: center; 129 | justify-content: center; 130 | 131 | .material-icons { 132 | margin-right: 0; 133 | margin-left: 4px; 134 | } 135 | 136 | &:hover { 137 | text-decoration: none; 138 | } 139 | } 140 | 141 | &:hover { 142 | text-decoration: underline; 143 | } 144 | } 145 | } 146 | 147 | 148 | } 149 | } 150 | 151 | @media only screen and (max-width: 600px) { 152 | #toasted-container { 153 | @include fit-to-screen; 154 | 155 | &.top-center, &.bottom-center { 156 | align-items: stretch !important; 157 | } 158 | 159 | &.top-right, &.top-left, &.bottom-left, &.bottom-right { 160 | .toasted { 161 | float: none; 162 | } 163 | } 164 | 165 | .toasted { 166 | border-radius: 0; 167 | } 168 | 169 | } 170 | } -------------------------------------------------------------------------------- /src/js/toasted.js: -------------------------------------------------------------------------------- 1 | import Toast from './toast'; 2 | const uuid = require('shortid'); 3 | import animations from './animations'; 4 | 5 | // add Object.assign Polyfill 6 | require('es6-object-assign').polyfill(); 7 | 8 | /** 9 | * Allows Toasted to be Extended 10 | * 11 | */ 12 | export const Extender = function () { 13 | 14 | return { 15 | hook: { 16 | options : [], 17 | actions : [] 18 | }, 19 | run : function(name, callback) { 20 | 21 | if(!Array.isArray(this.hook[name])) { 22 | console.warn("[toasted] : hook not found"); 23 | return; 24 | } 25 | 26 | this.hook[name].forEach((hook) => { 27 | 28 | // check if it is a function 29 | if(!hook && typeof hook !== 'function') return; 30 | 31 | callback && callback(hook); 32 | }) 33 | }, 34 | utils : { 35 | warn : (message) => { 36 | console.warn(`[toasted] : ${message}`); 37 | } 38 | } 39 | } 40 | }(); 41 | 42 | /** 43 | * Toast 44 | * core instance of toast 45 | * 46 | * @param _options 47 | * @returns {Toasted} 48 | * @constructor 49 | */ 50 | export const Toasted = function (_options) { 51 | 52 | if (!_options) _options = {}; 53 | 54 | /** 55 | * Unique id of the toast 56 | */ 57 | this.id = uuid.generate(); 58 | 59 | /** 60 | * Shared Options of the Toast 61 | */ 62 | this.options = _options; 63 | 64 | 65 | /** 66 | * Shared Toasts list 67 | */ 68 | this.global = {}; 69 | 70 | 71 | /** 72 | * All Registered Groups 73 | */ 74 | this.groups = []; 75 | 76 | 77 | /** 78 | * All Registered Toasts 79 | */ 80 | this.toasts = []; 81 | 82 | 83 | /** 84 | * Create New Group of Toasts 85 | * 86 | * @param o 87 | */ 88 | this.group = (o) => { 89 | 90 | if (!o) o = {}; 91 | 92 | if (!o.globalToasts) { 93 | o.globalToasts = {}; 94 | } 95 | 96 | // share parents global toasts 97 | Object.assign(o.globalToasts, this.global); 98 | 99 | // tell parent about the group 100 | let group = new Toasted(o); 101 | this.groups.push(group); 102 | 103 | return group; 104 | } 105 | 106 | /** 107 | * Base Toast Show Function 108 | * 109 | * @param message 110 | * @param options 111 | * @returns {*} 112 | * @private 113 | */ 114 | let _show = (message, options) => { 115 | 116 | // clone the global options 117 | let _options = Object.assign({}, this.options); 118 | 119 | // merge the cached global options with options 120 | Object.assign(_options, options); 121 | 122 | let toast = new Toast(this); 123 | return toast.create(message, _options); 124 | } 125 | 126 | /** 127 | * 128 | */ 129 | let initiateCustomToasts = () => { 130 | 131 | let customToasts = this.options.globalToasts; 132 | 133 | // this will initiate toast for the custom toast. 134 | let initiate = (message, options) => { 135 | 136 | // check if passed option is a available method if so call it. 137 | if (typeof(options) === 'string' && this[options]) { 138 | return this[options].apply(this, [message, {}]); 139 | } 140 | 141 | // or else create a new toast with passed options. 142 | return _show(message, options); 143 | }; 144 | 145 | if (customToasts) { 146 | 147 | this.global = {}; 148 | 149 | Object.keys(customToasts).forEach(key => { 150 | 151 | // register the custom toast events to the Toast.custom property 152 | this.global[key] = (payload = {}) => { 153 | 154 | // return the it in order to expose the Toast methods 155 | return customToasts[key].apply(null, [payload, initiate]); 156 | }; 157 | }); 158 | 159 | } 160 | }; 161 | 162 | 163 | /** 164 | * Register a Global Toast 165 | * 166 | * @param name 167 | * @param message 168 | * @param options 169 | */ 170 | this.register = (name, message, options) => { 171 | options = options || {}; 172 | 173 | (!this.options.globalToasts) ? this.options.globalToasts = {} : null; 174 | 175 | this.options.globalToasts[name] = function (payload, initiate) { 176 | 177 | if (typeof message === 'function') { 178 | message = message(payload); 179 | } 180 | 181 | return initiate(message, options); 182 | }; 183 | 184 | initiateCustomToasts(); 185 | } 186 | 187 | 188 | /** 189 | * Show a Simple Toast 190 | * 191 | * @param message 192 | * @param options 193 | * @returns {*} 194 | */ 195 | this.show = (message, options) => { 196 | return _show(message, options); 197 | } 198 | 199 | 200 | /** 201 | * Show a Toast with Success Style 202 | * 203 | * @param message 204 | * @param options 205 | * @returns {*} 206 | */ 207 | this.success = (message, options) => { 208 | options = options || {}; 209 | options.type = "success"; 210 | return _show(message, options); 211 | } 212 | 213 | 214 | /** 215 | * Show a Toast with Info Style 216 | * 217 | * @param message 218 | * @param options 219 | * @returns {*} 220 | */ 221 | this.info = (message, options) => { 222 | options = options || {}; 223 | options.type = "info"; 224 | return _show(message, options); 225 | } 226 | 227 | 228 | /** 229 | * Show a Toast with Error Style 230 | * 231 | * @param message 232 | * @param options 233 | * @returns {*} 234 | */ 235 | this.error = (message, options) => { 236 | options = options || {}; 237 | options.type = "error"; 238 | return _show(message, options); 239 | } 240 | 241 | 242 | /** 243 | * Clear All Toasts 244 | * 245 | * @returns {boolean} 246 | */ 247 | this.clear = () => { 248 | 249 | let toasts = this.toasts; 250 | let last = toasts.slice(-1)[0]; 251 | 252 | // start vanishing from the bottom if toasts are on top 253 | if(last && last.options.position.includes('top')) { 254 | toasts = toasts.reverse(); 255 | } 256 | 257 | animations.clearAnimation(toasts); 258 | this.toasts = []; 259 | } 260 | 261 | /** 262 | * Initiate custom toasts 263 | */ 264 | initiateCustomToasts(); 265 | 266 | return this; 267 | }; 268 | 269 | export default {Toasted, Extender}; -------------------------------------------------------------------------------- /dist/toasted.min.css: -------------------------------------------------------------------------------- 1 | .toasted.alive{padding:0 20px;min-height:38px;font-size:100%;line-height:1.1em;font-weight:700;border-radius:2px;background-color:#fff;color:#007fff;box-shadow:0 12px 44px 0 rgba(10,21,84,.24)}.toasted.alive.success{color:#4caf50}.toasted.alive.error{color:#f44336}.toasted.alive.info{color:#3f51b5}.toasted.alive .action{color:#007fff}.toasted.alive .material-icons{color:#ffc107}.toasted.material{padding:0 20px;min-height:38px;font-size:100%;line-height:1.1em;background-color:#353535;border-radius:2px;font-weight:300;color:#fff;box-shadow:0 1px 3px rgba(0,0,0,.12),0 1px 2px rgba(0,0,0,.24)}.toasted.material.success{color:#4caf50}.toasted.material.error{color:#f44336}.toasted.material.info{color:#3f51b5}.toasted.material .action{color:#a1c2fa}.toasted.colombo{padding:0 20px;min-height:38px;font-size:100%;line-height:1.1em;border-radius:6px;color:#7492b1;border:2px solid #7492b1;background:#fff;font-weight:700}.toasted.colombo:after{content:"";width:8px;height:8px;background-color:#5e7b9a;position:absolute;top:-4px;left:-5px;border-radius:100%}.toasted.colombo.success{color:#4caf50}.toasted.colombo.error{color:#f44336}.toasted.colombo.info{color:#3f51b5}.toasted.colombo .action{color:#007fff}.toasted.colombo .material-icons{color:#5dcccd}.toasted.bootstrap{padding:0 20px;min-height:38px;font-size:100%;line-height:1.1em;color:#31708f;background-color:#f9fbfd;border:1px solid transparent;border-color:#d9edf7;border-radius:.25rem;font-weight:700;box-shadow:0 1px 3px rgba(0,0,0,.07)}.toasted.bootstrap.success{color:#3c763d;background-color:#dff0d8;border-color:#d0e9c6}.toasted.bootstrap.error{color:#a94442;background-color:#f2dede;border-color:#f2dede}.toasted.bootstrap.info{color:#31708f;background-color:#d9edf7;border-color:#d9edf7}.toasted.venice{padding:0 20px;min-height:38px;font-size:100%;line-height:1.1em;border-radius:30px;color:#fff;background:linear-gradient(85deg,#5861bf,#a56be2);font-weight:700;box-shadow:0 12px 44px 0 rgba(10,21,84,.24)}.toasted.venice.success{color:#4caf50}.toasted.venice.error{color:#f44336}.toasted.venice.info{color:#3f51b5}.toasted.venice .action{color:#007fff}.toasted.venice .material-icons{color:#fff}.toasted.bulma{padding:0 20px;min-height:38px;font-size:100%;line-height:1.1em;background-color:#00d1b2;color:#fff;border-radius:3px;font-weight:700}.toasted.bulma.success{color:#fff;background-color:#23d160}.toasted.bulma.error{color:#a94442;background-color:#ff3860}.toasted.bulma.info{color:#fff;background-color:#3273dc}.toasted-container{position:fixed;z-index:10000}.toasted-container,.toasted-container.full-width{display:-ms-flexbox;display:flex;-ms-flex-direction:column;flex-direction:column}.toasted-container.full-width{max-width:86%;width:100%}.toasted-container.full-width.fit-to-screen{min-width:100%}.toasted-container.full-width.fit-to-screen .toasted:first-child{margin-top:0}.toasted-container.full-width.fit-to-screen.top-right{top:0;right:0}.toasted-container.full-width.fit-to-screen.top-left{top:0;left:0}.toasted-container.full-width.fit-to-screen.top-center{top:0;left:0;-webkit-transform:translateX(0);transform:translateX(0)}.toasted-container.full-width.fit-to-screen.bottom-right{right:0;bottom:0}.toasted-container.full-width.fit-to-screen.bottom-left{left:0;bottom:0}.toasted-container.full-width.fit-to-screen.bottom-center{left:0;bottom:0;-webkit-transform:translateX(0);transform:translateX(0)}.toasted-container.top-right{top:10%;right:7%}.toasted-container.top-right:not(.full-width){-ms-flex-align:end;align-items:flex-end}.toasted-container.top-left{top:10%;left:7%}.toasted-container.top-left:not(.full-width){-ms-flex-align:start;align-items:flex-start}.toasted-container.top-center{top:10%;left:50%;-ms-flex-align:center;align-items:center;-webkit-transform:translateX(-50%);transform:translateX(-50%)}.toasted-container.bottom-right{right:5%;bottom:7%}.toasted-container.bottom-right:not(.full-width){-ms-flex-align:end;align-items:flex-end}.toasted-container.bottom-left{left:5%;bottom:7%}.toasted-container.bottom-left:not(.full-width){-ms-flex-align:start;align-items:flex-start}.toasted-container.bottom-center{left:50%;bottom:7%;-ms-flex-align:center;align-items:center;-webkit-transform:translateX(-50%);transform:translateX(-50%)}.toasted-container.bottom-left .toasted,.toasted-container.top-left .toasted{float:left}.toasted-container.bottom-right .toasted,.toasted-container.top-right .toasted{float:right}.toasted-container .toasted{top:35px;width:auto;clear:both;margin-top:.8em;position:relative;max-width:100%;height:auto;word-break:break-all;display:-ms-flexbox;display:flex;-ms-flex-align:center;align-items:center;-ms-flex-pack:justify;justify-content:space-between;box-sizing:inherit}.toasted-container .toasted .material-icons{margin-right:.5rem;margin-left:-.4rem}.toasted-container .toasted .material-icons.after{margin-left:.5rem;margin-right:-.4rem}.toasted-container .toasted .actions-wrapper{margin-left:.4em;margin-right:-1.2em}.toasted-container .toasted .actions-wrapper .action{text-decoration:none;font-size:.9rem;padding:8px;border-radius:3px;text-transform:uppercase;letter-spacing:.03em;font-weight:600;cursor:pointer;margin-right:.2rem}.toasted-container .toasted .actions-wrapper .action.icon{padding:4px;display:-ms-flexbox;display:flex;-ms-flex-align:center;align-items:center;-ms-flex-pack:center;justify-content:center}.toasted-container .toasted .actions-wrapper .action.icon .material-icons{margin-right:0;margin-left:4px}.toasted-container .toasted .actions-wrapper .action.icon:hover{text-decoration:none}.toasted-container .toasted .actions-wrapper .action:hover{text-decoration:underline}@media only screen and (max-width:600px){#toasted-container{min-width:100%}#toasted-container .toasted:first-child{margin-top:0}#toasted-container.top-right{top:0;right:0}#toasted-container.top-left{top:0;left:0}#toasted-container.top-center{top:0;left:0;-webkit-transform:translateX(0);transform:translateX(0)}#toasted-container.bottom-right{right:0;bottom:0}#toasted-container.bottom-left{left:0;bottom:0}#toasted-container.bottom-center{left:0;bottom:0;-webkit-transform:translateX(0);transform:translateX(0)}#toasted-container.bottom-center,#toasted-container.top-center{-ms-flex-align:stretch!important;align-items:stretch!important}#toasted-container.bottom-left .toasted,#toasted-container.bottom-right .toasted,#toasted-container.top-left .toasted,#toasted-container.top-right .toasted{float:none}#toasted-container .toasted{border-radius:0}} -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |

2 | 3 | 4 | 5 |

6 | 7 |

8 | 9 | 10 |

11 | 12 | ## Introduction 13 | 14 | toastedjs is heir of [vue-toasted](https://github.com/shakee93/vue-toasted) it is responsive, touch compatible, easy to use, attractive and feature rich with icons, actions etc... 15 | 16 | ### Interactive demo 17 | 18 | demo here 19 | 20 | ### Installation 21 | 22 | **Source**|**Info** 23 | -----|----- 24 | npm | `npm install toastedjs --save` 25 | yarn | `yarn add toastedjs` 26 | unpkg (js) | [https://unpkg.com/toastedjs/dist/toasted.min.js](https://unpkg.com/toastedjs/dist/toasted.min.js) 27 | unpkg (css) | [https://unpkg.com/toastedjs/dist/toasted.min.css](https://unpkg.com/toastedjs/dist/toasted.min.css) 28 | jsdelivr | [https://jsdelivr.com/package/npm/toastedjs](https://jsdelivr.com/package/npm/toastedjs) 29 | 30 | ## Basic Usage 31 | 32 | #### ES6 33 | ```javascript 34 | 35 | import Toasted from 'toastedjs' 36 | 37 | import 'toastedjs/dist/toastedjs.min.css' 38 | //import 'toastedjs/src/sass/toast.scss' 39 | 40 | let toasted = new Toasted({ /* your options.. */ }) 41 | toasted.show('yo, toasted here !!') 42 | 43 | ``` 44 | 45 | #### Direct 46 | ```html 47 | 48 | 49 | 50 | 51 | 52 | 53 | 57 | ``` 58 | 59 | ## Guide 60 | 61 | #### Actions 62 | 63 | Actions are used to make the toasts interactive **(save, undo, cancel, close)**, you can have **one or more** options on a single toast. 64 | 65 |

66 | 67 |

68 | 69 | ```javascript 70 | // you can pass multiple actions as an array of actions 71 | action : { 72 | text : 'Save', 73 | onClick : (e, toasted) => { 74 | toasted.delete() 75 | } 76 | } 77 | ``` 78 | **[⬇ check action api below](#actions-1)** 79 | 80 | #### Icons 81 | 82 | [Material Icons](http://google.github.io/material-design-icons/) supported. you will have to import the icon packs into your project. 83 | 84 | 85 | ```javascript 86 | { 87 | // pass the icon name as string 88 | icon : 'check' 89 | 90 | // or you can pass an object 91 | icon : { 92 | name : 'watch', 93 | after : true // this will append the icon to the end of content 94 | } 95 | } 96 | ``` 97 | **[⬇ check icons api below](#icons-1)** 98 | 99 | ## Api 100 | 101 | ### Options 102 | below are the options you can pass to create a toast or you can set these options globally. 103 | 104 | ```javascript 105 | // you can pass options either 106 | let toasted = new Toasted({ 107 | position : 'top-center', 108 | theme : 'alive', 109 | onComplete : () => { 110 | console.log('i am done !') 111 | } 112 | }) 113 | ``` 114 | 115 | **Option**|**Type's**|**Default**|**Description** 116 | -----|-----|-----|----- 117 | **position**|String|'top-right'|Position of the toast container
 **['top-right', 'top-center', 'top-left', 'bottom-right', 'bottom-center', 'bottom-left']** 118 | **duration**|Number|null|Display time of the toast in millisecond 119 | **action**|Object, Array|null| **[⬇ check action api below](#actions-1)** 120 | **fullWidth**|Boolean|FALSE|Enable Full Width 121 | **fitToScreen**|Boolean|FALSE|Fits to Screen on Full Width 122 | **className**|String, Array|null|Custom css class name of the toast 123 | **containerClass**|String, Array|null|Custom css classes for toast container 124 | **Icon**|String, Object|null| **[⬇ check icons api below](#icons-1)** 125 | **type**|String|'default'|Type of the Toast 
**['success', 'info', 'error']** 126 | **theme**|String|'primary'|Theme of the toast you prefer
**['primary', 'outline', 'bubble']** 127 | **onComplete**|Function|null|Trigger when toast is completed 128 | 129 | 130 | #### Actions 131 | 132 | **Parameters**|**Type's**|**Default**|**Description** 133 | -----|-----|-----|----- 134 | text*|String|-| name of action 135 | href|String|`null`| url of action 136 | icon|String|`null`| name of material for action 137 | class|String/Array|`null`| custom css class for the action 138 | onClick|Function(e,toastObject) |`null`| onClick Function of action 139 | 140 | #### Icons 141 | 142 | **Parameters**|**Type's**|**Default**|**Description** 143 | -----|-----|-----|----- 144 | name*|String|-| name of the icon 145 | color|String|`null`| color of the icon 146 | after|Boolean|`null`| append the icon to end of the toast 147 | 148 | ### Methods 149 | 150 | Methods available under ToastedJS 151 | 152 | ```javascript 153 | // you can pass string or html to message 154 | let toasted = new Toasted({ /* global options */ }) 155 | toasted.show( 'my message', { /* some new option */ }) 156 | ``` 157 | 158 | **Method**|**Parameter's**|**Description** 159 | -----|-----|----- 160 | **show**|message*, options|Show a toast 161 | **success**|message*, options|Show a toast success style 162 | **info**|message*, options|Show a toast info style 163 | **error**|message*, options|Show a toast error style 164 | **register**|name, message[string,function(payload)]* , options|Register your own toast with options explained here 165 | **group**|options|Create a new group of toasts (new toast container with its options) 166 | **clear**|-|Clear all toasts 167 | 168 | ### Toast Instance (Single toast instance) 169 | 170 | Each Toast Returns a Toast Instance where you can manipulate the toast. 171 | 172 | ```javascript 173 | let toasted = new Toasted() 174 | 175 | let myToast = toasted.show("Holla !!") 176 | myToast.text("Changing the text !!!").delete(1500) 177 | 178 | let anotherToast = toasted.error("Oopss.. my bad !") 179 | anotherToast.text("Oopss.. it's okey..") 180 | ``` 181 | 182 | **Option**|**Type's**|**Description** 183 | -----|-----|----- 184 | **options**|Object|Options of the toast instance 185 | **toast**|HTMLElement|Html Element of the toast 186 | **text**|Function(message)|Change text of the toast 187 | **delete**|Function(delay = 300)|Delete the toast with animation and delay 188 | **destroy**|Function|Destroy the toast unregister from parent instance 189 | 190 | 191 | ## Browsers support 192 | 193 | | [IE / Edge](http://godban.github.io/browsers-support-badges/)
IE / Edge | [Firefox](http://godban.github.io/browsers-support-badges/)
Firefox | [Chrome](http://godban.github.io/browsers-support-badges/)
Chrome | [Safari](http://godban.github.io/browsers-support-badges/)
Safari | [Opera](http://godban.github.io/browsers-support-badges/)
Opera | [iOS Safari](http://godban.github.io/browsers-support-badges/)
iOS Safari | [Chrome for Android](http://godban.github.io/browsers-support-badges/)
Chrome for Android | 194 | | --------- | --------- | --------- | --------- | --------- | --------- | --------- | 195 | | IE10, IE11, Edge| last 7 versions| last 7 versions| last 7 versions| last 7 versions| last 3 versions| last 3 versions 196 | 197 | Please Open and issue If You have Found any Issues. 198 | 199 | ## Mobile Responsiveness 200 | 201 | On Mobile Toasts will be on full width. according to the position the toast will either be on top or bottom. 202 | 203 | 204 | ## Credits 205 | 206 | + Inspired and developed from [materialize-css](https://github.com/Dogfalo/materialize) toast. 207 | + Uses [hammerjs](http://hammerjs.github.io/) for touch events 208 | + Uses lightweight and fast [animejs](http://animejs.com/) for animations. 209 | + Whoever contributes to this project :wink: 210 | 211 | Enjoy Toasting !! -------------------------------------------------------------------------------- /src/js/toast.js: -------------------------------------------------------------------------------- 1 | import Hammer from 'hammerjs'; 2 | import {Extender} from './toasted'; 3 | import animations from './animations' 4 | const uuid = require('shortid'); 5 | 6 | export const Toast = function (instance) { 7 | 8 | /** 9 | * Compiled options of the toast 10 | */ 11 | this.options = {}; 12 | 13 | 14 | /** 15 | * Unique id for the toast 16 | */ 17 | this.id = uuid.generate(); 18 | 19 | 20 | /** 21 | * Toast Html Element 22 | * 23 | * @type {null|Element} 24 | */ 25 | this.toast = null; 26 | 27 | 28 | /** 29 | * Check if Initialized the toast 30 | * 31 | * @type {boolean} 32 | */ 33 | let initialized = false; 34 | 35 | 36 | let constructor = () => { 37 | instance.toasts.push(this); 38 | }; 39 | constructor(); 40 | 41 | 42 | /** 43 | * Create Toast 44 | * 45 | * @param message 46 | * @param options 47 | * @returns {Toast} 48 | */ 49 | this.create = (message, options) => { 50 | 51 | if(!message || initialized) { 52 | return; 53 | } 54 | 55 | options = setOptions(options); 56 | let container = getContainer(); 57 | 58 | this.toast = document.createElement('div'); 59 | this.toast.classList.add('toasted'); 60 | 61 | // add classes 62 | if (options.className) { 63 | options.className.forEach((className) => { 64 | this.toast.classList.add(className); 65 | }); 66 | } 67 | 68 | 69 | 70 | // Append the Message 71 | appendMessage(message); 72 | 73 | // add the touch events of the toast 74 | addTouchEvents(); 75 | 76 | 77 | // add material icon if available 78 | createIcon(); 79 | 80 | // append the actions 81 | appendActions(); 82 | 83 | // append the toasts 84 | container.appendChild(this.toast); 85 | 86 | // animate toast 87 | animations.animateIn(this.toast); 88 | 89 | // set its duration 90 | setDuration(); 91 | 92 | // TODO : remove this later, this is here to backward compatibility 93 | this.el = this.toast; 94 | 95 | // Let Instance know the toast is initialized 96 | initialized = true; 97 | 98 | return this; 99 | }; 100 | 101 | /** 102 | * Append Message to the Toast 103 | * 104 | * @param message 105 | */ 106 | let appendMessage = (message) => { 107 | 108 | if(!message) { 109 | return; 110 | } 111 | 112 | // If type of parameter is HTML Element 113 | if (typeof HTMLElement === "object" ? message instanceof HTMLElement : message && typeof message === "object" && message !== null && message.nodeType === 1 && typeof message.nodeName === "string") { 114 | this.toast.appendChild(message); 115 | } 116 | else { 117 | // Insert as text; 118 | this.toast.innerHTML = message; 119 | } 120 | 121 | } 122 | 123 | 124 | /** 125 | * Get the Toast Container 126 | * 127 | * @returns {Element} 128 | */ 129 | let getContainer = () => { 130 | 131 | let container = document.getElementById(instance.id); 132 | 133 | if(container === null) { 134 | container = document.createElement('div'); 135 | container.id = instance.id; 136 | document.body.appendChild(container); 137 | } 138 | 139 | // check if the container classes has changed if so update it 140 | if (container.className !== this.options.containerClass.join(' ')) { 141 | container.className = ""; 142 | this.options.containerClass.forEach((className) => { 143 | container.classList.add(className); 144 | }); 145 | } 146 | 147 | return container; 148 | } 149 | 150 | 151 | /** 152 | * Parse and Set Toast Options 153 | * 154 | * @param options 155 | * @returns {*} 156 | */ 157 | let setOptions = (options) => { 158 | 159 | // toast position 160 | options.position = options.position || "top-right"; 161 | 162 | // toast duration 163 | options.duration = options.duration || null; 164 | 165 | // get action name 166 | options.action = options.action || null; 167 | 168 | // check if the fullWidth is enabled 169 | options.fullWidth = options.fullWidth || false; 170 | 171 | // check if the toast needs to be fitted in the screen (no margin gap between screen) 172 | options.fitToScreen = options.fitToScreen || null; 173 | 174 | // class name to be added on the toast 175 | options.className = options.className || null; 176 | 177 | // class name to be added on the toast container 178 | options.containerClass = options.containerClass || null; 179 | 180 | // get icon name 181 | options.icon = options.icon || null; 182 | 183 | // normal type will allow the basic color 184 | options.type = options.type || "default"; 185 | 186 | // normal type will allow the basic color 187 | options.theme = options.theme || "material"; 188 | 189 | // normal type will allow the basic color 190 | options.color = options.color || null; 191 | 192 | // get icon color 193 | options.iconColor = options.iconColor || null; 194 | 195 | // complete call back of the toast 196 | options.onComplete = options.onComplete || null; 197 | 198 | // TODO : closeOnSwipe, singleton, iconPack 199 | 200 | /* transform options */ 201 | 202 | // toast class 203 | if (options.className && typeof(options.className) === "string") { 204 | options.className = options.className.split(' '); 205 | } 206 | 207 | if (!options.className) { 208 | options.className = []; 209 | } 210 | 211 | (options.theme) && options.className.push(options.theme.trim()); 212 | (options.type) && options.className.push(options.type); 213 | 214 | 215 | // toast container class 216 | if (options.containerClass && typeof(options.containerClass) === "string") { 217 | options.containerClass = options.containerClass.split(' '); 218 | } 219 | 220 | if (!options.containerClass) { 221 | options.containerClass = []; 222 | } 223 | 224 | (options.position) && options.containerClass.push(options.position.trim()); 225 | (options.fullWidth) && options.containerClass.push('full-width'); 226 | (options.fitToScreen) && options.containerClass.push('fit-to-screen'); 227 | 228 | // add toasted container class to top 229 | options.containerClass.unshift('toasted-container'); 230 | 231 | // HOOK : options 232 | Extender.run("options", hook => hook(options, this.options)) 233 | 234 | this.options = options; 235 | return options; 236 | } 237 | 238 | 239 | /** 240 | * Add Hammer Touch events to the Toast 241 | */ 242 | let addTouchEvents = () => { 243 | 244 | let toast = this.toast; 245 | 246 | // Bind hammer 247 | let hammerHandler = new Hammer(toast, {prevent_default: false}); 248 | hammerHandler.on('pan', function (e) { 249 | let deltaX = e.deltaX; 250 | let activationDistance = 80; 251 | 252 | // Change toast state 253 | if (!toast.classList.contains('panning')) { 254 | toast.classList.add('panning'); 255 | } 256 | 257 | let opacityPercent = 1 - Math.abs(deltaX / activationDistance); 258 | if (opacityPercent < 0) 259 | opacityPercent = 0; 260 | 261 | animations.animatePanning(toast, deltaX, opacityPercent) 262 | 263 | }); 264 | 265 | hammerHandler.on('panend', (e) =>{ 266 | let deltaX = e.deltaX; 267 | let activationDistance = 80; 268 | 269 | // If toast dragged past activation point 270 | if (Math.abs(deltaX) > activationDistance) { 271 | 272 | animations.animatePanEnd(toast, () => { 273 | if (typeof(this.options.onComplete) === "function") { 274 | this.options.onComplete(); 275 | } 276 | 277 | this.destroy(); 278 | }) 279 | 280 | } else { 281 | toast.classList.remove('panning'); 282 | // Put toast back into original position 283 | animations.animateReset(toast) 284 | 285 | } 286 | }); 287 | 288 | } 289 | 290 | 291 | let createIcon = () => { 292 | 293 | let options = this.options; 294 | 295 | // add material icon if available 296 | if (options.icon) { 297 | 298 | let iel = document.createElement('i'); 299 | iel.classList.add('material-icons'); 300 | 301 | // add color to the icon. priority : icon.color > option.color > theme 302 | iel.style.color = (options.icon.color) ? options.icon.color : options.color; 303 | 304 | if (options.icon.after && options.icon.name) { 305 | iel.textContent = options.icon.name; 306 | iel.classList.add('after'); 307 | this.toast.appendChild(iel); 308 | } 309 | else if (options.icon.name) { 310 | iel.textContent = options.icon.name; 311 | this.toast.insertBefore(iel, this.toast.firstChild); 312 | } 313 | else { 314 | iel.textContent = options.icon; 315 | this.toast.insertBefore(iel, this.toast.firstChild); 316 | } 317 | 318 | } 319 | 320 | } 321 | 322 | /** 323 | * Create Actions to the toast 324 | * 325 | * @param action 326 | * @returns {*} 327 | */ 328 | let createAction = (action) => { 329 | 330 | if (!action) { 331 | return null; 332 | } 333 | 334 | let el = document.createElement('a'); 335 | 336 | // add color to icon 337 | el.style.color = (action.color) ? action.color : this.options.color; 338 | 339 | el.classList.add('action'); 340 | 341 | if (action.text) { 342 | el.text = action.text 343 | } 344 | 345 | if (action.href) { 346 | el.href = action.href 347 | } 348 | 349 | if (action.icon) { 350 | 351 | // add icon class to style it 352 | el.classList.add('icon'); 353 | 354 | // create icon element 355 | let iel = document.createElement('i'); 356 | iel.classList.add('material-icons'); 357 | iel.textContent = action.icon 358 | 359 | // append it to the button 360 | el.appendChild(iel); 361 | } 362 | 363 | if (action.class) { 364 | 365 | switch (typeof action.class) { 366 | case 'string' : 367 | action.class.split(' ').forEach((className) => { 368 | el.classList.add(className) 369 | }) 370 | break; 371 | case 'array' : 372 | action.class.forEach((className) => { 373 | el.classList.add(className) 374 | }) 375 | } 376 | } 377 | 378 | if (action.onClick && typeof action.onClick === 'function') { 379 | el.addEventListener('click', (e) => { 380 | 381 | if (action.onClick) { 382 | e.preventDefault() 383 | action.onClick(e, this) 384 | } 385 | 386 | }) 387 | } 388 | 389 | // HOOK : actions 390 | Extender.run("actions", hook => hook(el, action, this, instance)) 391 | 392 | return el; 393 | 394 | } 395 | 396 | 397 | /** 398 | * Append actions to the toast 399 | */ 400 | let appendActions = () => { 401 | 402 | let options = this.options; 403 | let hasActions = false; 404 | 405 | let actionWrapper = document.createElement('span'); 406 | actionWrapper.classList.add('actions-wrapper'); 407 | 408 | // create and append actions 409 | if (Array.isArray(options.action)) { 410 | options.action.forEach((action) => { 411 | let el = createAction(action); 412 | if (el) { 413 | actionWrapper.appendChild(el); 414 | hasActions = true; 415 | } 416 | }) 417 | } 418 | else if (typeof options.action === 'object') { 419 | let action = createAction(options.action); 420 | if (action) { 421 | actionWrapper.appendChild(action); 422 | hasActions = true 423 | } 424 | } 425 | 426 | if(hasActions) this.toast.appendChild(actionWrapper); 427 | } 428 | 429 | /** 430 | * Set Toast duration to fade away 431 | */ 432 | let setDuration = () => { 433 | 434 | // Allows timer to be pause while being panned 435 | let timeLeft = this.options.duration; 436 | let counterInterval; 437 | if (timeLeft !== null) { 438 | counterInterval = setInterval(() => { 439 | if (this.toast.parentNode === null) 440 | window.clearInterval(counterInterval); 441 | 442 | // If toast is not being dragged, decrease its time remaining 443 | if (!this.toast.classList.contains('panning')) { 444 | timeLeft -= 20; 445 | } 446 | 447 | if (timeLeft <= 0) { 448 | // Animate toast out 449 | 450 | animations.animateOut(this.toast, () => { 451 | // Call the optional callback 452 | if (typeof(this.options.onComplete) === "function") 453 | this.options.onComplete(); 454 | 455 | // Remove toast after it times out 456 | this.destroy(); 457 | 458 | }) 459 | 460 | window.clearInterval(counterInterval); 461 | } 462 | }, 20); 463 | } 464 | 465 | } 466 | 467 | /** 468 | * Change Text of the Toast 469 | * 470 | * @param message 471 | * @returns {Toast} 472 | */ 473 | this.text = (message) => { 474 | appendMessage(message); 475 | return this; 476 | } 477 | 478 | /** 479 | * Delete the Toast with Animation and Delay 480 | * 481 | * @param delay 482 | * @returns {boolean} 483 | */ 484 | this.delete = (delay = 300) => { 485 | 486 | setTimeout(() => { 487 | animations.animateOut(this.toast, () => { 488 | this.destroy(); 489 | }) 490 | }, delay); 491 | 492 | return true; 493 | } 494 | 495 | /** 496 | * Destroy the Toast and Unregister from Instance 497 | */ 498 | this.destroy = () => { 499 | 500 | instance.toasts = instance.toasts.filter((t) => { 501 | return t.id !== this.id; 502 | }) 503 | 504 | if (this.toast.parentNode) this.toast.parentNode.removeChild(this.toast) 505 | } 506 | 507 | /** 508 | * @deprecated since 0.0.11 509 | * @param delay 510 | */ 511 | this.goAway = (delay) => { 512 | return this.delete(delay); 513 | } 514 | 515 | 516 | /** 517 | * @deprecated since 0.0.11 518 | * @type {*} 519 | */ 520 | this.el = this.toast; 521 | 522 | 523 | return this; 524 | }; 525 | 526 | export default Toast; -------------------------------------------------------------------------------- /dist/toasted.min.js: -------------------------------------------------------------------------------- 1 | !function(t,e){if("object"==typeof exports&&"object"==typeof module)module.exports=e();else if("function"==typeof define&&define.amd)define([],e);else{var n=e();for(var i in n)("object"==typeof exports?exports:t)[i]=n[i]}}(this,function(){return function(t){function e(i){if(n[i])return n[i].exports;var r=n[i]={i:i,l:!1,exports:{}};return t[i].call(r.exports,r,r.exports,e),r.l=!0,r.exports}var n={};return e.m=t,e.c=n,e.d=function(t,n,i){e.o(t,n)||Object.defineProperty(t,n,{configurable:!1,enumerable:!0,get:i})},e.n=function(t){var n=t&&t.__esModule?function(){return t.default}:function(){return t};return e.d(n,"a",n),n},e.o=function(t,e){return Object.prototype.hasOwnProperty.call(t,e)},e.p="",e(e.s=5)}([function(t,e,n){"use strict";function i(){h=!1}function r(t){if(!t)return void(l!==d&&(l=d,i()));if(t!==l){if(t.length!==d.length)throw new Error("Custom alphabet for shortid must be "+d.length+" unique characters. You submitted "+t.length+" characters: "+t);var e=t.split("").filter(function(t,e,n){return e!==n.lastIndexOf(t)});if(e.length)throw new Error("Custom alphabet for shortid must be "+d.length+" unique characters. These characters were not unique: "+e.join(", "));l=t,i()}}function o(t){return r(t),l}function s(t){p.seed(t),f!==t&&(i(),f=t)}function a(){l||r(d);for(var t,e=l.split(""),n=[],i=p.nextValue();e.length>0;)i=p.nextValue(),t=Math.floor(i*e.length),n.push(e.splice(t,1)[0]);return n.join("")}function u(){return h||(h=a())}function c(t){return u()[t]}var l,f,h,p=n(10),d="0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ_-";t.exports={characters:o,seed:s,lookup:c,shuffled:u}},function(t,e,n){"use strict";function i(t){return t&&t.__esModule?t:{default:t}}Object.defineProperty(e,"__esModule",{value:!0}),e.Toasted=e.Extender=void 0;var r=n(6),o=i(r),s=n(2),a=i(s),u=n(3);n(16).polyfill();var c=e.Extender=function(){return{hook:{options:[],actions:[]},run:function(t,e){if(!Array.isArray(this.hook[t]))return void console.warn("[toasted] : hook not found");this.hook[t].forEach(function(t){(t||"function"==typeof t)&&e&&e(t)})},utils:{warn:function(t){console.warn("[toasted] : "+t)}}}}(),l=e.Toasted=function t(e){var n=this;e||(e={}),this.id=u.generate(),this.options=e,this.global={},this.groups=[],this.toasts=[],this.group=function(e){e||(e={}),e.globalToasts||(e.globalToasts={}),Object.assign(e.globalToasts,n.global);var i=new t(e);return n.groups.push(i),i};var i=function(t,e){var i=Object.assign({},n.options);return Object.assign(i,e),new o.default(n).create(t,i)},r=function(){var t=n.options.globalToasts,e=function(t,e){return"string"==typeof e&&n[e]?n[e].apply(n,[t,{}]):i(t,e)};t&&(n.global={},Object.keys(t).forEach(function(i){n.global[i]=function(){var n=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{};return t[i].apply(null,[n,e])}}))};return this.register=function(t,e,i){i=i||{},n.options.globalToasts||(n.options.globalToasts={}),n.options.globalToasts[t]=function(t,n){return"function"==typeof e&&(e=e(t)),n(e,i)},r()},this.show=function(t,e){return i(t,e)},this.success=function(t,e){return e=e||{},e.type="success",i(t,e)},this.info=function(t,e){return e=e||{},e.type="info",i(t,e)},this.error=function(t,e){return e=e||{},e.type="error",i(t,e)},this.clear=function(){var t=n.toasts,e=t.slice(-1)[0];e&&e.options.position.includes("top")&&(t=t.reverse()),a.default.clearAnimation(t),n.toasts=[]},r(),this};e.default={Toasted:l,Extender:c}},function(t,e,n){"use strict";Object.defineProperty(e,"__esModule",{value:!0});var i=n(8),r=function(t){return t&&t.__esModule?t:{default:t}}(i);e.default={animateIn:function(t){(0,r.default)({targets:t,translateY:"-35px",opacity:1,duration:300,easing:"easeOutCubic"})},animateOut:function(t,e){(0,r.default)({targets:t,opacity:0,marginTop:"-40px",duration:300,easing:"easeOutExpo",complete:e})},animateReset:function(t){(0,r.default)({targets:t,left:0,opacity:1,duration:300,easing:"easeOutExpo"})},animatePanning:function(t,e,n){(0,r.default)({targets:t,duration:10,easing:"easeOutQuad",left:e,opacity:n})},animatePanEnd:function(t,e){(0,r.default)({targets:t,opacity:0,duration:300,easing:"easeOutExpo",complete:e})},clearAnimation:function(t){var e=r.default.timeline();t.forEach(function(t){e.add({targets:t.el,opacity:0,right:"-40px",duration:300,offset:"-=150",easing:"easeOutExpo",complete:function(){t.destroy()}})})}}},function(t,e,n){"use strict";t.exports=n(9)},function(t,e,n){"use strict";function i(t,e){for(var n,i=0,o="";!n;)o+=t(e>>4*i&15|r()),n=e80?c.default.animatePanEnd(t,function(){"function"==typeof e.options.onComplete&&e.options.onComplete(),e.destroy()}):(t.classList.remove("panning"),c.default.animateReset(t))})},h=function(){var t=e.options;if(t.icon){var n=document.createElement("i");n.classList.add("material-icons"),n.style.color=t.icon.color?t.icon.color:t.color,t.icon.after&&t.icon.name?(n.textContent=t.icon.name,n.classList.add("after"),e.toast.appendChild(n)):t.icon.name?(n.textContent=t.icon.name,e.toast.insertBefore(n,e.toast.firstChild)):(n.textContent=t.icon,e.toast.insertBefore(n,e.toast.firstChild))}},p=function(n){if(!n)return null;var i=document.createElement("a");if(i.style.color=n.color?n.color:e.options.color,i.classList.add("action"),n.text&&(i.text=n.text),n.href&&(i.href=n.href),n.icon){i.classList.add("icon");var o=document.createElement("i");o.classList.add("material-icons"),o.textContent=n.icon,i.appendChild(o)}if(n.class)switch(r(n.class)){case"string":n.class.split(" ").forEach(function(t){i.classList.add(t)});break;case"array":n.class.forEach(function(t){i.classList.add(t)})}return n.onClick&&"function"==typeof n.onClick&&i.addEventListener("click",function(t){n.onClick&&(t.preventDefault(),n.onClick(t,e))}),a.Extender.run("actions",function(r){return r(i,n,e,t)}),i},d=function(){var t=e.options,n=!1,i=document.createElement("span");if(i.classList.add("actions-wrapper"),Array.isArray(t.action))t.action.forEach(function(t){var e=p(t);e&&(i.appendChild(e),n=!0)});else if("object"===r(t.action)){var o=p(t.action);o&&(i.appendChild(o),n=!0)}n&&e.toast.appendChild(i)},v=function(){var t=e.options.duration,n=void 0;null!==t&&(n=setInterval(function(){null===e.toast.parentNode&&window.clearInterval(n),e.toast.classList.contains("panning")||(t-=20),t<=0&&(c.default.animateOut(e.toast,function(){"function"==typeof e.options.onComplete&&e.options.onComplete(),e.destroy()}),window.clearInterval(n))},20))};return this.text=function(t){return i(t),e},this.delete=function(){var t=arguments.length>0&&void 0!==arguments[0]?arguments[0]:300;return setTimeout(function(){c.default.animateOut(e.toast,function(){e.destroy()})},t),!0},this.destroy=function(){t.toasts=t.toasts.filter(function(t){return t.id!==e.id}),e.toast.parentNode&&e.toast.parentNode.removeChild(e.toast)},this.goAway=function(t){return e.delete(t)},this.el=this.toast,this};e.default=f},function(t,e,n){var i;!function(r,o,s,a){"use strict";function u(t,e,n){return setTimeout(p(t,n),e)}function c(t,e,n){return!!Array.isArray(t)&&(l(t,n[e],n),!0)}function l(t,e,n){var i;if(t)if(t.forEach)t.forEach(e,n);else if(t.length!==a)for(i=0;i\s*\(/gm,"{anonymous}()@"):"Unknown Stack Trace",o=r.console&&(r.console.warn||r.console.log);return o&&o.call(r.console,i,n),t.apply(this,arguments)}}function h(t,e,n){var i,r=e.prototype;i=t.prototype=Object.create(r),i.constructor=t,i._super=r,n&&dt(i,n)}function p(t,e){return function(){return t.apply(e,arguments)}}function d(t,e){return typeof t==gt?t.apply(e?e[0]||a:a,e):t}function v(t,e){return t===a?e:t}function m(t,e,n){l(b(e),function(e){t.addEventListener(e,n,!1)})}function g(t,e,n){l(b(e),function(e){t.removeEventListener(e,n,!1)})}function y(t,e){for(;t;){if(t==e)return!0;t=t.parentNode}return!1}function T(t,e){return t.indexOf(e)>-1}function b(t){return t.trim().split(/\s+/g)}function E(t,e,n){if(t.indexOf&&!n)return t.indexOf(e);for(var i=0;in[e]}):i.sort()),i}function C(t,e){for(var n,i,r=e[0].toUpperCase()+e.slice(1),o=0;o1&&!n.firstMultiple?n.firstMultiple=j(e):1===r&&(n.firstMultiple=!1);var o=n.firstInput,s=n.firstMultiple,a=s?s.center:o.center,u=e.center=N(i);e.timeStamp=bt(),e.deltaTime=e.timeStamp-o.timeStamp,e.angle=X(a,u),e.distance=R(a,u),S(n,e),e.offsetDirection=D(e.deltaX,e.deltaY);var c=L(e.deltaTime,e.deltaX,e.deltaY);e.overallVelocityX=c.x,e.overallVelocityY=c.y,e.overallVelocity=Tt(c.x)>Tt(c.y)?c.x:c.y,e.scale=s?z(s.pointers,i):1,e.rotation=s?Y(s.pointers,i):0,e.maxPointers=n.prevInput?e.pointers.length>n.prevInput.maxPointers?e.pointers.length:n.prevInput.maxPointers:e.pointers.length,k(n,e);var l=t.element;y(e.srcEvent.target,l)&&(l=e.srcEvent.target),e.target=l}function S(t,e){var n=e.center,i=t.offsetDelta||{},r=t.prevDelta||{},o=t.prevInput||{};e.eventType!==It&&o.eventType!==St||(r=t.prevDelta={x:o.deltaX||0,y:o.deltaY||0},i=t.offsetDelta={x:n.x,y:n.y}),e.deltaX=r.x+(n.x-i.x),e.deltaY=r.y+(n.y-i.y)}function k(t,e){var n,i,r,o,s=t.lastInterval||e,u=e.timeStamp-s.timeStamp;if(e.eventType!=kt&&(u>At||s.velocity===a)){var c=e.deltaX-s.deltaX,l=e.deltaY-s.deltaY,f=L(u,c,l);i=f.x,r=f.y,n=Tt(f.x)>Tt(f.y)?f.x:f.y,o=D(c,l),t.lastInterval=e}else n=s.velocity,i=s.velocityX,r=s.velocityY,o=s.direction;e.velocity=n,e.velocityX=i,e.velocityY=r,e.direction=o}function j(t){for(var e=[],n=0;n=Tt(e)?t<0?Nt:Lt:e<0?Dt:Rt}function R(t,e,n){n||(n=Ft);var i=e[n[0]]-t[n[0]],r=e[n[1]]-t[n[1]];return Math.sqrt(i*i+r*r)}function X(t,e,n){n||(n=Ft);var i=e[n[0]]-t[n[0]],r=e[n[1]]-t[n[1]];return 180*Math.atan2(r,i)/Math.PI}function Y(t,e){return X(e[1],e[0],qt)+X(t[1],t[0],qt)}function z(t,e){return R(e[0],e[1],qt)/R(t[0],t[1],qt)}function F(){this.evEl=Wt,this.evWin=Ht,this.pressed=!1,P.apply(this,arguments)}function q(){this.evEl=Zt,this.evWin=Bt,P.apply(this,arguments),this.store=this.manager.session.pointerEvents=[]}function V(){this.evTarget=Qt,this.evWin=Jt,this.started=!1,P.apply(this,arguments)}function W(t,e){var n=x(t.touches),i=x(t.changedTouches);return e&(St|kt)&&(n=w(n.concat(i),"identifier",!0)),[n,i]}function H(){this.evTarget=te,this.targetIds={},P.apply(this,arguments)}function U(t,e){var n=x(t.touches),i=this.targetIds;if(e&(It|_t)&&1===n.length)return i[n[0].identifier]=!0,[n,n];var r,o,s=x(t.changedTouches),a=[],u=this.target;if(o=n.filter(function(t){return y(t.target,u)}),e===It)for(r=0;r-1&&i.splice(t,1)};setTimeout(r,ee)}}function G(t){for(var e=t.srcEvent.clientX,n=t.srcEvent.clientY,i=0;i-1&&this.requireFail.splice(e,1),this},hasRequireFailures:function(){return this.requireFail.length>0},canRecognizeWith:function(t){return!!this.simultaneous[t.id]},emit:function(t){function e(e){n.manager.emit(e,t)}var n=this,i=this.state;i=de&&e(n.options.event+tt(i))},tryEmit:function(t){if(this.canEmit())return this.emit(t);this.state=32},canEmit:function(){for(var t=0;te.threshold&&r&e.direction},attrTest:function(t){return it.prototype.attrTest.call(this,t)&&(this.state&he||!(this.state&he)&&this.directionTest(t))},emit:function(t){this.pX=t.deltaX,this.pY=t.deltaY;var e=et(t.direction);e&&(t.additionalEvent=this.options.event+e),this._super.emit.call(this,t)}}),h(ot,it,{defaults:{event:"pinch",threshold:0,pointers:2},getTouchAction:function(){return[ae]},attrTest:function(t){return this._super.attrTest.call(this,t)&&(Math.abs(t.scale-1)>this.options.threshold||this.state&he)},emit:function(t){if(1!==t.scale){var e=t.scale<1?"in":"out";t.additionalEvent=this.options.event+e}this._super.emit.call(this,t)}}),h(st,K,{defaults:{event:"press",pointers:1,time:251,threshold:9},getTouchAction:function(){return[oe]},process:function(t){var e=this.options,n=t.pointers.length===e.pointers,i=t.distancee.time;if(this._input=t,!i||!n||t.eventType&(St|kt)&&!r)this.reset();else if(t.eventType&It)this.reset(),this._timer=u(function(){this.state=ve,this.tryEmit()},e.time,this);else if(t.eventType&St)return ve;return 32},reset:function(){clearTimeout(this._timer)},emit:function(t){this.state===ve&&(t&&t.eventType&St?this.manager.emit(this.options.event+"up",t):(this._input.timeStamp=bt(),this.manager.emit(this.options.event,this._input)))}}),h(at,it,{defaults:{event:"rotate",threshold:0,pointers:2},getTouchAction:function(){return[ae]},attrTest:function(t){return this._super.attrTest.call(this,t)&&(Math.abs(t.rotation)>this.options.threshold||this.state&he)}}),h(ut,it,{defaults:{event:"swipe",threshold:10,velocity:.3,direction:Xt|Yt,pointers:1},getTouchAction:function(){return rt.prototype.getTouchAction.call(this)},attrTest:function(t){var e,n=this.options.direction;return n&(Xt|Yt)?e=t.overallVelocity:n&Xt?e=t.overallVelocityX:n&Yt&&(e=t.overallVelocityY),this._super.attrTest.call(this,t)&&n&t.offsetDirection&&t.distance>this.options.threshold&&t.maxPointers==this.options.pointers&&Tt(e)>this.options.velocity&&t.eventType&St},emit:function(t){var e=et(t.offsetDirection);e&&this.manager.emit(this.options.event+e,t),this.manager.emit(this.options.event,t)}}),h(ct,K,{defaults:{event:"tap",pointers:1,taps:1,interval:300,time:250,threshold:9,posThreshold:10},getTouchAction:function(){return[se]},process:function(t){var e=this.options,n=t.pointers.length===e.pointers,i=t.distancen&&(n+=1),1n?e:n<2/3?t+(e-t)*(2/3-n)*6:t}var n=/hsl\((\d+),\s*([\d.]+)%,\s*([\d.]+)%\)/g.exec(t);t=parseInt(n[1])/360;var i=parseInt(n[2])/100,n=parseInt(n[3])/100;if(0==i)i=n=t=n;else{var r=.5>n?n*(1+i):n+i-n*i,o=2*n-r,i=e(o,r,t+1/3),n=e(o,r,t);t=e(o,r,t-1/3)}return"rgb("+255*i+","+255*n+","+255*t+")"}function l(t){if(t=/([\+\-]?[0-9#\.]+)(%|px|pt|em|rem|in|cm|mm|ex|pc|vw|vh|deg|rad|turn)?/.exec(t))return t[2]}function f(t){return-1=h.currentTime)for(var T=0;Tp&&g=d&&(h.began=!0,r("begin")),r("run")):(g<=p&&0!==v&&(i(0),m&&o()),g>=s&&v!==s&&(i(s),m||o())),t>=s&&(h.remaining?(u=a,"alternate"===h.direction&&(h.reversed=!h.reversed)):(h.pause(),"Promise"in window&&(l(),f=e()),h.completed||(h.completed=!0,r("complete"))),c=0),r("update")}t=void 0===t?{}:t;var a,u,c=0,l=null,f=e(),h=_(t);return h.reset=function(){var t=h.direction,e=h.loop;for(h.currentTime=0,h.progress=0,h.paused=!0,h.began=!1,h.completed=!1,h.reversed="reverse"===t,h.remaining="alternate"===t&&1===e?2:e,t=h.children.length;t--;)e=h.children[t],e.seek(e.offset),e.reset()},h.tick=function(t){a=t,u||(u=a),s((c+a-u)*S.speed)},h.seek=function(t){s(n(t))},h.pause=function(){var t=z.indexOf(h);-1=e&&0<=i&&1>=i){var o=new Float32Array(11);if(e!==n||i!==r)for(var s=0;11>s;++s)o[s]=t(.1*s,e,i);return function(s){if(e===n&&i===r)return s;if(0===s)return 0;if(1===s)return 1;for(var a=0,u=1;10!==u&&o[u]<=s;++u)a+=.1;--u;var u=a+(s-o[u])/(o[u+1]-o[u])*.1,c=3*(1-3*i+3*e)*u*u+2*(3*i-6*e)*u+3*e;if(.001<=c){for(a=0;4>a&&0!==(c=3*(1-3*i+3*e)*u*u+2*(3*i-6*e)*u+3*e);++a)var l=t(u,e,i)-s,u=u-l/c;s=u}else if(0===c)s=u;else{var u=a,a=a+.1,f=0;do{l=u+(a-u)/2,c=t(l,e,i)-s,0++f);s=l}return t(s,n,r)}}}}(),X=function(){function t(t,e){return 0===t||1===t?t:-Math.pow(2,10*(t-1))*Math.sin(2*(t-1-e/(2*Math.PI)*Math.asin(1))*Math.PI/e)}var e,n="Quad Cubic Quart Quint Sine Expo Circ Back Elastic".split(" "),i={In:[[.55,.085,.68,.53],[.55,.055,.675,.19],[.895,.03,.685,.22],[.755,.05,.855,.06],[.47,0,.745,.715],[.95,.05,.795,.035],[.6,.04,.98,.335],[.6,-.28,.735,.045],t],Out:[[.25,.46,.45,.94],[.215,.61,.355,1],[.165,.84,.44,1],[.23,1,.32,1],[.39,.575,.565,1],[.19,1,.22,1],[.075,.82,.165,1],[.175,.885,.32,1.275],function(e,n){return 1-t(1-e,n)}],InOut:[[.455,.03,.515,.955],[.645,.045,.355,1],[.77,0,.175,1],[.86,0,.07,1],[.445,.05,.55,.95],[1,0,0,1],[.785,.135,.15,.86],[.68,-.55,.265,1.55],function(e,n){return.5>e?t(2*e,n)/2:1-t(-2*e+2,n)/2}]},r={linear:R(.25,.25,.75,.75)},o={};for(e in i)o.type=e,i[o.type].forEach(function(t){return function(e,i){r["ease"+t.type+n[i]]=D.fnc(e)?e:R.apply(s,e)}}(o)),o={type:o.type};return r}(),Y={css:function(t,e,n){return t.style[e]=n},attribute:function(t,e,n){return t.setAttribute(e,n)},object:function(t,e,n){return t[e]=n},transform:function(t,e,n,i,r){i[r]||(i[r]=[]),i[r].push(e+"("+n+")")}},z=[],F=0,q=function(){function t(){F=requestAnimationFrame(e)}function e(e){var n=z.length;if(n){for(var i=0;in&&(e.duration=t.duration),t.began=!0,e.children.push(t)}),e.reset(),e.seek(0),e.autoplay&&e.restart(),e},e},S.random=function(t,e){return Math.floor(Math.random()*(e-t+1))+t},S})},function(t,e,n){"use strict";function i(e){return a.seed(e),t.exports}function r(e){return f=e,t.exports}function o(t){return void 0!==t&&a.characters(t),a.shuffled()}function s(){return c(f)}var a=n(0),u=(n(4),n(12)),c=n(13),l=n(14),f=n(15)||0;t.exports=s,t.exports.generate=s,t.exports.seed=i,t.exports.worker=r,t.exports.characters=o,t.exports.decode=u,t.exports.isValid=l},function(t,e,n){"use strict";function i(){return(o=(9301*o+49297)%233280)/233280}function r(t){o=t}var o=1;t.exports={nextValue:i,seed:r}},function(t,e,n){"use strict";function i(){if(!r||!r.getRandomValues)return 48&Math.floor(256*Math.random());var t=new Uint8Array(1);return r.getRandomValues(t),48&t[0]}var r="object"==typeof window&&(window.crypto||window.msCrypto);t.exports=i},function(t,e,n){"use strict";function i(t){var e=r.shuffled();return{version:15&e.indexOf(t.substr(0,1)),worker:15&e.indexOf(t.substr(1,1))}}var r=n(0);t.exports=i},function(t,e,n){"use strict";function i(t){var e="",n=Math.floor(.001*(Date.now()-u));return n===o?r++:(r=0,o=n),e+=s(a.lookup,c),e+=s(a.lookup,t),r>0&&(e+=s(a.lookup,r)),e+=s(a.lookup,n)}var r,o,s=n(4),a=n(0),u=1459707606518,c=6;t.exports=i},function(t,e,n){"use strict";function i(t){if(!t||"string"!=typeof t||t.length<6)return!1;for(var e=r.characters(),n=t.length,i=0;i 0) { 148 | r = randomFromSeed.nextValue(); 149 | characterIndex = Math.floor(r * sourceArray.length); 150 | targetArray.push(sourceArray.splice(characterIndex, 1)[0]); 151 | } 152 | return targetArray.join(''); 153 | } 154 | 155 | function getShuffled() { 156 | if (shuffled) { 157 | return shuffled; 158 | } 159 | shuffled = shuffle(); 160 | return shuffled; 161 | } 162 | 163 | /** 164 | * lookup shuffled letter 165 | * @param index 166 | * @returns {string} 167 | */ 168 | function lookup(index) { 169 | var alphabetShuffled = getShuffled(); 170 | return alphabetShuffled[index]; 171 | } 172 | 173 | module.exports = { 174 | characters: characters, 175 | seed: setSeed, 176 | lookup: lookup, 177 | shuffled: getShuffled 178 | }; 179 | 180 | 181 | /***/ }), 182 | /* 1 */ 183 | /***/ (function(module, exports, __webpack_require__) { 184 | 185 | "use strict"; 186 | 187 | 188 | Object.defineProperty(exports, "__esModule", { 189 | value: true 190 | }); 191 | exports.Toasted = exports.Extender = undefined; 192 | 193 | var _toast = __webpack_require__(6); 194 | 195 | var _toast2 = _interopRequireDefault(_toast); 196 | 197 | var _animations = __webpack_require__(2); 198 | 199 | var _animations2 = _interopRequireDefault(_animations); 200 | 201 | function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } 202 | 203 | var uuid = __webpack_require__(3); 204 | 205 | 206 | // add Object.assign Polyfill 207 | __webpack_require__(16).polyfill(); 208 | 209 | /** 210 | * Allows Toasted to be Extended 211 | * 212 | */ 213 | var Extender = exports.Extender = function () { 214 | 215 | return { 216 | hook: { 217 | options: [], 218 | actions: [] 219 | }, 220 | run: function run(name, callback) { 221 | 222 | if (!Array.isArray(this.hook[name])) { 223 | console.warn("[toasted] : hook not found"); 224 | return; 225 | } 226 | 227 | this.hook[name].forEach(function (hook) { 228 | 229 | // check if it is a function 230 | if (!hook && typeof hook !== 'function') return; 231 | 232 | callback && callback(hook); 233 | }); 234 | }, 235 | utils: { 236 | warn: function warn(message) { 237 | console.warn('[toasted] : ' + message); 238 | } 239 | } 240 | }; 241 | }(); 242 | 243 | /** 244 | * Toast 245 | * core instance of toast 246 | * 247 | * @param _options 248 | * @returns {Toasted} 249 | * @constructor 250 | */ 251 | var Toasted = exports.Toasted = function Toasted(_options) { 252 | var _this = this; 253 | 254 | if (!_options) _options = {}; 255 | 256 | /** 257 | * Unique id of the toast 258 | */ 259 | this.id = uuid.generate(); 260 | 261 | /** 262 | * Shared Options of the Toast 263 | */ 264 | this.options = _options; 265 | 266 | /** 267 | * Shared Toasts list 268 | */ 269 | this.global = {}; 270 | 271 | /** 272 | * All Registered Groups 273 | */ 274 | this.groups = []; 275 | 276 | /** 277 | * All Registered Toasts 278 | */ 279 | this.toasts = []; 280 | 281 | /** 282 | * Create New Group of Toasts 283 | * 284 | * @param o 285 | */ 286 | this.group = function (o) { 287 | 288 | if (!o) o = {}; 289 | 290 | if (!o.globalToasts) { 291 | o.globalToasts = {}; 292 | } 293 | 294 | // share parents global toasts 295 | Object.assign(o.globalToasts, _this.global); 296 | 297 | // tell parent about the group 298 | var group = new Toasted(o); 299 | _this.groups.push(group); 300 | 301 | return group; 302 | }; 303 | 304 | /** 305 | * Base Toast Show Function 306 | * 307 | * @param message 308 | * @param options 309 | * @returns {*} 310 | * @private 311 | */ 312 | var _show = function _show(message, options) { 313 | 314 | // clone the global options 315 | var _options = Object.assign({}, _this.options); 316 | 317 | // merge the cached global options with options 318 | Object.assign(_options, options); 319 | 320 | var toast = new _toast2.default(_this); 321 | return toast.create(message, _options); 322 | }; 323 | 324 | /** 325 | * 326 | */ 327 | var initiateCustomToasts = function initiateCustomToasts() { 328 | 329 | var customToasts = _this.options.globalToasts; 330 | 331 | // this will initiate toast for the custom toast. 332 | var initiate = function initiate(message, options) { 333 | 334 | // check if passed option is a available method if so call it. 335 | if (typeof options === 'string' && _this[options]) { 336 | return _this[options].apply(_this, [message, {}]); 337 | } 338 | 339 | // or else create a new toast with passed options. 340 | return _show(message, options); 341 | }; 342 | 343 | if (customToasts) { 344 | 345 | _this.global = {}; 346 | 347 | Object.keys(customToasts).forEach(function (key) { 348 | 349 | // register the custom toast events to the Toast.custom property 350 | _this.global[key] = function () { 351 | var payload = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {}; 352 | 353 | 354 | // return the it in order to expose the Toast methods 355 | return customToasts[key].apply(null, [payload, initiate]); 356 | }; 357 | }); 358 | } 359 | }; 360 | 361 | /** 362 | * Register a Global Toast 363 | * 364 | * @param name 365 | * @param message 366 | * @param options 367 | */ 368 | this.register = function (name, message, options) { 369 | options = options || {}; 370 | 371 | !_this.options.globalToasts ? _this.options.globalToasts = {} : null; 372 | 373 | _this.options.globalToasts[name] = function (payload, initiate) { 374 | 375 | if (typeof message === 'function') { 376 | message = message(payload); 377 | } 378 | 379 | return initiate(message, options); 380 | }; 381 | 382 | initiateCustomToasts(); 383 | }; 384 | 385 | /** 386 | * Show a Simple Toast 387 | * 388 | * @param message 389 | * @param options 390 | * @returns {*} 391 | */ 392 | this.show = function (message, options) { 393 | return _show(message, options); 394 | }; 395 | 396 | /** 397 | * Show a Toast with Success Style 398 | * 399 | * @param message 400 | * @param options 401 | * @returns {*} 402 | */ 403 | this.success = function (message, options) { 404 | options = options || {}; 405 | options.type = "success"; 406 | return _show(message, options); 407 | }; 408 | 409 | /** 410 | * Show a Toast with Info Style 411 | * 412 | * @param message 413 | * @param options 414 | * @returns {*} 415 | */ 416 | this.info = function (message, options) { 417 | options = options || {}; 418 | options.type = "info"; 419 | return _show(message, options); 420 | }; 421 | 422 | /** 423 | * Show a Toast with Error Style 424 | * 425 | * @param message 426 | * @param options 427 | * @returns {*} 428 | */ 429 | this.error = function (message, options) { 430 | options = options || {}; 431 | options.type = "error"; 432 | return _show(message, options); 433 | }; 434 | 435 | /** 436 | * Clear All Toasts 437 | * 438 | * @returns {boolean} 439 | */ 440 | this.clear = function () { 441 | 442 | var toasts = _this.toasts; 443 | var last = toasts.slice(-1)[0]; 444 | 445 | // start vanishing from the bottom if toasts are on top 446 | if (last && last.options.position.includes('top')) { 447 | toasts = toasts.reverse(); 448 | } 449 | 450 | _animations2.default.clearAnimation(toasts); 451 | _this.toasts = []; 452 | }; 453 | 454 | /** 455 | * Initiate custom toasts 456 | */ 457 | initiateCustomToasts(); 458 | 459 | return this; 460 | }; 461 | 462 | exports.default = { Toasted: Toasted, Extender: Extender }; 463 | 464 | /***/ }), 465 | /* 2 */ 466 | /***/ (function(module, exports, __webpack_require__) { 467 | 468 | "use strict"; 469 | 470 | 471 | Object.defineProperty(exports, "__esModule", { 472 | value: true 473 | }); 474 | 475 | var _animejs = __webpack_require__(8); 476 | 477 | var _animejs2 = _interopRequireDefault(_animejs); 478 | 479 | function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } 480 | 481 | var duration = 300; 482 | 483 | exports.default = { 484 | animateIn: function animateIn(el) { 485 | (0, _animejs2.default)({ 486 | targets: el, 487 | translateY: '-35px', 488 | opacity: 1, 489 | duration: duration, 490 | easing: 'easeOutCubic' 491 | }); 492 | }, 493 | animateOut: function animateOut(el, onComplete) { 494 | (0, _animejs2.default)({ 495 | targets: el, 496 | opacity: 0, 497 | marginTop: '-40px', 498 | duration: duration, 499 | easing: 'easeOutExpo', 500 | complete: onComplete 501 | }); 502 | }, 503 | animateReset: function animateReset(el) { 504 | (0, _animejs2.default)({ 505 | targets: el, 506 | left: 0, 507 | opacity: 1, 508 | duration: duration, 509 | easing: 'easeOutExpo' 510 | }); 511 | }, 512 | animatePanning: function animatePanning(el, left, opacity) { 513 | (0, _animejs2.default)({ 514 | targets: el, 515 | duration: 10, 516 | easing: 'easeOutQuad', 517 | left: left, 518 | opacity: opacity 519 | }); 520 | }, 521 | animatePanEnd: function animatePanEnd(el, onComplete) { 522 | (0, _animejs2.default)({ 523 | targets: el, 524 | opacity: 0, 525 | duration: duration, 526 | easing: 'easeOutExpo', 527 | complete: onComplete 528 | }); 529 | }, 530 | clearAnimation: function clearAnimation(toasts) { 531 | 532 | var timeline = _animejs2.default.timeline(); 533 | 534 | toasts.forEach(function (t) { 535 | timeline.add({ 536 | targets: t.el, 537 | opacity: 0, 538 | right: '-40px', 539 | duration: 300, 540 | offset: '-=150', 541 | easing: 'easeOutExpo', 542 | complete: function complete() { 543 | t.destroy(); 544 | } 545 | }); 546 | }); 547 | } 548 | }; 549 | 550 | /***/ }), 551 | /* 3 */ 552 | /***/ (function(module, exports, __webpack_require__) { 553 | 554 | "use strict"; 555 | 556 | module.exports = __webpack_require__(9); 557 | 558 | 559 | /***/ }), 560 | /* 4 */ 561 | /***/ (function(module, exports, __webpack_require__) { 562 | 563 | "use strict"; 564 | 565 | 566 | var randomByte = __webpack_require__(11); 567 | 568 | function encode(lookup, number) { 569 | var loopCounter = 0; 570 | var done; 571 | 572 | var str = ''; 573 | 574 | while (!done) { 575 | str = str + lookup( ( (number >> (4 * loopCounter)) & 0x0f ) | randomByte() ); 576 | done = number < (Math.pow(16, loopCounter + 1 ) ); 577 | loopCounter++; 578 | } 579 | return str; 580 | } 581 | 582 | module.exports = encode; 583 | 584 | 585 | /***/ }), 586 | /* 5 */ 587 | /***/ (function(module, exports, __webpack_require__) { 588 | 589 | "use strict"; 590 | var __WEBPACK_AMD_DEFINE_ARRAY__, __WEBPACK_AMD_DEFINE_RESULT__; 591 | 592 | Object.defineProperty(exports, "__esModule", { 593 | value: true 594 | }); 595 | 596 | var _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol" ? function (obj) { return typeof obj; } : function (obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; }; 597 | 598 | var _toasted = __webpack_require__(1); 599 | 600 | _toasted.Toasted.extend = _toasted.Extender.hook; 601 | _toasted.Toasted.utils = _toasted.Extender.utils; 602 | 603 | (function (root, factory) { 604 | if (true) { 605 | !(__WEBPACK_AMD_DEFINE_ARRAY__ = [], __WEBPACK_AMD_DEFINE_RESULT__ = function () { 606 | return root.Toasted = factory(); 607 | }.apply(exports, __WEBPACK_AMD_DEFINE_ARRAY__), 608 | __WEBPACK_AMD_DEFINE_RESULT__ !== undefined && (module.exports = __WEBPACK_AMD_DEFINE_RESULT__)); 609 | } else if ((typeof module === "undefined" ? "undefined" : _typeof(module)) === "object" && module.exports) { 610 | module.exports = root.Toasted = factory(); 611 | } else { 612 | root.Toasted = factory(); 613 | } 614 | })(window, function () { 615 | return _toasted.Toasted; 616 | }); 617 | 618 | exports.default = _toasted.Toasted; 619 | 620 | /***/ }), 621 | /* 6 */ 622 | /***/ (function(module, exports, __webpack_require__) { 623 | 624 | "use strict"; 625 | 626 | 627 | Object.defineProperty(exports, "__esModule", { 628 | value: true 629 | }); 630 | exports.Toast = undefined; 631 | 632 | var _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol" ? function (obj) { return typeof obj; } : function (obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; }; 633 | 634 | var _hammerjs = __webpack_require__(7); 635 | 636 | var _hammerjs2 = _interopRequireDefault(_hammerjs); 637 | 638 | var _toasted = __webpack_require__(1); 639 | 640 | var _animations = __webpack_require__(2); 641 | 642 | var _animations2 = _interopRequireDefault(_animations); 643 | 644 | function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } 645 | 646 | var uuid = __webpack_require__(3); 647 | 648 | var Toast = exports.Toast = function Toast(instance) { 649 | var _this = this; 650 | 651 | /** 652 | * Compiled options of the toast 653 | */ 654 | this.options = {}; 655 | 656 | /** 657 | * Unique id for the toast 658 | */ 659 | this.id = uuid.generate(); 660 | 661 | /** 662 | * Toast Html Element 663 | * 664 | * @type {null|Element} 665 | */ 666 | this.toast = null; 667 | 668 | /** 669 | * Check if Initialized the toast 670 | * 671 | * @type {boolean} 672 | */ 673 | var initialized = false; 674 | 675 | var constructor = function constructor() { 676 | instance.toasts.push(_this); 677 | }; 678 | constructor(); 679 | 680 | /** 681 | * Create Toast 682 | * 683 | * @param message 684 | * @param options 685 | * @returns {Toast} 686 | */ 687 | this.create = function (message, options) { 688 | 689 | if (!message || initialized) { 690 | return; 691 | } 692 | 693 | options = setOptions(options); 694 | var container = getContainer(); 695 | 696 | _this.toast = document.createElement('div'); 697 | _this.toast.classList.add('toasted'); 698 | 699 | // add classes 700 | if (options.className) { 701 | options.className.forEach(function (className) { 702 | _this.toast.classList.add(className); 703 | }); 704 | } 705 | 706 | // Append the Message 707 | appendMessage(message); 708 | 709 | // add the touch events of the toast 710 | addTouchEvents(); 711 | 712 | // add material icon if available 713 | createIcon(); 714 | 715 | // append the actions 716 | appendActions(); 717 | 718 | // append the toasts 719 | container.appendChild(_this.toast); 720 | 721 | // animate toast 722 | _animations2.default.animateIn(_this.toast); 723 | 724 | // set its duration 725 | setDuration(); 726 | 727 | // TODO : remove this later, this is here to backward compatibility 728 | _this.el = _this.toast; 729 | 730 | // Let Instance know the toast is initialized 731 | initialized = true; 732 | 733 | return _this; 734 | }; 735 | 736 | /** 737 | * Append Message to the Toast 738 | * 739 | * @param message 740 | */ 741 | var appendMessage = function appendMessage(message) { 742 | 743 | if (!message) { 744 | return; 745 | } 746 | 747 | // If type of parameter is HTML Element 748 | if ((typeof HTMLElement === 'undefined' ? 'undefined' : _typeof(HTMLElement)) === "object" ? message instanceof HTMLElement : message && (typeof message === 'undefined' ? 'undefined' : _typeof(message)) === "object" && message !== null && message.nodeType === 1 && typeof message.nodeName === "string") { 749 | _this.toast.appendChild(message); 750 | } else { 751 | // Insert as text; 752 | _this.toast.innerHTML = message; 753 | } 754 | }; 755 | 756 | /** 757 | * Get the Toast Container 758 | * 759 | * @returns {Element} 760 | */ 761 | var getContainer = function getContainer() { 762 | 763 | var container = document.getElementById(instance.id); 764 | 765 | if (container === null) { 766 | container = document.createElement('div'); 767 | container.id = instance.id; 768 | document.body.appendChild(container); 769 | } 770 | 771 | // check if the container classes has changed if so update it 772 | if (container.className !== _this.options.containerClass.join(' ')) { 773 | container.className = ""; 774 | _this.options.containerClass.forEach(function (className) { 775 | container.classList.add(className); 776 | }); 777 | } 778 | 779 | return container; 780 | }; 781 | 782 | /** 783 | * Parse and Set Toast Options 784 | * 785 | * @param options 786 | * @returns {*} 787 | */ 788 | var setOptions = function setOptions(options) { 789 | 790 | // toast position 791 | options.position = options.position || "top-right"; 792 | 793 | // toast duration 794 | options.duration = options.duration || null; 795 | 796 | // get action name 797 | options.action = options.action || null; 798 | 799 | // check if the fullWidth is enabled 800 | options.fullWidth = options.fullWidth || false; 801 | 802 | // check if the toast needs to be fitted in the screen (no margin gap between screen) 803 | options.fitToScreen = options.fitToScreen || null; 804 | 805 | // class name to be added on the toast 806 | options.className = options.className || null; 807 | 808 | // class name to be added on the toast container 809 | options.containerClass = options.containerClass || null; 810 | 811 | // get icon name 812 | options.icon = options.icon || null; 813 | 814 | // normal type will allow the basic color 815 | options.type = options.type || "default"; 816 | 817 | // normal type will allow the basic color 818 | options.theme = options.theme || "material"; 819 | 820 | // normal type will allow the basic color 821 | options.color = options.color || null; 822 | 823 | // get icon color 824 | options.iconColor = options.iconColor || null; 825 | 826 | // complete call back of the toast 827 | options.onComplete = options.onComplete || null; 828 | 829 | // TODO : closeOnSwipe, singleton, iconPack 830 | 831 | /* transform options */ 832 | 833 | // toast class 834 | if (options.className && typeof options.className === "string") { 835 | options.className = options.className.split(' '); 836 | } 837 | 838 | if (!options.className) { 839 | options.className = []; 840 | } 841 | 842 | options.theme && options.className.push(options.theme.trim()); 843 | options.type && options.className.push(options.type); 844 | 845 | // toast container class 846 | if (options.containerClass && typeof options.containerClass === "string") { 847 | options.containerClass = options.containerClass.split(' '); 848 | } 849 | 850 | if (!options.containerClass) { 851 | options.containerClass = []; 852 | } 853 | 854 | options.position && options.containerClass.push(options.position.trim()); 855 | options.fullWidth && options.containerClass.push('full-width'); 856 | options.fitToScreen && options.containerClass.push('fit-to-screen'); 857 | 858 | // add toasted container class to top 859 | options.containerClass.unshift('toasted-container'); 860 | 861 | // HOOK : options 862 | _toasted.Extender.run("options", function (hook) { 863 | return hook(options, _this.options); 864 | }); 865 | 866 | _this.options = options; 867 | return options; 868 | }; 869 | 870 | /** 871 | * Add Hammer Touch events to the Toast 872 | */ 873 | var addTouchEvents = function addTouchEvents() { 874 | 875 | var toast = _this.toast; 876 | 877 | // Bind hammer 878 | var hammerHandler = new _hammerjs2.default(toast, { prevent_default: false }); 879 | hammerHandler.on('pan', function (e) { 880 | var deltaX = e.deltaX; 881 | var activationDistance = 80; 882 | 883 | // Change toast state 884 | if (!toast.classList.contains('panning')) { 885 | toast.classList.add('panning'); 886 | } 887 | 888 | var opacityPercent = 1 - Math.abs(deltaX / activationDistance); 889 | if (opacityPercent < 0) opacityPercent = 0; 890 | 891 | _animations2.default.animatePanning(toast, deltaX, opacityPercent); 892 | }); 893 | 894 | hammerHandler.on('panend', function (e) { 895 | var deltaX = e.deltaX; 896 | var activationDistance = 80; 897 | 898 | // If toast dragged past activation point 899 | if (Math.abs(deltaX) > activationDistance) { 900 | 901 | _animations2.default.animatePanEnd(toast, function () { 902 | if (typeof _this.options.onComplete === "function") { 903 | _this.options.onComplete(); 904 | } 905 | 906 | _this.destroy(); 907 | }); 908 | } else { 909 | toast.classList.remove('panning'); 910 | // Put toast back into original position 911 | _animations2.default.animateReset(toast); 912 | } 913 | }); 914 | }; 915 | 916 | var createIcon = function createIcon() { 917 | 918 | var options = _this.options; 919 | 920 | // add material icon if available 921 | if (options.icon) { 922 | 923 | var iel = document.createElement('i'); 924 | iel.classList.add('material-icons'); 925 | 926 | // add color to the icon. priority : icon.color > option.color > theme 927 | iel.style.color = options.icon.color ? options.icon.color : options.color; 928 | 929 | if (options.icon.after && options.icon.name) { 930 | iel.textContent = options.icon.name; 931 | iel.classList.add('after'); 932 | _this.toast.appendChild(iel); 933 | } else if (options.icon.name) { 934 | iel.textContent = options.icon.name; 935 | _this.toast.insertBefore(iel, _this.toast.firstChild); 936 | } else { 937 | iel.textContent = options.icon; 938 | _this.toast.insertBefore(iel, _this.toast.firstChild); 939 | } 940 | } 941 | }; 942 | 943 | /** 944 | * Create Actions to the toast 945 | * 946 | * @param action 947 | * @returns {*} 948 | */ 949 | var createAction = function createAction(action) { 950 | 951 | if (!action) { 952 | return null; 953 | } 954 | 955 | var el = document.createElement('a'); 956 | 957 | // add color to icon 958 | el.style.color = action.color ? action.color : _this.options.color; 959 | 960 | el.classList.add('action'); 961 | 962 | if (action.text) { 963 | el.text = action.text; 964 | } 965 | 966 | if (action.href) { 967 | el.href = action.href; 968 | } 969 | 970 | if (action.icon) { 971 | 972 | // add icon class to style it 973 | el.classList.add('icon'); 974 | 975 | // create icon element 976 | var iel = document.createElement('i'); 977 | iel.classList.add('material-icons'); 978 | iel.textContent = action.icon; 979 | 980 | // append it to the button 981 | el.appendChild(iel); 982 | } 983 | 984 | if (action.class) { 985 | 986 | switch (_typeof(action.class)) { 987 | case 'string': 988 | action.class.split(' ').forEach(function (className) { 989 | el.classList.add(className); 990 | }); 991 | break; 992 | case 'array': 993 | action.class.forEach(function (className) { 994 | el.classList.add(className); 995 | }); 996 | } 997 | } 998 | 999 | if (action.onClick && typeof action.onClick === 'function') { 1000 | el.addEventListener('click', function (e) { 1001 | 1002 | if (action.onClick) { 1003 | e.preventDefault(); 1004 | action.onClick(e, _this); 1005 | } 1006 | }); 1007 | } 1008 | 1009 | // HOOK : actions 1010 | _toasted.Extender.run("actions", function (hook) { 1011 | return hook(el, action, _this, instance); 1012 | }); 1013 | 1014 | return el; 1015 | }; 1016 | 1017 | /** 1018 | * Append actions to the toast 1019 | */ 1020 | var appendActions = function appendActions() { 1021 | 1022 | var options = _this.options; 1023 | var hasActions = false; 1024 | 1025 | var actionWrapper = document.createElement('span'); 1026 | actionWrapper.classList.add('actions-wrapper'); 1027 | 1028 | // create and append actions 1029 | if (Array.isArray(options.action)) { 1030 | options.action.forEach(function (action) { 1031 | var el = createAction(action); 1032 | if (el) { 1033 | actionWrapper.appendChild(el); 1034 | hasActions = true; 1035 | } 1036 | }); 1037 | } else if (_typeof(options.action) === 'object') { 1038 | var action = createAction(options.action); 1039 | if (action) { 1040 | actionWrapper.appendChild(action); 1041 | hasActions = true; 1042 | } 1043 | } 1044 | 1045 | if (hasActions) _this.toast.appendChild(actionWrapper); 1046 | }; 1047 | 1048 | /** 1049 | * Set Toast duration to fade away 1050 | */ 1051 | var setDuration = function setDuration() { 1052 | 1053 | // Allows timer to be pause while being panned 1054 | var timeLeft = _this.options.duration; 1055 | var counterInterval = void 0; 1056 | if (timeLeft !== null) { 1057 | counterInterval = setInterval(function () { 1058 | if (_this.toast.parentNode === null) window.clearInterval(counterInterval); 1059 | 1060 | // If toast is not being dragged, decrease its time remaining 1061 | if (!_this.toast.classList.contains('panning')) { 1062 | timeLeft -= 20; 1063 | } 1064 | 1065 | if (timeLeft <= 0) { 1066 | // Animate toast out 1067 | 1068 | _animations2.default.animateOut(_this.toast, function () { 1069 | // Call the optional callback 1070 | if (typeof _this.options.onComplete === "function") _this.options.onComplete(); 1071 | 1072 | // Remove toast after it times out 1073 | _this.destroy(); 1074 | }); 1075 | 1076 | window.clearInterval(counterInterval); 1077 | } 1078 | }, 20); 1079 | } 1080 | }; 1081 | 1082 | /** 1083 | * Change Text of the Toast 1084 | * 1085 | * @param message 1086 | * @returns {Toast} 1087 | */ 1088 | this.text = function (message) { 1089 | appendMessage(message); 1090 | return _this; 1091 | }; 1092 | 1093 | /** 1094 | * Delete the Toast with Animation and Delay 1095 | * 1096 | * @param delay 1097 | * @returns {boolean} 1098 | */ 1099 | this.delete = function () { 1100 | var delay = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : 300; 1101 | 1102 | 1103 | setTimeout(function () { 1104 | _animations2.default.animateOut(_this.toast, function () { 1105 | _this.destroy(); 1106 | }); 1107 | }, delay); 1108 | 1109 | return true; 1110 | }; 1111 | 1112 | /** 1113 | * Destroy the Toast and Unregister from Instance 1114 | */ 1115 | this.destroy = function () { 1116 | 1117 | instance.toasts = instance.toasts.filter(function (t) { 1118 | return t.id !== _this.id; 1119 | }); 1120 | 1121 | if (_this.toast.parentNode) _this.toast.parentNode.removeChild(_this.toast); 1122 | }; 1123 | 1124 | /** 1125 | * @deprecated since 0.0.11 1126 | * @param delay 1127 | */ 1128 | this.goAway = function (delay) { 1129 | return _this.delete(delay); 1130 | }; 1131 | 1132 | /** 1133 | * @deprecated since 0.0.11 1134 | * @type {*} 1135 | */ 1136 | this.el = this.toast; 1137 | 1138 | return this; 1139 | }; 1140 | 1141 | exports.default = Toast; 1142 | 1143 | /***/ }), 1144 | /* 7 */ 1145 | /***/ (function(module, exports, __webpack_require__) { 1146 | 1147 | var __WEBPACK_AMD_DEFINE_RESULT__;/*! Hammer.JS - v2.0.7 - 2016-04-22 1148 | * http://hammerjs.github.io/ 1149 | * 1150 | * Copyright (c) 2016 Jorik Tangelder; 1151 | * Licensed under the MIT license */ 1152 | (function(window, document, exportName, undefined) { 1153 | 'use strict'; 1154 | 1155 | var VENDOR_PREFIXES = ['', 'webkit', 'Moz', 'MS', 'ms', 'o']; 1156 | var TEST_ELEMENT = document.createElement('div'); 1157 | 1158 | var TYPE_FUNCTION = 'function'; 1159 | 1160 | var round = Math.round; 1161 | var abs = Math.abs; 1162 | var now = Date.now; 1163 | 1164 | /** 1165 | * set a timeout with a given scope 1166 | * @param {Function} fn 1167 | * @param {Number} timeout 1168 | * @param {Object} context 1169 | * @returns {number} 1170 | */ 1171 | function setTimeoutContext(fn, timeout, context) { 1172 | return setTimeout(bindFn(fn, context), timeout); 1173 | } 1174 | 1175 | /** 1176 | * if the argument is an array, we want to execute the fn on each entry 1177 | * if it aint an array we don't want to do a thing. 1178 | * this is used by all the methods that accept a single and array argument. 1179 | * @param {*|Array} arg 1180 | * @param {String} fn 1181 | * @param {Object} [context] 1182 | * @returns {Boolean} 1183 | */ 1184 | function invokeArrayArg(arg, fn, context) { 1185 | if (Array.isArray(arg)) { 1186 | each(arg, context[fn], context); 1187 | return true; 1188 | } 1189 | return false; 1190 | } 1191 | 1192 | /** 1193 | * walk objects and arrays 1194 | * @param {Object} obj 1195 | * @param {Function} iterator 1196 | * @param {Object} context 1197 | */ 1198 | function each(obj, iterator, context) { 1199 | var i; 1200 | 1201 | if (!obj) { 1202 | return; 1203 | } 1204 | 1205 | if (obj.forEach) { 1206 | obj.forEach(iterator, context); 1207 | } else if (obj.length !== undefined) { 1208 | i = 0; 1209 | while (i < obj.length) { 1210 | iterator.call(context, obj[i], i, obj); 1211 | i++; 1212 | } 1213 | } else { 1214 | for (i in obj) { 1215 | obj.hasOwnProperty(i) && iterator.call(context, obj[i], i, obj); 1216 | } 1217 | } 1218 | } 1219 | 1220 | /** 1221 | * wrap a method with a deprecation warning and stack trace 1222 | * @param {Function} method 1223 | * @param {String} name 1224 | * @param {String} message 1225 | * @returns {Function} A new function wrapping the supplied method. 1226 | */ 1227 | function deprecate(method, name, message) { 1228 | var deprecationMessage = 'DEPRECATED METHOD: ' + name + '\n' + message + ' AT \n'; 1229 | return function() { 1230 | var e = new Error('get-stack-trace'); 1231 | var stack = e && e.stack ? e.stack.replace(/^[^\(]+?[\n$]/gm, '') 1232 | .replace(/^\s+at\s+/gm, '') 1233 | .replace(/^Object.\s*\(/gm, '{anonymous}()@') : 'Unknown Stack Trace'; 1234 | 1235 | var log = window.console && (window.console.warn || window.console.log); 1236 | if (log) { 1237 | log.call(window.console, deprecationMessage, stack); 1238 | } 1239 | return method.apply(this, arguments); 1240 | }; 1241 | } 1242 | 1243 | /** 1244 | * extend object. 1245 | * means that properties in dest will be overwritten by the ones in src. 1246 | * @param {Object} target 1247 | * @param {...Object} objects_to_assign 1248 | * @returns {Object} target 1249 | */ 1250 | var assign; 1251 | if (typeof Object.assign !== 'function') { 1252 | assign = function assign(target) { 1253 | if (target === undefined || target === null) { 1254 | throw new TypeError('Cannot convert undefined or null to object'); 1255 | } 1256 | 1257 | var output = Object(target); 1258 | for (var index = 1; index < arguments.length; index++) { 1259 | var source = arguments[index]; 1260 | if (source !== undefined && source !== null) { 1261 | for (var nextKey in source) { 1262 | if (source.hasOwnProperty(nextKey)) { 1263 | output[nextKey] = source[nextKey]; 1264 | } 1265 | } 1266 | } 1267 | } 1268 | return output; 1269 | }; 1270 | } else { 1271 | assign = Object.assign; 1272 | } 1273 | 1274 | /** 1275 | * extend object. 1276 | * means that properties in dest will be overwritten by the ones in src. 1277 | * @param {Object} dest 1278 | * @param {Object} src 1279 | * @param {Boolean} [merge=false] 1280 | * @returns {Object} dest 1281 | */ 1282 | var extend = deprecate(function extend(dest, src, merge) { 1283 | var keys = Object.keys(src); 1284 | var i = 0; 1285 | while (i < keys.length) { 1286 | if (!merge || (merge && dest[keys[i]] === undefined)) { 1287 | dest[keys[i]] = src[keys[i]]; 1288 | } 1289 | i++; 1290 | } 1291 | return dest; 1292 | }, 'extend', 'Use `assign`.'); 1293 | 1294 | /** 1295 | * merge the values from src in the dest. 1296 | * means that properties that exist in dest will not be overwritten by src 1297 | * @param {Object} dest 1298 | * @param {Object} src 1299 | * @returns {Object} dest 1300 | */ 1301 | var merge = deprecate(function merge(dest, src) { 1302 | return extend(dest, src, true); 1303 | }, 'merge', 'Use `assign`.'); 1304 | 1305 | /** 1306 | * simple class inheritance 1307 | * @param {Function} child 1308 | * @param {Function} base 1309 | * @param {Object} [properties] 1310 | */ 1311 | function inherit(child, base, properties) { 1312 | var baseP = base.prototype, 1313 | childP; 1314 | 1315 | childP = child.prototype = Object.create(baseP); 1316 | childP.constructor = child; 1317 | childP._super = baseP; 1318 | 1319 | if (properties) { 1320 | assign(childP, properties); 1321 | } 1322 | } 1323 | 1324 | /** 1325 | * simple function bind 1326 | * @param {Function} fn 1327 | * @param {Object} context 1328 | * @returns {Function} 1329 | */ 1330 | function bindFn(fn, context) { 1331 | return function boundFn() { 1332 | return fn.apply(context, arguments); 1333 | }; 1334 | } 1335 | 1336 | /** 1337 | * let a boolean value also be a function that must return a boolean 1338 | * this first item in args will be used as the context 1339 | * @param {Boolean|Function} val 1340 | * @param {Array} [args] 1341 | * @returns {Boolean} 1342 | */ 1343 | function boolOrFn(val, args) { 1344 | if (typeof val == TYPE_FUNCTION) { 1345 | return val.apply(args ? args[0] || undefined : undefined, args); 1346 | } 1347 | return val; 1348 | } 1349 | 1350 | /** 1351 | * use the val2 when val1 is undefined 1352 | * @param {*} val1 1353 | * @param {*} val2 1354 | * @returns {*} 1355 | */ 1356 | function ifUndefined(val1, val2) { 1357 | return (val1 === undefined) ? val2 : val1; 1358 | } 1359 | 1360 | /** 1361 | * addEventListener with multiple events at once 1362 | * @param {EventTarget} target 1363 | * @param {String} types 1364 | * @param {Function} handler 1365 | */ 1366 | function addEventListeners(target, types, handler) { 1367 | each(splitStr(types), function(type) { 1368 | target.addEventListener(type, handler, false); 1369 | }); 1370 | } 1371 | 1372 | /** 1373 | * removeEventListener with multiple events at once 1374 | * @param {EventTarget} target 1375 | * @param {String} types 1376 | * @param {Function} handler 1377 | */ 1378 | function removeEventListeners(target, types, handler) { 1379 | each(splitStr(types), function(type) { 1380 | target.removeEventListener(type, handler, false); 1381 | }); 1382 | } 1383 | 1384 | /** 1385 | * find if a node is in the given parent 1386 | * @method hasParent 1387 | * @param {HTMLElement} node 1388 | * @param {HTMLElement} parent 1389 | * @return {Boolean} found 1390 | */ 1391 | function hasParent(node, parent) { 1392 | while (node) { 1393 | if (node == parent) { 1394 | return true; 1395 | } 1396 | node = node.parentNode; 1397 | } 1398 | return false; 1399 | } 1400 | 1401 | /** 1402 | * small indexOf wrapper 1403 | * @param {String} str 1404 | * @param {String} find 1405 | * @returns {Boolean} found 1406 | */ 1407 | function inStr(str, find) { 1408 | return str.indexOf(find) > -1; 1409 | } 1410 | 1411 | /** 1412 | * split string on whitespace 1413 | * @param {String} str 1414 | * @returns {Array} words 1415 | */ 1416 | function splitStr(str) { 1417 | return str.trim().split(/\s+/g); 1418 | } 1419 | 1420 | /** 1421 | * find if a array contains the object using indexOf or a simple polyFill 1422 | * @param {Array} src 1423 | * @param {String} find 1424 | * @param {String} [findByKey] 1425 | * @return {Boolean|Number} false when not found, or the index 1426 | */ 1427 | function inArray(src, find, findByKey) { 1428 | if (src.indexOf && !findByKey) { 1429 | return src.indexOf(find); 1430 | } else { 1431 | var i = 0; 1432 | while (i < src.length) { 1433 | if ((findByKey && src[i][findByKey] == find) || (!findByKey && src[i] === find)) { 1434 | return i; 1435 | } 1436 | i++; 1437 | } 1438 | return -1; 1439 | } 1440 | } 1441 | 1442 | /** 1443 | * convert array-like objects to real arrays 1444 | * @param {Object} obj 1445 | * @returns {Array} 1446 | */ 1447 | function toArray(obj) { 1448 | return Array.prototype.slice.call(obj, 0); 1449 | } 1450 | 1451 | /** 1452 | * unique array with objects based on a key (like 'id') or just by the array's value 1453 | * @param {Array} src [{id:1},{id:2},{id:1}] 1454 | * @param {String} [key] 1455 | * @param {Boolean} [sort=False] 1456 | * @returns {Array} [{id:1},{id:2}] 1457 | */ 1458 | function uniqueArray(src, key, sort) { 1459 | var results = []; 1460 | var values = []; 1461 | var i = 0; 1462 | 1463 | while (i < src.length) { 1464 | var val = key ? src[i][key] : src[i]; 1465 | if (inArray(values, val) < 0) { 1466 | results.push(src[i]); 1467 | } 1468 | values[i] = val; 1469 | i++; 1470 | } 1471 | 1472 | if (sort) { 1473 | if (!key) { 1474 | results = results.sort(); 1475 | } else { 1476 | results = results.sort(function sortUniqueArray(a, b) { 1477 | return a[key] > b[key]; 1478 | }); 1479 | } 1480 | } 1481 | 1482 | return results; 1483 | } 1484 | 1485 | /** 1486 | * get the prefixed property 1487 | * @param {Object} obj 1488 | * @param {String} property 1489 | * @returns {String|Undefined} prefixed 1490 | */ 1491 | function prefixed(obj, property) { 1492 | var prefix, prop; 1493 | var camelProp = property[0].toUpperCase() + property.slice(1); 1494 | 1495 | var i = 0; 1496 | while (i < VENDOR_PREFIXES.length) { 1497 | prefix = VENDOR_PREFIXES[i]; 1498 | prop = (prefix) ? prefix + camelProp : property; 1499 | 1500 | if (prop in obj) { 1501 | return prop; 1502 | } 1503 | i++; 1504 | } 1505 | return undefined; 1506 | } 1507 | 1508 | /** 1509 | * get a unique id 1510 | * @returns {number} uniqueId 1511 | */ 1512 | var _uniqueId = 1; 1513 | function uniqueId() { 1514 | return _uniqueId++; 1515 | } 1516 | 1517 | /** 1518 | * get the window object of an element 1519 | * @param {HTMLElement} element 1520 | * @returns {DocumentView|Window} 1521 | */ 1522 | function getWindowForElement(element) { 1523 | var doc = element.ownerDocument || element; 1524 | return (doc.defaultView || doc.parentWindow || window); 1525 | } 1526 | 1527 | var MOBILE_REGEX = /mobile|tablet|ip(ad|hone|od)|android/i; 1528 | 1529 | var SUPPORT_TOUCH = ('ontouchstart' in window); 1530 | var SUPPORT_POINTER_EVENTS = prefixed(window, 'PointerEvent') !== undefined; 1531 | var SUPPORT_ONLY_TOUCH = SUPPORT_TOUCH && MOBILE_REGEX.test(navigator.userAgent); 1532 | 1533 | var INPUT_TYPE_TOUCH = 'touch'; 1534 | var INPUT_TYPE_PEN = 'pen'; 1535 | var INPUT_TYPE_MOUSE = 'mouse'; 1536 | var INPUT_TYPE_KINECT = 'kinect'; 1537 | 1538 | var COMPUTE_INTERVAL = 25; 1539 | 1540 | var INPUT_START = 1; 1541 | var INPUT_MOVE = 2; 1542 | var INPUT_END = 4; 1543 | var INPUT_CANCEL = 8; 1544 | 1545 | var DIRECTION_NONE = 1; 1546 | var DIRECTION_LEFT = 2; 1547 | var DIRECTION_RIGHT = 4; 1548 | var DIRECTION_UP = 8; 1549 | var DIRECTION_DOWN = 16; 1550 | 1551 | var DIRECTION_HORIZONTAL = DIRECTION_LEFT | DIRECTION_RIGHT; 1552 | var DIRECTION_VERTICAL = DIRECTION_UP | DIRECTION_DOWN; 1553 | var DIRECTION_ALL = DIRECTION_HORIZONTAL | DIRECTION_VERTICAL; 1554 | 1555 | var PROPS_XY = ['x', 'y']; 1556 | var PROPS_CLIENT_XY = ['clientX', 'clientY']; 1557 | 1558 | /** 1559 | * create new input type manager 1560 | * @param {Manager} manager 1561 | * @param {Function} callback 1562 | * @returns {Input} 1563 | * @constructor 1564 | */ 1565 | function Input(manager, callback) { 1566 | var self = this; 1567 | this.manager = manager; 1568 | this.callback = callback; 1569 | this.element = manager.element; 1570 | this.target = manager.options.inputTarget; 1571 | 1572 | // smaller wrapper around the handler, for the scope and the enabled state of the manager, 1573 | // so when disabled the input events are completely bypassed. 1574 | this.domHandler = function(ev) { 1575 | if (boolOrFn(manager.options.enable, [manager])) { 1576 | self.handler(ev); 1577 | } 1578 | }; 1579 | 1580 | this.init(); 1581 | 1582 | } 1583 | 1584 | Input.prototype = { 1585 | /** 1586 | * should handle the inputEvent data and trigger the callback 1587 | * @virtual 1588 | */ 1589 | handler: function() { }, 1590 | 1591 | /** 1592 | * bind the events 1593 | */ 1594 | init: function() { 1595 | this.evEl && addEventListeners(this.element, this.evEl, this.domHandler); 1596 | this.evTarget && addEventListeners(this.target, this.evTarget, this.domHandler); 1597 | this.evWin && addEventListeners(getWindowForElement(this.element), this.evWin, this.domHandler); 1598 | }, 1599 | 1600 | /** 1601 | * unbind the events 1602 | */ 1603 | destroy: function() { 1604 | this.evEl && removeEventListeners(this.element, this.evEl, this.domHandler); 1605 | this.evTarget && removeEventListeners(this.target, this.evTarget, this.domHandler); 1606 | this.evWin && removeEventListeners(getWindowForElement(this.element), this.evWin, this.domHandler); 1607 | } 1608 | }; 1609 | 1610 | /** 1611 | * create new input type manager 1612 | * called by the Manager constructor 1613 | * @param {Hammer} manager 1614 | * @returns {Input} 1615 | */ 1616 | function createInputInstance(manager) { 1617 | var Type; 1618 | var inputClass = manager.options.inputClass; 1619 | 1620 | if (inputClass) { 1621 | Type = inputClass; 1622 | } else if (SUPPORT_POINTER_EVENTS) { 1623 | Type = PointerEventInput; 1624 | } else if (SUPPORT_ONLY_TOUCH) { 1625 | Type = TouchInput; 1626 | } else if (!SUPPORT_TOUCH) { 1627 | Type = MouseInput; 1628 | } else { 1629 | Type = TouchMouseInput; 1630 | } 1631 | return new (Type)(manager, inputHandler); 1632 | } 1633 | 1634 | /** 1635 | * handle input events 1636 | * @param {Manager} manager 1637 | * @param {String} eventType 1638 | * @param {Object} input 1639 | */ 1640 | function inputHandler(manager, eventType, input) { 1641 | var pointersLen = input.pointers.length; 1642 | var changedPointersLen = input.changedPointers.length; 1643 | var isFirst = (eventType & INPUT_START && (pointersLen - changedPointersLen === 0)); 1644 | var isFinal = (eventType & (INPUT_END | INPUT_CANCEL) && (pointersLen - changedPointersLen === 0)); 1645 | 1646 | input.isFirst = !!isFirst; 1647 | input.isFinal = !!isFinal; 1648 | 1649 | if (isFirst) { 1650 | manager.session = {}; 1651 | } 1652 | 1653 | // source event is the normalized value of the domEvents 1654 | // like 'touchstart, mouseup, pointerdown' 1655 | input.eventType = eventType; 1656 | 1657 | // compute scale, rotation etc 1658 | computeInputData(manager, input); 1659 | 1660 | // emit secret event 1661 | manager.emit('hammer.input', input); 1662 | 1663 | manager.recognize(input); 1664 | manager.session.prevInput = input; 1665 | } 1666 | 1667 | /** 1668 | * extend the data with some usable properties like scale, rotate, velocity etc 1669 | * @param {Object} manager 1670 | * @param {Object} input 1671 | */ 1672 | function computeInputData(manager, input) { 1673 | var session = manager.session; 1674 | var pointers = input.pointers; 1675 | var pointersLength = pointers.length; 1676 | 1677 | // store the first input to calculate the distance and direction 1678 | if (!session.firstInput) { 1679 | session.firstInput = simpleCloneInputData(input); 1680 | } 1681 | 1682 | // to compute scale and rotation we need to store the multiple touches 1683 | if (pointersLength > 1 && !session.firstMultiple) { 1684 | session.firstMultiple = simpleCloneInputData(input); 1685 | } else if (pointersLength === 1) { 1686 | session.firstMultiple = false; 1687 | } 1688 | 1689 | var firstInput = session.firstInput; 1690 | var firstMultiple = session.firstMultiple; 1691 | var offsetCenter = firstMultiple ? firstMultiple.center : firstInput.center; 1692 | 1693 | var center = input.center = getCenter(pointers); 1694 | input.timeStamp = now(); 1695 | input.deltaTime = input.timeStamp - firstInput.timeStamp; 1696 | 1697 | input.angle = getAngle(offsetCenter, center); 1698 | input.distance = getDistance(offsetCenter, center); 1699 | 1700 | computeDeltaXY(session, input); 1701 | input.offsetDirection = getDirection(input.deltaX, input.deltaY); 1702 | 1703 | var overallVelocity = getVelocity(input.deltaTime, input.deltaX, input.deltaY); 1704 | input.overallVelocityX = overallVelocity.x; 1705 | input.overallVelocityY = overallVelocity.y; 1706 | input.overallVelocity = (abs(overallVelocity.x) > abs(overallVelocity.y)) ? overallVelocity.x : overallVelocity.y; 1707 | 1708 | input.scale = firstMultiple ? getScale(firstMultiple.pointers, pointers) : 1; 1709 | input.rotation = firstMultiple ? getRotation(firstMultiple.pointers, pointers) : 0; 1710 | 1711 | input.maxPointers = !session.prevInput ? input.pointers.length : ((input.pointers.length > 1712 | session.prevInput.maxPointers) ? input.pointers.length : session.prevInput.maxPointers); 1713 | 1714 | computeIntervalInputData(session, input); 1715 | 1716 | // find the correct target 1717 | var target = manager.element; 1718 | if (hasParent(input.srcEvent.target, target)) { 1719 | target = input.srcEvent.target; 1720 | } 1721 | input.target = target; 1722 | } 1723 | 1724 | function computeDeltaXY(session, input) { 1725 | var center = input.center; 1726 | var offset = session.offsetDelta || {}; 1727 | var prevDelta = session.prevDelta || {}; 1728 | var prevInput = session.prevInput || {}; 1729 | 1730 | if (input.eventType === INPUT_START || prevInput.eventType === INPUT_END) { 1731 | prevDelta = session.prevDelta = { 1732 | x: prevInput.deltaX || 0, 1733 | y: prevInput.deltaY || 0 1734 | }; 1735 | 1736 | offset = session.offsetDelta = { 1737 | x: center.x, 1738 | y: center.y 1739 | }; 1740 | } 1741 | 1742 | input.deltaX = prevDelta.x + (center.x - offset.x); 1743 | input.deltaY = prevDelta.y + (center.y - offset.y); 1744 | } 1745 | 1746 | /** 1747 | * velocity is calculated every x ms 1748 | * @param {Object} session 1749 | * @param {Object} input 1750 | */ 1751 | function computeIntervalInputData(session, input) { 1752 | var last = session.lastInterval || input, 1753 | deltaTime = input.timeStamp - last.timeStamp, 1754 | velocity, velocityX, velocityY, direction; 1755 | 1756 | if (input.eventType != INPUT_CANCEL && (deltaTime > COMPUTE_INTERVAL || last.velocity === undefined)) { 1757 | var deltaX = input.deltaX - last.deltaX; 1758 | var deltaY = input.deltaY - last.deltaY; 1759 | 1760 | var v = getVelocity(deltaTime, deltaX, deltaY); 1761 | velocityX = v.x; 1762 | velocityY = v.y; 1763 | velocity = (abs(v.x) > abs(v.y)) ? v.x : v.y; 1764 | direction = getDirection(deltaX, deltaY); 1765 | 1766 | session.lastInterval = input; 1767 | } else { 1768 | // use latest velocity info if it doesn't overtake a minimum period 1769 | velocity = last.velocity; 1770 | velocityX = last.velocityX; 1771 | velocityY = last.velocityY; 1772 | direction = last.direction; 1773 | } 1774 | 1775 | input.velocity = velocity; 1776 | input.velocityX = velocityX; 1777 | input.velocityY = velocityY; 1778 | input.direction = direction; 1779 | } 1780 | 1781 | /** 1782 | * create a simple clone from the input used for storage of firstInput and firstMultiple 1783 | * @param {Object} input 1784 | * @returns {Object} clonedInputData 1785 | */ 1786 | function simpleCloneInputData(input) { 1787 | // make a simple copy of the pointers because we will get a reference if we don't 1788 | // we only need clientXY for the calculations 1789 | var pointers = []; 1790 | var i = 0; 1791 | while (i < input.pointers.length) { 1792 | pointers[i] = { 1793 | clientX: round(input.pointers[i].clientX), 1794 | clientY: round(input.pointers[i].clientY) 1795 | }; 1796 | i++; 1797 | } 1798 | 1799 | return { 1800 | timeStamp: now(), 1801 | pointers: pointers, 1802 | center: getCenter(pointers), 1803 | deltaX: input.deltaX, 1804 | deltaY: input.deltaY 1805 | }; 1806 | } 1807 | 1808 | /** 1809 | * get the center of all the pointers 1810 | * @param {Array} pointers 1811 | * @return {Object} center contains `x` and `y` properties 1812 | */ 1813 | function getCenter(pointers) { 1814 | var pointersLength = pointers.length; 1815 | 1816 | // no need to loop when only one touch 1817 | if (pointersLength === 1) { 1818 | return { 1819 | x: round(pointers[0].clientX), 1820 | y: round(pointers[0].clientY) 1821 | }; 1822 | } 1823 | 1824 | var x = 0, y = 0, i = 0; 1825 | while (i < pointersLength) { 1826 | x += pointers[i].clientX; 1827 | y += pointers[i].clientY; 1828 | i++; 1829 | } 1830 | 1831 | return { 1832 | x: round(x / pointersLength), 1833 | y: round(y / pointersLength) 1834 | }; 1835 | } 1836 | 1837 | /** 1838 | * calculate the velocity between two points. unit is in px per ms. 1839 | * @param {Number} deltaTime 1840 | * @param {Number} x 1841 | * @param {Number} y 1842 | * @return {Object} velocity `x` and `y` 1843 | */ 1844 | function getVelocity(deltaTime, x, y) { 1845 | return { 1846 | x: x / deltaTime || 0, 1847 | y: y / deltaTime || 0 1848 | }; 1849 | } 1850 | 1851 | /** 1852 | * get the direction between two points 1853 | * @param {Number} x 1854 | * @param {Number} y 1855 | * @return {Number} direction 1856 | */ 1857 | function getDirection(x, y) { 1858 | if (x === y) { 1859 | return DIRECTION_NONE; 1860 | } 1861 | 1862 | if (abs(x) >= abs(y)) { 1863 | return x < 0 ? DIRECTION_LEFT : DIRECTION_RIGHT; 1864 | } 1865 | return y < 0 ? DIRECTION_UP : DIRECTION_DOWN; 1866 | } 1867 | 1868 | /** 1869 | * calculate the absolute distance between two points 1870 | * @param {Object} p1 {x, y} 1871 | * @param {Object} p2 {x, y} 1872 | * @param {Array} [props] containing x and y keys 1873 | * @return {Number} distance 1874 | */ 1875 | function getDistance(p1, p2, props) { 1876 | if (!props) { 1877 | props = PROPS_XY; 1878 | } 1879 | var x = p2[props[0]] - p1[props[0]], 1880 | y = p2[props[1]] - p1[props[1]]; 1881 | 1882 | return Math.sqrt((x * x) + (y * y)); 1883 | } 1884 | 1885 | /** 1886 | * calculate the angle between two coordinates 1887 | * @param {Object} p1 1888 | * @param {Object} p2 1889 | * @param {Array} [props] containing x and y keys 1890 | * @return {Number} angle 1891 | */ 1892 | function getAngle(p1, p2, props) { 1893 | if (!props) { 1894 | props = PROPS_XY; 1895 | } 1896 | var x = p2[props[0]] - p1[props[0]], 1897 | y = p2[props[1]] - p1[props[1]]; 1898 | return Math.atan2(y, x) * 180 / Math.PI; 1899 | } 1900 | 1901 | /** 1902 | * calculate the rotation degrees between two pointersets 1903 | * @param {Array} start array of pointers 1904 | * @param {Array} end array of pointers 1905 | * @return {Number} rotation 1906 | */ 1907 | function getRotation(start, end) { 1908 | return getAngle(end[1], end[0], PROPS_CLIENT_XY) + getAngle(start[1], start[0], PROPS_CLIENT_XY); 1909 | } 1910 | 1911 | /** 1912 | * calculate the scale factor between two pointersets 1913 | * no scale is 1, and goes down to 0 when pinched together, and bigger when pinched out 1914 | * @param {Array} start array of pointers 1915 | * @param {Array} end array of pointers 1916 | * @return {Number} scale 1917 | */ 1918 | function getScale(start, end) { 1919 | return getDistance(end[0], end[1], PROPS_CLIENT_XY) / getDistance(start[0], start[1], PROPS_CLIENT_XY); 1920 | } 1921 | 1922 | var MOUSE_INPUT_MAP = { 1923 | mousedown: INPUT_START, 1924 | mousemove: INPUT_MOVE, 1925 | mouseup: INPUT_END 1926 | }; 1927 | 1928 | var MOUSE_ELEMENT_EVENTS = 'mousedown'; 1929 | var MOUSE_WINDOW_EVENTS = 'mousemove mouseup'; 1930 | 1931 | /** 1932 | * Mouse events input 1933 | * @constructor 1934 | * @extends Input 1935 | */ 1936 | function MouseInput() { 1937 | this.evEl = MOUSE_ELEMENT_EVENTS; 1938 | this.evWin = MOUSE_WINDOW_EVENTS; 1939 | 1940 | this.pressed = false; // mousedown state 1941 | 1942 | Input.apply(this, arguments); 1943 | } 1944 | 1945 | inherit(MouseInput, Input, { 1946 | /** 1947 | * handle mouse events 1948 | * @param {Object} ev 1949 | */ 1950 | handler: function MEhandler(ev) { 1951 | var eventType = MOUSE_INPUT_MAP[ev.type]; 1952 | 1953 | // on start we want to have the left mouse button down 1954 | if (eventType & INPUT_START && ev.button === 0) { 1955 | this.pressed = true; 1956 | } 1957 | 1958 | if (eventType & INPUT_MOVE && ev.which !== 1) { 1959 | eventType = INPUT_END; 1960 | } 1961 | 1962 | // mouse must be down 1963 | if (!this.pressed) { 1964 | return; 1965 | } 1966 | 1967 | if (eventType & INPUT_END) { 1968 | this.pressed = false; 1969 | } 1970 | 1971 | this.callback(this.manager, eventType, { 1972 | pointers: [ev], 1973 | changedPointers: [ev], 1974 | pointerType: INPUT_TYPE_MOUSE, 1975 | srcEvent: ev 1976 | }); 1977 | } 1978 | }); 1979 | 1980 | var POINTER_INPUT_MAP = { 1981 | pointerdown: INPUT_START, 1982 | pointermove: INPUT_MOVE, 1983 | pointerup: INPUT_END, 1984 | pointercancel: INPUT_CANCEL, 1985 | pointerout: INPUT_CANCEL 1986 | }; 1987 | 1988 | // in IE10 the pointer types is defined as an enum 1989 | var IE10_POINTER_TYPE_ENUM = { 1990 | 2: INPUT_TYPE_TOUCH, 1991 | 3: INPUT_TYPE_PEN, 1992 | 4: INPUT_TYPE_MOUSE, 1993 | 5: INPUT_TYPE_KINECT // see https://twitter.com/jacobrossi/status/480596438489890816 1994 | }; 1995 | 1996 | var POINTER_ELEMENT_EVENTS = 'pointerdown'; 1997 | var POINTER_WINDOW_EVENTS = 'pointermove pointerup pointercancel'; 1998 | 1999 | // IE10 has prefixed support, and case-sensitive 2000 | if (window.MSPointerEvent && !window.PointerEvent) { 2001 | POINTER_ELEMENT_EVENTS = 'MSPointerDown'; 2002 | POINTER_WINDOW_EVENTS = 'MSPointerMove MSPointerUp MSPointerCancel'; 2003 | } 2004 | 2005 | /** 2006 | * Pointer events input 2007 | * @constructor 2008 | * @extends Input 2009 | */ 2010 | function PointerEventInput() { 2011 | this.evEl = POINTER_ELEMENT_EVENTS; 2012 | this.evWin = POINTER_WINDOW_EVENTS; 2013 | 2014 | Input.apply(this, arguments); 2015 | 2016 | this.store = (this.manager.session.pointerEvents = []); 2017 | } 2018 | 2019 | inherit(PointerEventInput, Input, { 2020 | /** 2021 | * handle mouse events 2022 | * @param {Object} ev 2023 | */ 2024 | handler: function PEhandler(ev) { 2025 | var store = this.store; 2026 | var removePointer = false; 2027 | 2028 | var eventTypeNormalized = ev.type.toLowerCase().replace('ms', ''); 2029 | var eventType = POINTER_INPUT_MAP[eventTypeNormalized]; 2030 | var pointerType = IE10_POINTER_TYPE_ENUM[ev.pointerType] || ev.pointerType; 2031 | 2032 | var isTouch = (pointerType == INPUT_TYPE_TOUCH); 2033 | 2034 | // get index of the event in the store 2035 | var storeIndex = inArray(store, ev.pointerId, 'pointerId'); 2036 | 2037 | // start and mouse must be down 2038 | if (eventType & INPUT_START && (ev.button === 0 || isTouch)) { 2039 | if (storeIndex < 0) { 2040 | store.push(ev); 2041 | storeIndex = store.length - 1; 2042 | } 2043 | } else if (eventType & (INPUT_END | INPUT_CANCEL)) { 2044 | removePointer = true; 2045 | } 2046 | 2047 | // it not found, so the pointer hasn't been down (so it's probably a hover) 2048 | if (storeIndex < 0) { 2049 | return; 2050 | } 2051 | 2052 | // update the event in the store 2053 | store[storeIndex] = ev; 2054 | 2055 | this.callback(this.manager, eventType, { 2056 | pointers: store, 2057 | changedPointers: [ev], 2058 | pointerType: pointerType, 2059 | srcEvent: ev 2060 | }); 2061 | 2062 | if (removePointer) { 2063 | // remove from the store 2064 | store.splice(storeIndex, 1); 2065 | } 2066 | } 2067 | }); 2068 | 2069 | var SINGLE_TOUCH_INPUT_MAP = { 2070 | touchstart: INPUT_START, 2071 | touchmove: INPUT_MOVE, 2072 | touchend: INPUT_END, 2073 | touchcancel: INPUT_CANCEL 2074 | }; 2075 | 2076 | var SINGLE_TOUCH_TARGET_EVENTS = 'touchstart'; 2077 | var SINGLE_TOUCH_WINDOW_EVENTS = 'touchstart touchmove touchend touchcancel'; 2078 | 2079 | /** 2080 | * Touch events input 2081 | * @constructor 2082 | * @extends Input 2083 | */ 2084 | function SingleTouchInput() { 2085 | this.evTarget = SINGLE_TOUCH_TARGET_EVENTS; 2086 | this.evWin = SINGLE_TOUCH_WINDOW_EVENTS; 2087 | this.started = false; 2088 | 2089 | Input.apply(this, arguments); 2090 | } 2091 | 2092 | inherit(SingleTouchInput, Input, { 2093 | handler: function TEhandler(ev) { 2094 | var type = SINGLE_TOUCH_INPUT_MAP[ev.type]; 2095 | 2096 | // should we handle the touch events? 2097 | if (type === INPUT_START) { 2098 | this.started = true; 2099 | } 2100 | 2101 | if (!this.started) { 2102 | return; 2103 | } 2104 | 2105 | var touches = normalizeSingleTouches.call(this, ev, type); 2106 | 2107 | // when done, reset the started state 2108 | if (type & (INPUT_END | INPUT_CANCEL) && touches[0].length - touches[1].length === 0) { 2109 | this.started = false; 2110 | } 2111 | 2112 | this.callback(this.manager, type, { 2113 | pointers: touches[0], 2114 | changedPointers: touches[1], 2115 | pointerType: INPUT_TYPE_TOUCH, 2116 | srcEvent: ev 2117 | }); 2118 | } 2119 | }); 2120 | 2121 | /** 2122 | * @this {TouchInput} 2123 | * @param {Object} ev 2124 | * @param {Number} type flag 2125 | * @returns {undefined|Array} [all, changed] 2126 | */ 2127 | function normalizeSingleTouches(ev, type) { 2128 | var all = toArray(ev.touches); 2129 | var changed = toArray(ev.changedTouches); 2130 | 2131 | if (type & (INPUT_END | INPUT_CANCEL)) { 2132 | all = uniqueArray(all.concat(changed), 'identifier', true); 2133 | } 2134 | 2135 | return [all, changed]; 2136 | } 2137 | 2138 | var TOUCH_INPUT_MAP = { 2139 | touchstart: INPUT_START, 2140 | touchmove: INPUT_MOVE, 2141 | touchend: INPUT_END, 2142 | touchcancel: INPUT_CANCEL 2143 | }; 2144 | 2145 | var TOUCH_TARGET_EVENTS = 'touchstart touchmove touchend touchcancel'; 2146 | 2147 | /** 2148 | * Multi-user touch events input 2149 | * @constructor 2150 | * @extends Input 2151 | */ 2152 | function TouchInput() { 2153 | this.evTarget = TOUCH_TARGET_EVENTS; 2154 | this.targetIds = {}; 2155 | 2156 | Input.apply(this, arguments); 2157 | } 2158 | 2159 | inherit(TouchInput, Input, { 2160 | handler: function MTEhandler(ev) { 2161 | var type = TOUCH_INPUT_MAP[ev.type]; 2162 | var touches = getTouches.call(this, ev, type); 2163 | if (!touches) { 2164 | return; 2165 | } 2166 | 2167 | this.callback(this.manager, type, { 2168 | pointers: touches[0], 2169 | changedPointers: touches[1], 2170 | pointerType: INPUT_TYPE_TOUCH, 2171 | srcEvent: ev 2172 | }); 2173 | } 2174 | }); 2175 | 2176 | /** 2177 | * @this {TouchInput} 2178 | * @param {Object} ev 2179 | * @param {Number} type flag 2180 | * @returns {undefined|Array} [all, changed] 2181 | */ 2182 | function getTouches(ev, type) { 2183 | var allTouches = toArray(ev.touches); 2184 | var targetIds = this.targetIds; 2185 | 2186 | // when there is only one touch, the process can be simplified 2187 | if (type & (INPUT_START | INPUT_MOVE) && allTouches.length === 1) { 2188 | targetIds[allTouches[0].identifier] = true; 2189 | return [allTouches, allTouches]; 2190 | } 2191 | 2192 | var i, 2193 | targetTouches, 2194 | changedTouches = toArray(ev.changedTouches), 2195 | changedTargetTouches = [], 2196 | target = this.target; 2197 | 2198 | // get target touches from touches 2199 | targetTouches = allTouches.filter(function(touch) { 2200 | return hasParent(touch.target, target); 2201 | }); 2202 | 2203 | // collect touches 2204 | if (type === INPUT_START) { 2205 | i = 0; 2206 | while (i < targetTouches.length) { 2207 | targetIds[targetTouches[i].identifier] = true; 2208 | i++; 2209 | } 2210 | } 2211 | 2212 | // filter changed touches to only contain touches that exist in the collected target ids 2213 | i = 0; 2214 | while (i < changedTouches.length) { 2215 | if (targetIds[changedTouches[i].identifier]) { 2216 | changedTargetTouches.push(changedTouches[i]); 2217 | } 2218 | 2219 | // cleanup removed touches 2220 | if (type & (INPUT_END | INPUT_CANCEL)) { 2221 | delete targetIds[changedTouches[i].identifier]; 2222 | } 2223 | i++; 2224 | } 2225 | 2226 | if (!changedTargetTouches.length) { 2227 | return; 2228 | } 2229 | 2230 | return [ 2231 | // merge targetTouches with changedTargetTouches so it contains ALL touches, including 'end' and 'cancel' 2232 | uniqueArray(targetTouches.concat(changedTargetTouches), 'identifier', true), 2233 | changedTargetTouches 2234 | ]; 2235 | } 2236 | 2237 | /** 2238 | * Combined touch and mouse input 2239 | * 2240 | * Touch has a higher priority then mouse, and while touching no mouse events are allowed. 2241 | * This because touch devices also emit mouse events while doing a touch. 2242 | * 2243 | * @constructor 2244 | * @extends Input 2245 | */ 2246 | 2247 | var DEDUP_TIMEOUT = 2500; 2248 | var DEDUP_DISTANCE = 25; 2249 | 2250 | function TouchMouseInput() { 2251 | Input.apply(this, arguments); 2252 | 2253 | var handler = bindFn(this.handler, this); 2254 | this.touch = new TouchInput(this.manager, handler); 2255 | this.mouse = new MouseInput(this.manager, handler); 2256 | 2257 | this.primaryTouch = null; 2258 | this.lastTouches = []; 2259 | } 2260 | 2261 | inherit(TouchMouseInput, Input, { 2262 | /** 2263 | * handle mouse and touch events 2264 | * @param {Hammer} manager 2265 | * @param {String} inputEvent 2266 | * @param {Object} inputData 2267 | */ 2268 | handler: function TMEhandler(manager, inputEvent, inputData) { 2269 | var isTouch = (inputData.pointerType == INPUT_TYPE_TOUCH), 2270 | isMouse = (inputData.pointerType == INPUT_TYPE_MOUSE); 2271 | 2272 | if (isMouse && inputData.sourceCapabilities && inputData.sourceCapabilities.firesTouchEvents) { 2273 | return; 2274 | } 2275 | 2276 | // when we're in a touch event, record touches to de-dupe synthetic mouse event 2277 | if (isTouch) { 2278 | recordTouches.call(this, inputEvent, inputData); 2279 | } else if (isMouse && isSyntheticEvent.call(this, inputData)) { 2280 | return; 2281 | } 2282 | 2283 | this.callback(manager, inputEvent, inputData); 2284 | }, 2285 | 2286 | /** 2287 | * remove the event listeners 2288 | */ 2289 | destroy: function destroy() { 2290 | this.touch.destroy(); 2291 | this.mouse.destroy(); 2292 | } 2293 | }); 2294 | 2295 | function recordTouches(eventType, eventData) { 2296 | if (eventType & INPUT_START) { 2297 | this.primaryTouch = eventData.changedPointers[0].identifier; 2298 | setLastTouch.call(this, eventData); 2299 | } else if (eventType & (INPUT_END | INPUT_CANCEL)) { 2300 | setLastTouch.call(this, eventData); 2301 | } 2302 | } 2303 | 2304 | function setLastTouch(eventData) { 2305 | var touch = eventData.changedPointers[0]; 2306 | 2307 | if (touch.identifier === this.primaryTouch) { 2308 | var lastTouch = {x: touch.clientX, y: touch.clientY}; 2309 | this.lastTouches.push(lastTouch); 2310 | var lts = this.lastTouches; 2311 | var removeLastTouch = function() { 2312 | var i = lts.indexOf(lastTouch); 2313 | if (i > -1) { 2314 | lts.splice(i, 1); 2315 | } 2316 | }; 2317 | setTimeout(removeLastTouch, DEDUP_TIMEOUT); 2318 | } 2319 | } 2320 | 2321 | function isSyntheticEvent(eventData) { 2322 | var x = eventData.srcEvent.clientX, y = eventData.srcEvent.clientY; 2323 | for (var i = 0; i < this.lastTouches.length; i++) { 2324 | var t = this.lastTouches[i]; 2325 | var dx = Math.abs(x - t.x), dy = Math.abs(y - t.y); 2326 | if (dx <= DEDUP_DISTANCE && dy <= DEDUP_DISTANCE) { 2327 | return true; 2328 | } 2329 | } 2330 | return false; 2331 | } 2332 | 2333 | var PREFIXED_TOUCH_ACTION = prefixed(TEST_ELEMENT.style, 'touchAction'); 2334 | var NATIVE_TOUCH_ACTION = PREFIXED_TOUCH_ACTION !== undefined; 2335 | 2336 | // magical touchAction value 2337 | var TOUCH_ACTION_COMPUTE = 'compute'; 2338 | var TOUCH_ACTION_AUTO = 'auto'; 2339 | var TOUCH_ACTION_MANIPULATION = 'manipulation'; // not implemented 2340 | var TOUCH_ACTION_NONE = 'none'; 2341 | var TOUCH_ACTION_PAN_X = 'pan-x'; 2342 | var TOUCH_ACTION_PAN_Y = 'pan-y'; 2343 | var TOUCH_ACTION_MAP = getTouchActionProps(); 2344 | 2345 | /** 2346 | * Touch Action 2347 | * sets the touchAction property or uses the js alternative 2348 | * @param {Manager} manager 2349 | * @param {String} value 2350 | * @constructor 2351 | */ 2352 | function TouchAction(manager, value) { 2353 | this.manager = manager; 2354 | this.set(value); 2355 | } 2356 | 2357 | TouchAction.prototype = { 2358 | /** 2359 | * set the touchAction value on the element or enable the polyfill 2360 | * @param {String} value 2361 | */ 2362 | set: function(value) { 2363 | // find out the touch-action by the event handlers 2364 | if (value == TOUCH_ACTION_COMPUTE) { 2365 | value = this.compute(); 2366 | } 2367 | 2368 | if (NATIVE_TOUCH_ACTION && this.manager.element.style && TOUCH_ACTION_MAP[value]) { 2369 | this.manager.element.style[PREFIXED_TOUCH_ACTION] = value; 2370 | } 2371 | this.actions = value.toLowerCase().trim(); 2372 | }, 2373 | 2374 | /** 2375 | * just re-set the touchAction value 2376 | */ 2377 | update: function() { 2378 | this.set(this.manager.options.touchAction); 2379 | }, 2380 | 2381 | /** 2382 | * compute the value for the touchAction property based on the recognizer's settings 2383 | * @returns {String} value 2384 | */ 2385 | compute: function() { 2386 | var actions = []; 2387 | each(this.manager.recognizers, function(recognizer) { 2388 | if (boolOrFn(recognizer.options.enable, [recognizer])) { 2389 | actions = actions.concat(recognizer.getTouchAction()); 2390 | } 2391 | }); 2392 | return cleanTouchActions(actions.join(' ')); 2393 | }, 2394 | 2395 | /** 2396 | * this method is called on each input cycle and provides the preventing of the browser behavior 2397 | * @param {Object} input 2398 | */ 2399 | preventDefaults: function(input) { 2400 | var srcEvent = input.srcEvent; 2401 | var direction = input.offsetDirection; 2402 | 2403 | // if the touch action did prevented once this session 2404 | if (this.manager.session.prevented) { 2405 | srcEvent.preventDefault(); 2406 | return; 2407 | } 2408 | 2409 | var actions = this.actions; 2410 | var hasNone = inStr(actions, TOUCH_ACTION_NONE) && !TOUCH_ACTION_MAP[TOUCH_ACTION_NONE]; 2411 | var hasPanY = inStr(actions, TOUCH_ACTION_PAN_Y) && !TOUCH_ACTION_MAP[TOUCH_ACTION_PAN_Y]; 2412 | var hasPanX = inStr(actions, TOUCH_ACTION_PAN_X) && !TOUCH_ACTION_MAP[TOUCH_ACTION_PAN_X]; 2413 | 2414 | if (hasNone) { 2415 | //do not prevent defaults if this is a tap gesture 2416 | 2417 | var isTapPointer = input.pointers.length === 1; 2418 | var isTapMovement = input.distance < 2; 2419 | var isTapTouchTime = input.deltaTime < 250; 2420 | 2421 | if (isTapPointer && isTapMovement && isTapTouchTime) { 2422 | return; 2423 | } 2424 | } 2425 | 2426 | if (hasPanX && hasPanY) { 2427 | // `pan-x pan-y` means browser handles all scrolling/panning, do not prevent 2428 | return; 2429 | } 2430 | 2431 | if (hasNone || 2432 | (hasPanY && direction & DIRECTION_HORIZONTAL) || 2433 | (hasPanX && direction & DIRECTION_VERTICAL)) { 2434 | return this.preventSrc(srcEvent); 2435 | } 2436 | }, 2437 | 2438 | /** 2439 | * call preventDefault to prevent the browser's default behavior (scrolling in most cases) 2440 | * @param {Object} srcEvent 2441 | */ 2442 | preventSrc: function(srcEvent) { 2443 | this.manager.session.prevented = true; 2444 | srcEvent.preventDefault(); 2445 | } 2446 | }; 2447 | 2448 | /** 2449 | * when the touchActions are collected they are not a valid value, so we need to clean things up. * 2450 | * @param {String} actions 2451 | * @returns {*} 2452 | */ 2453 | function cleanTouchActions(actions) { 2454 | // none 2455 | if (inStr(actions, TOUCH_ACTION_NONE)) { 2456 | return TOUCH_ACTION_NONE; 2457 | } 2458 | 2459 | var hasPanX = inStr(actions, TOUCH_ACTION_PAN_X); 2460 | var hasPanY = inStr(actions, TOUCH_ACTION_PAN_Y); 2461 | 2462 | // if both pan-x and pan-y are set (different recognizers 2463 | // for different directions, e.g. horizontal pan but vertical swipe?) 2464 | // we need none (as otherwise with pan-x pan-y combined none of these 2465 | // recognizers will work, since the browser would handle all panning 2466 | if (hasPanX && hasPanY) { 2467 | return TOUCH_ACTION_NONE; 2468 | } 2469 | 2470 | // pan-x OR pan-y 2471 | if (hasPanX || hasPanY) { 2472 | return hasPanX ? TOUCH_ACTION_PAN_X : TOUCH_ACTION_PAN_Y; 2473 | } 2474 | 2475 | // manipulation 2476 | if (inStr(actions, TOUCH_ACTION_MANIPULATION)) { 2477 | return TOUCH_ACTION_MANIPULATION; 2478 | } 2479 | 2480 | return TOUCH_ACTION_AUTO; 2481 | } 2482 | 2483 | function getTouchActionProps() { 2484 | if (!NATIVE_TOUCH_ACTION) { 2485 | return false; 2486 | } 2487 | var touchMap = {}; 2488 | var cssSupports = window.CSS && window.CSS.supports; 2489 | ['auto', 'manipulation', 'pan-y', 'pan-x', 'pan-x pan-y', 'none'].forEach(function(val) { 2490 | 2491 | // If css.supports is not supported but there is native touch-action assume it supports 2492 | // all values. This is the case for IE 10 and 11. 2493 | touchMap[val] = cssSupports ? window.CSS.supports('touch-action', val) : true; 2494 | }); 2495 | return touchMap; 2496 | } 2497 | 2498 | /** 2499 | * Recognizer flow explained; * 2500 | * All recognizers have the initial state of POSSIBLE when a input session starts. 2501 | * The definition of a input session is from the first input until the last input, with all it's movement in it. * 2502 | * Example session for mouse-input: mousedown -> mousemove -> mouseup 2503 | * 2504 | * On each recognizing cycle (see Manager.recognize) the .recognize() method is executed 2505 | * which determines with state it should be. 2506 | * 2507 | * If the recognizer has the state FAILED, CANCELLED or RECOGNIZED (equals ENDED), it is reset to 2508 | * POSSIBLE to give it another change on the next cycle. 2509 | * 2510 | * Possible 2511 | * | 2512 | * +-----+---------------+ 2513 | * | | 2514 | * +-----+-----+ | 2515 | * | | | 2516 | * Failed Cancelled | 2517 | * +-------+------+ 2518 | * | | 2519 | * Recognized Began 2520 | * | 2521 | * Changed 2522 | * | 2523 | * Ended/Recognized 2524 | */ 2525 | var STATE_POSSIBLE = 1; 2526 | var STATE_BEGAN = 2; 2527 | var STATE_CHANGED = 4; 2528 | var STATE_ENDED = 8; 2529 | var STATE_RECOGNIZED = STATE_ENDED; 2530 | var STATE_CANCELLED = 16; 2531 | var STATE_FAILED = 32; 2532 | 2533 | /** 2534 | * Recognizer 2535 | * Every recognizer needs to extend from this class. 2536 | * @constructor 2537 | * @param {Object} options 2538 | */ 2539 | function Recognizer(options) { 2540 | this.options = assign({}, this.defaults, options || {}); 2541 | 2542 | this.id = uniqueId(); 2543 | 2544 | this.manager = null; 2545 | 2546 | // default is enable true 2547 | this.options.enable = ifUndefined(this.options.enable, true); 2548 | 2549 | this.state = STATE_POSSIBLE; 2550 | 2551 | this.simultaneous = {}; 2552 | this.requireFail = []; 2553 | } 2554 | 2555 | Recognizer.prototype = { 2556 | /** 2557 | * @virtual 2558 | * @type {Object} 2559 | */ 2560 | defaults: {}, 2561 | 2562 | /** 2563 | * set options 2564 | * @param {Object} options 2565 | * @return {Recognizer} 2566 | */ 2567 | set: function(options) { 2568 | assign(this.options, options); 2569 | 2570 | // also update the touchAction, in case something changed about the directions/enabled state 2571 | this.manager && this.manager.touchAction.update(); 2572 | return this; 2573 | }, 2574 | 2575 | /** 2576 | * recognize simultaneous with an other recognizer. 2577 | * @param {Recognizer} otherRecognizer 2578 | * @returns {Recognizer} this 2579 | */ 2580 | recognizeWith: function(otherRecognizer) { 2581 | if (invokeArrayArg(otherRecognizer, 'recognizeWith', this)) { 2582 | return this; 2583 | } 2584 | 2585 | var simultaneous = this.simultaneous; 2586 | otherRecognizer = getRecognizerByNameIfManager(otherRecognizer, this); 2587 | if (!simultaneous[otherRecognizer.id]) { 2588 | simultaneous[otherRecognizer.id] = otherRecognizer; 2589 | otherRecognizer.recognizeWith(this); 2590 | } 2591 | return this; 2592 | }, 2593 | 2594 | /** 2595 | * drop the simultaneous link. it doesnt remove the link on the other recognizer. 2596 | * @param {Recognizer} otherRecognizer 2597 | * @returns {Recognizer} this 2598 | */ 2599 | dropRecognizeWith: function(otherRecognizer) { 2600 | if (invokeArrayArg(otherRecognizer, 'dropRecognizeWith', this)) { 2601 | return this; 2602 | } 2603 | 2604 | otherRecognizer = getRecognizerByNameIfManager(otherRecognizer, this); 2605 | delete this.simultaneous[otherRecognizer.id]; 2606 | return this; 2607 | }, 2608 | 2609 | /** 2610 | * recognizer can only run when an other is failing 2611 | * @param {Recognizer} otherRecognizer 2612 | * @returns {Recognizer} this 2613 | */ 2614 | requireFailure: function(otherRecognizer) { 2615 | if (invokeArrayArg(otherRecognizer, 'requireFailure', this)) { 2616 | return this; 2617 | } 2618 | 2619 | var requireFail = this.requireFail; 2620 | otherRecognizer = getRecognizerByNameIfManager(otherRecognizer, this); 2621 | if (inArray(requireFail, otherRecognizer) === -1) { 2622 | requireFail.push(otherRecognizer); 2623 | otherRecognizer.requireFailure(this); 2624 | } 2625 | return this; 2626 | }, 2627 | 2628 | /** 2629 | * drop the requireFailure link. it does not remove the link on the other recognizer. 2630 | * @param {Recognizer} otherRecognizer 2631 | * @returns {Recognizer} this 2632 | */ 2633 | dropRequireFailure: function(otherRecognizer) { 2634 | if (invokeArrayArg(otherRecognizer, 'dropRequireFailure', this)) { 2635 | return this; 2636 | } 2637 | 2638 | otherRecognizer = getRecognizerByNameIfManager(otherRecognizer, this); 2639 | var index = inArray(this.requireFail, otherRecognizer); 2640 | if (index > -1) { 2641 | this.requireFail.splice(index, 1); 2642 | } 2643 | return this; 2644 | }, 2645 | 2646 | /** 2647 | * has require failures boolean 2648 | * @returns {boolean} 2649 | */ 2650 | hasRequireFailures: function() { 2651 | return this.requireFail.length > 0; 2652 | }, 2653 | 2654 | /** 2655 | * if the recognizer can recognize simultaneous with an other recognizer 2656 | * @param {Recognizer} otherRecognizer 2657 | * @returns {Boolean} 2658 | */ 2659 | canRecognizeWith: function(otherRecognizer) { 2660 | return !!this.simultaneous[otherRecognizer.id]; 2661 | }, 2662 | 2663 | /** 2664 | * You should use `tryEmit` instead of `emit` directly to check 2665 | * that all the needed recognizers has failed before emitting. 2666 | * @param {Object} input 2667 | */ 2668 | emit: function(input) { 2669 | var self = this; 2670 | var state = this.state; 2671 | 2672 | function emit(event) { 2673 | self.manager.emit(event, input); 2674 | } 2675 | 2676 | // 'panstart' and 'panmove' 2677 | if (state < STATE_ENDED) { 2678 | emit(self.options.event + stateStr(state)); 2679 | } 2680 | 2681 | emit(self.options.event); // simple 'eventName' events 2682 | 2683 | if (input.additionalEvent) { // additional event(panleft, panright, pinchin, pinchout...) 2684 | emit(input.additionalEvent); 2685 | } 2686 | 2687 | // panend and pancancel 2688 | if (state >= STATE_ENDED) { 2689 | emit(self.options.event + stateStr(state)); 2690 | } 2691 | }, 2692 | 2693 | /** 2694 | * Check that all the require failure recognizers has failed, 2695 | * if true, it emits a gesture event, 2696 | * otherwise, setup the state to FAILED. 2697 | * @param {Object} input 2698 | */ 2699 | tryEmit: function(input) { 2700 | if (this.canEmit()) { 2701 | return this.emit(input); 2702 | } 2703 | // it's failing anyway 2704 | this.state = STATE_FAILED; 2705 | }, 2706 | 2707 | /** 2708 | * can we emit? 2709 | * @returns {boolean} 2710 | */ 2711 | canEmit: function() { 2712 | var i = 0; 2713 | while (i < this.requireFail.length) { 2714 | if (!(this.requireFail[i].state & (STATE_FAILED | STATE_POSSIBLE))) { 2715 | return false; 2716 | } 2717 | i++; 2718 | } 2719 | return true; 2720 | }, 2721 | 2722 | /** 2723 | * update the recognizer 2724 | * @param {Object} inputData 2725 | */ 2726 | recognize: function(inputData) { 2727 | // make a new copy of the inputData 2728 | // so we can change the inputData without messing up the other recognizers 2729 | var inputDataClone = assign({}, inputData); 2730 | 2731 | // is is enabled and allow recognizing? 2732 | if (!boolOrFn(this.options.enable, [this, inputDataClone])) { 2733 | this.reset(); 2734 | this.state = STATE_FAILED; 2735 | return; 2736 | } 2737 | 2738 | // reset when we've reached the end 2739 | if (this.state & (STATE_RECOGNIZED | STATE_CANCELLED | STATE_FAILED)) { 2740 | this.state = STATE_POSSIBLE; 2741 | } 2742 | 2743 | this.state = this.process(inputDataClone); 2744 | 2745 | // the recognizer has recognized a gesture 2746 | // so trigger an event 2747 | if (this.state & (STATE_BEGAN | STATE_CHANGED | STATE_ENDED | STATE_CANCELLED)) { 2748 | this.tryEmit(inputDataClone); 2749 | } 2750 | }, 2751 | 2752 | /** 2753 | * return the state of the recognizer 2754 | * the actual recognizing happens in this method 2755 | * @virtual 2756 | * @param {Object} inputData 2757 | * @returns {Const} STATE 2758 | */ 2759 | process: function(inputData) { }, // jshint ignore:line 2760 | 2761 | /** 2762 | * return the preferred touch-action 2763 | * @virtual 2764 | * @returns {Array} 2765 | */ 2766 | getTouchAction: function() { }, 2767 | 2768 | /** 2769 | * called when the gesture isn't allowed to recognize 2770 | * like when another is being recognized or it is disabled 2771 | * @virtual 2772 | */ 2773 | reset: function() { } 2774 | }; 2775 | 2776 | /** 2777 | * get a usable string, used as event postfix 2778 | * @param {Const} state 2779 | * @returns {String} state 2780 | */ 2781 | function stateStr(state) { 2782 | if (state & STATE_CANCELLED) { 2783 | return 'cancel'; 2784 | } else if (state & STATE_ENDED) { 2785 | return 'end'; 2786 | } else if (state & STATE_CHANGED) { 2787 | return 'move'; 2788 | } else if (state & STATE_BEGAN) { 2789 | return 'start'; 2790 | } 2791 | return ''; 2792 | } 2793 | 2794 | /** 2795 | * direction cons to string 2796 | * @param {Const} direction 2797 | * @returns {String} 2798 | */ 2799 | function directionStr(direction) { 2800 | if (direction == DIRECTION_DOWN) { 2801 | return 'down'; 2802 | } else if (direction == DIRECTION_UP) { 2803 | return 'up'; 2804 | } else if (direction == DIRECTION_LEFT) { 2805 | return 'left'; 2806 | } else if (direction == DIRECTION_RIGHT) { 2807 | return 'right'; 2808 | } 2809 | return ''; 2810 | } 2811 | 2812 | /** 2813 | * get a recognizer by name if it is bound to a manager 2814 | * @param {Recognizer|String} otherRecognizer 2815 | * @param {Recognizer} recognizer 2816 | * @returns {Recognizer} 2817 | */ 2818 | function getRecognizerByNameIfManager(otherRecognizer, recognizer) { 2819 | var manager = recognizer.manager; 2820 | if (manager) { 2821 | return manager.get(otherRecognizer); 2822 | } 2823 | return otherRecognizer; 2824 | } 2825 | 2826 | /** 2827 | * This recognizer is just used as a base for the simple attribute recognizers. 2828 | * @constructor 2829 | * @extends Recognizer 2830 | */ 2831 | function AttrRecognizer() { 2832 | Recognizer.apply(this, arguments); 2833 | } 2834 | 2835 | inherit(AttrRecognizer, Recognizer, { 2836 | /** 2837 | * @namespace 2838 | * @memberof AttrRecognizer 2839 | */ 2840 | defaults: { 2841 | /** 2842 | * @type {Number} 2843 | * @default 1 2844 | */ 2845 | pointers: 1 2846 | }, 2847 | 2848 | /** 2849 | * Used to check if it the recognizer receives valid input, like input.distance > 10. 2850 | * @memberof AttrRecognizer 2851 | * @param {Object} input 2852 | * @returns {Boolean} recognized 2853 | */ 2854 | attrTest: function(input) { 2855 | var optionPointers = this.options.pointers; 2856 | return optionPointers === 0 || input.pointers.length === optionPointers; 2857 | }, 2858 | 2859 | /** 2860 | * Process the input and return the state for the recognizer 2861 | * @memberof AttrRecognizer 2862 | * @param {Object} input 2863 | * @returns {*} State 2864 | */ 2865 | process: function(input) { 2866 | var state = this.state; 2867 | var eventType = input.eventType; 2868 | 2869 | var isRecognized = state & (STATE_BEGAN | STATE_CHANGED); 2870 | var isValid = this.attrTest(input); 2871 | 2872 | // on cancel input and we've recognized before, return STATE_CANCELLED 2873 | if (isRecognized && (eventType & INPUT_CANCEL || !isValid)) { 2874 | return state | STATE_CANCELLED; 2875 | } else if (isRecognized || isValid) { 2876 | if (eventType & INPUT_END) { 2877 | return state | STATE_ENDED; 2878 | } else if (!(state & STATE_BEGAN)) { 2879 | return STATE_BEGAN; 2880 | } 2881 | return state | STATE_CHANGED; 2882 | } 2883 | return STATE_FAILED; 2884 | } 2885 | }); 2886 | 2887 | /** 2888 | * Pan 2889 | * Recognized when the pointer is down and moved in the allowed direction. 2890 | * @constructor 2891 | * @extends AttrRecognizer 2892 | */ 2893 | function PanRecognizer() { 2894 | AttrRecognizer.apply(this, arguments); 2895 | 2896 | this.pX = null; 2897 | this.pY = null; 2898 | } 2899 | 2900 | inherit(PanRecognizer, AttrRecognizer, { 2901 | /** 2902 | * @namespace 2903 | * @memberof PanRecognizer 2904 | */ 2905 | defaults: { 2906 | event: 'pan', 2907 | threshold: 10, 2908 | pointers: 1, 2909 | direction: DIRECTION_ALL 2910 | }, 2911 | 2912 | getTouchAction: function() { 2913 | var direction = this.options.direction; 2914 | var actions = []; 2915 | if (direction & DIRECTION_HORIZONTAL) { 2916 | actions.push(TOUCH_ACTION_PAN_Y); 2917 | } 2918 | if (direction & DIRECTION_VERTICAL) { 2919 | actions.push(TOUCH_ACTION_PAN_X); 2920 | } 2921 | return actions; 2922 | }, 2923 | 2924 | directionTest: function(input) { 2925 | var options = this.options; 2926 | var hasMoved = true; 2927 | var distance = input.distance; 2928 | var direction = input.direction; 2929 | var x = input.deltaX; 2930 | var y = input.deltaY; 2931 | 2932 | // lock to axis? 2933 | if (!(direction & options.direction)) { 2934 | if (options.direction & DIRECTION_HORIZONTAL) { 2935 | direction = (x === 0) ? DIRECTION_NONE : (x < 0) ? DIRECTION_LEFT : DIRECTION_RIGHT; 2936 | hasMoved = x != this.pX; 2937 | distance = Math.abs(input.deltaX); 2938 | } else { 2939 | direction = (y === 0) ? DIRECTION_NONE : (y < 0) ? DIRECTION_UP : DIRECTION_DOWN; 2940 | hasMoved = y != this.pY; 2941 | distance = Math.abs(input.deltaY); 2942 | } 2943 | } 2944 | input.direction = direction; 2945 | return hasMoved && distance > options.threshold && direction & options.direction; 2946 | }, 2947 | 2948 | attrTest: function(input) { 2949 | return AttrRecognizer.prototype.attrTest.call(this, input) && 2950 | (this.state & STATE_BEGAN || (!(this.state & STATE_BEGAN) && this.directionTest(input))); 2951 | }, 2952 | 2953 | emit: function(input) { 2954 | 2955 | this.pX = input.deltaX; 2956 | this.pY = input.deltaY; 2957 | 2958 | var direction = directionStr(input.direction); 2959 | 2960 | if (direction) { 2961 | input.additionalEvent = this.options.event + direction; 2962 | } 2963 | this._super.emit.call(this, input); 2964 | } 2965 | }); 2966 | 2967 | /** 2968 | * Pinch 2969 | * Recognized when two or more pointers are moving toward (zoom-in) or away from each other (zoom-out). 2970 | * @constructor 2971 | * @extends AttrRecognizer 2972 | */ 2973 | function PinchRecognizer() { 2974 | AttrRecognizer.apply(this, arguments); 2975 | } 2976 | 2977 | inherit(PinchRecognizer, AttrRecognizer, { 2978 | /** 2979 | * @namespace 2980 | * @memberof PinchRecognizer 2981 | */ 2982 | defaults: { 2983 | event: 'pinch', 2984 | threshold: 0, 2985 | pointers: 2 2986 | }, 2987 | 2988 | getTouchAction: function() { 2989 | return [TOUCH_ACTION_NONE]; 2990 | }, 2991 | 2992 | attrTest: function(input) { 2993 | return this._super.attrTest.call(this, input) && 2994 | (Math.abs(input.scale - 1) > this.options.threshold || this.state & STATE_BEGAN); 2995 | }, 2996 | 2997 | emit: function(input) { 2998 | if (input.scale !== 1) { 2999 | var inOut = input.scale < 1 ? 'in' : 'out'; 3000 | input.additionalEvent = this.options.event + inOut; 3001 | } 3002 | this._super.emit.call(this, input); 3003 | } 3004 | }); 3005 | 3006 | /** 3007 | * Press 3008 | * Recognized when the pointer is down for x ms without any movement. 3009 | * @constructor 3010 | * @extends Recognizer 3011 | */ 3012 | function PressRecognizer() { 3013 | Recognizer.apply(this, arguments); 3014 | 3015 | this._timer = null; 3016 | this._input = null; 3017 | } 3018 | 3019 | inherit(PressRecognizer, Recognizer, { 3020 | /** 3021 | * @namespace 3022 | * @memberof PressRecognizer 3023 | */ 3024 | defaults: { 3025 | event: 'press', 3026 | pointers: 1, 3027 | time: 251, // minimal time of the pointer to be pressed 3028 | threshold: 9 // a minimal movement is ok, but keep it low 3029 | }, 3030 | 3031 | getTouchAction: function() { 3032 | return [TOUCH_ACTION_AUTO]; 3033 | }, 3034 | 3035 | process: function(input) { 3036 | var options = this.options; 3037 | var validPointers = input.pointers.length === options.pointers; 3038 | var validMovement = input.distance < options.threshold; 3039 | var validTime = input.deltaTime > options.time; 3040 | 3041 | this._input = input; 3042 | 3043 | // we only allow little movement 3044 | // and we've reached an end event, so a tap is possible 3045 | if (!validMovement || !validPointers || (input.eventType & (INPUT_END | INPUT_CANCEL) && !validTime)) { 3046 | this.reset(); 3047 | } else if (input.eventType & INPUT_START) { 3048 | this.reset(); 3049 | this._timer = setTimeoutContext(function() { 3050 | this.state = STATE_RECOGNIZED; 3051 | this.tryEmit(); 3052 | }, options.time, this); 3053 | } else if (input.eventType & INPUT_END) { 3054 | return STATE_RECOGNIZED; 3055 | } 3056 | return STATE_FAILED; 3057 | }, 3058 | 3059 | reset: function() { 3060 | clearTimeout(this._timer); 3061 | }, 3062 | 3063 | emit: function(input) { 3064 | if (this.state !== STATE_RECOGNIZED) { 3065 | return; 3066 | } 3067 | 3068 | if (input && (input.eventType & INPUT_END)) { 3069 | this.manager.emit(this.options.event + 'up', input); 3070 | } else { 3071 | this._input.timeStamp = now(); 3072 | this.manager.emit(this.options.event, this._input); 3073 | } 3074 | } 3075 | }); 3076 | 3077 | /** 3078 | * Rotate 3079 | * Recognized when two or more pointer are moving in a circular motion. 3080 | * @constructor 3081 | * @extends AttrRecognizer 3082 | */ 3083 | function RotateRecognizer() { 3084 | AttrRecognizer.apply(this, arguments); 3085 | } 3086 | 3087 | inherit(RotateRecognizer, AttrRecognizer, { 3088 | /** 3089 | * @namespace 3090 | * @memberof RotateRecognizer 3091 | */ 3092 | defaults: { 3093 | event: 'rotate', 3094 | threshold: 0, 3095 | pointers: 2 3096 | }, 3097 | 3098 | getTouchAction: function() { 3099 | return [TOUCH_ACTION_NONE]; 3100 | }, 3101 | 3102 | attrTest: function(input) { 3103 | return this._super.attrTest.call(this, input) && 3104 | (Math.abs(input.rotation) > this.options.threshold || this.state & STATE_BEGAN); 3105 | } 3106 | }); 3107 | 3108 | /** 3109 | * Swipe 3110 | * Recognized when the pointer is moving fast (velocity), with enough distance in the allowed direction. 3111 | * @constructor 3112 | * @extends AttrRecognizer 3113 | */ 3114 | function SwipeRecognizer() { 3115 | AttrRecognizer.apply(this, arguments); 3116 | } 3117 | 3118 | inherit(SwipeRecognizer, AttrRecognizer, { 3119 | /** 3120 | * @namespace 3121 | * @memberof SwipeRecognizer 3122 | */ 3123 | defaults: { 3124 | event: 'swipe', 3125 | threshold: 10, 3126 | velocity: 0.3, 3127 | direction: DIRECTION_HORIZONTAL | DIRECTION_VERTICAL, 3128 | pointers: 1 3129 | }, 3130 | 3131 | getTouchAction: function() { 3132 | return PanRecognizer.prototype.getTouchAction.call(this); 3133 | }, 3134 | 3135 | attrTest: function(input) { 3136 | var direction = this.options.direction; 3137 | var velocity; 3138 | 3139 | if (direction & (DIRECTION_HORIZONTAL | DIRECTION_VERTICAL)) { 3140 | velocity = input.overallVelocity; 3141 | } else if (direction & DIRECTION_HORIZONTAL) { 3142 | velocity = input.overallVelocityX; 3143 | } else if (direction & DIRECTION_VERTICAL) { 3144 | velocity = input.overallVelocityY; 3145 | } 3146 | 3147 | return this._super.attrTest.call(this, input) && 3148 | direction & input.offsetDirection && 3149 | input.distance > this.options.threshold && 3150 | input.maxPointers == this.options.pointers && 3151 | abs(velocity) > this.options.velocity && input.eventType & INPUT_END; 3152 | }, 3153 | 3154 | emit: function(input) { 3155 | var direction = directionStr(input.offsetDirection); 3156 | if (direction) { 3157 | this.manager.emit(this.options.event + direction, input); 3158 | } 3159 | 3160 | this.manager.emit(this.options.event, input); 3161 | } 3162 | }); 3163 | 3164 | /** 3165 | * A tap is ecognized when the pointer is doing a small tap/click. Multiple taps are recognized if they occur 3166 | * between the given interval and position. The delay option can be used to recognize multi-taps without firing 3167 | * a single tap. 3168 | * 3169 | * The eventData from the emitted event contains the property `tapCount`, which contains the amount of 3170 | * multi-taps being recognized. 3171 | * @constructor 3172 | * @extends Recognizer 3173 | */ 3174 | function TapRecognizer() { 3175 | Recognizer.apply(this, arguments); 3176 | 3177 | // previous time and center, 3178 | // used for tap counting 3179 | this.pTime = false; 3180 | this.pCenter = false; 3181 | 3182 | this._timer = null; 3183 | this._input = null; 3184 | this.count = 0; 3185 | } 3186 | 3187 | inherit(TapRecognizer, Recognizer, { 3188 | /** 3189 | * @namespace 3190 | * @memberof PinchRecognizer 3191 | */ 3192 | defaults: { 3193 | event: 'tap', 3194 | pointers: 1, 3195 | taps: 1, 3196 | interval: 300, // max time between the multi-tap taps 3197 | time: 250, // max time of the pointer to be down (like finger on the screen) 3198 | threshold: 9, // a minimal movement is ok, but keep it low 3199 | posThreshold: 10 // a multi-tap can be a bit off the initial position 3200 | }, 3201 | 3202 | getTouchAction: function() { 3203 | return [TOUCH_ACTION_MANIPULATION]; 3204 | }, 3205 | 3206 | process: function(input) { 3207 | var options = this.options; 3208 | 3209 | var validPointers = input.pointers.length === options.pointers; 3210 | var validMovement = input.distance < options.threshold; 3211 | var validTouchTime = input.deltaTime < options.time; 3212 | 3213 | this.reset(); 3214 | 3215 | if ((input.eventType & INPUT_START) && (this.count === 0)) { 3216 | return this.failTimeout(); 3217 | } 3218 | 3219 | // we only allow little movement 3220 | // and we've reached an end event, so a tap is possible 3221 | if (validMovement && validTouchTime && validPointers) { 3222 | if (input.eventType != INPUT_END) { 3223 | return this.failTimeout(); 3224 | } 3225 | 3226 | var validInterval = this.pTime ? (input.timeStamp - this.pTime < options.interval) : true; 3227 | var validMultiTap = !this.pCenter || getDistance(this.pCenter, input.center) < options.posThreshold; 3228 | 3229 | this.pTime = input.timeStamp; 3230 | this.pCenter = input.center; 3231 | 3232 | if (!validMultiTap || !validInterval) { 3233 | this.count = 1; 3234 | } else { 3235 | this.count += 1; 3236 | } 3237 | 3238 | this._input = input; 3239 | 3240 | // if tap count matches we have recognized it, 3241 | // else it has began recognizing... 3242 | var tapCount = this.count % options.taps; 3243 | if (tapCount === 0) { 3244 | // no failing requirements, immediately trigger the tap event 3245 | // or wait as long as the multitap interval to trigger 3246 | if (!this.hasRequireFailures()) { 3247 | return STATE_RECOGNIZED; 3248 | } else { 3249 | this._timer = setTimeoutContext(function() { 3250 | this.state = STATE_RECOGNIZED; 3251 | this.tryEmit(); 3252 | }, options.interval, this); 3253 | return STATE_BEGAN; 3254 | } 3255 | } 3256 | } 3257 | return STATE_FAILED; 3258 | }, 3259 | 3260 | failTimeout: function() { 3261 | this._timer = setTimeoutContext(function() { 3262 | this.state = STATE_FAILED; 3263 | }, this.options.interval, this); 3264 | return STATE_FAILED; 3265 | }, 3266 | 3267 | reset: function() { 3268 | clearTimeout(this._timer); 3269 | }, 3270 | 3271 | emit: function() { 3272 | if (this.state == STATE_RECOGNIZED) { 3273 | this._input.tapCount = this.count; 3274 | this.manager.emit(this.options.event, this._input); 3275 | } 3276 | } 3277 | }); 3278 | 3279 | /** 3280 | * Simple way to create a manager with a default set of recognizers. 3281 | * @param {HTMLElement} element 3282 | * @param {Object} [options] 3283 | * @constructor 3284 | */ 3285 | function Hammer(element, options) { 3286 | options = options || {}; 3287 | options.recognizers = ifUndefined(options.recognizers, Hammer.defaults.preset); 3288 | return new Manager(element, options); 3289 | } 3290 | 3291 | /** 3292 | * @const {string} 3293 | */ 3294 | Hammer.VERSION = '2.0.7'; 3295 | 3296 | /** 3297 | * default settings 3298 | * @namespace 3299 | */ 3300 | Hammer.defaults = { 3301 | /** 3302 | * set if DOM events are being triggered. 3303 | * But this is slower and unused by simple implementations, so disabled by default. 3304 | * @type {Boolean} 3305 | * @default false 3306 | */ 3307 | domEvents: false, 3308 | 3309 | /** 3310 | * The value for the touchAction property/fallback. 3311 | * When set to `compute` it will magically set the correct value based on the added recognizers. 3312 | * @type {String} 3313 | * @default compute 3314 | */ 3315 | touchAction: TOUCH_ACTION_COMPUTE, 3316 | 3317 | /** 3318 | * @type {Boolean} 3319 | * @default true 3320 | */ 3321 | enable: true, 3322 | 3323 | /** 3324 | * EXPERIMENTAL FEATURE -- can be removed/changed 3325 | * Change the parent input target element. 3326 | * If Null, then it is being set the to main element. 3327 | * @type {Null|EventTarget} 3328 | * @default null 3329 | */ 3330 | inputTarget: null, 3331 | 3332 | /** 3333 | * force an input class 3334 | * @type {Null|Function} 3335 | * @default null 3336 | */ 3337 | inputClass: null, 3338 | 3339 | /** 3340 | * Default recognizer setup when calling `Hammer()` 3341 | * When creating a new Manager these will be skipped. 3342 | * @type {Array} 3343 | */ 3344 | preset: [ 3345 | // RecognizerClass, options, [recognizeWith, ...], [requireFailure, ...] 3346 | [RotateRecognizer, {enable: false}], 3347 | [PinchRecognizer, {enable: false}, ['rotate']], 3348 | [SwipeRecognizer, {direction: DIRECTION_HORIZONTAL}], 3349 | [PanRecognizer, {direction: DIRECTION_HORIZONTAL}, ['swipe']], 3350 | [TapRecognizer], 3351 | [TapRecognizer, {event: 'doubletap', taps: 2}, ['tap']], 3352 | [PressRecognizer] 3353 | ], 3354 | 3355 | /** 3356 | * Some CSS properties can be used to improve the working of Hammer. 3357 | * Add them to this method and they will be set when creating a new Manager. 3358 | * @namespace 3359 | */ 3360 | cssProps: { 3361 | /** 3362 | * Disables text selection to improve the dragging gesture. Mainly for desktop browsers. 3363 | * @type {String} 3364 | * @default 'none' 3365 | */ 3366 | userSelect: 'none', 3367 | 3368 | /** 3369 | * Disable the Windows Phone grippers when pressing an element. 3370 | * @type {String} 3371 | * @default 'none' 3372 | */ 3373 | touchSelect: 'none', 3374 | 3375 | /** 3376 | * Disables the default callout shown when you touch and hold a touch target. 3377 | * On iOS, when you touch and hold a touch target such as a link, Safari displays 3378 | * a callout containing information about the link. This property allows you to disable that callout. 3379 | * @type {String} 3380 | * @default 'none' 3381 | */ 3382 | touchCallout: 'none', 3383 | 3384 | /** 3385 | * Specifies whether zooming is enabled. Used by IE10> 3386 | * @type {String} 3387 | * @default 'none' 3388 | */ 3389 | contentZooming: 'none', 3390 | 3391 | /** 3392 | * Specifies that an entire element should be draggable instead of its contents. Mainly for desktop browsers. 3393 | * @type {String} 3394 | * @default 'none' 3395 | */ 3396 | userDrag: 'none', 3397 | 3398 | /** 3399 | * Overrides the highlight color shown when the user taps a link or a JavaScript 3400 | * clickable element in iOS. This property obeys the alpha value, if specified. 3401 | * @type {String} 3402 | * @default 'rgba(0,0,0,0)' 3403 | */ 3404 | tapHighlightColor: 'rgba(0,0,0,0)' 3405 | } 3406 | }; 3407 | 3408 | var STOP = 1; 3409 | var FORCED_STOP = 2; 3410 | 3411 | /** 3412 | * Manager 3413 | * @param {HTMLElement} element 3414 | * @param {Object} [options] 3415 | * @constructor 3416 | */ 3417 | function Manager(element, options) { 3418 | this.options = assign({}, Hammer.defaults, options || {}); 3419 | 3420 | this.options.inputTarget = this.options.inputTarget || element; 3421 | 3422 | this.handlers = {}; 3423 | this.session = {}; 3424 | this.recognizers = []; 3425 | this.oldCssProps = {}; 3426 | 3427 | this.element = element; 3428 | this.input = createInputInstance(this); 3429 | this.touchAction = new TouchAction(this, this.options.touchAction); 3430 | 3431 | toggleCssProps(this, true); 3432 | 3433 | each(this.options.recognizers, function(item) { 3434 | var recognizer = this.add(new (item[0])(item[1])); 3435 | item[2] && recognizer.recognizeWith(item[2]); 3436 | item[3] && recognizer.requireFailure(item[3]); 3437 | }, this); 3438 | } 3439 | 3440 | Manager.prototype = { 3441 | /** 3442 | * set options 3443 | * @param {Object} options 3444 | * @returns {Manager} 3445 | */ 3446 | set: function(options) { 3447 | assign(this.options, options); 3448 | 3449 | // Options that need a little more setup 3450 | if (options.touchAction) { 3451 | this.touchAction.update(); 3452 | } 3453 | if (options.inputTarget) { 3454 | // Clean up existing event listeners and reinitialize 3455 | this.input.destroy(); 3456 | this.input.target = options.inputTarget; 3457 | this.input.init(); 3458 | } 3459 | return this; 3460 | }, 3461 | 3462 | /** 3463 | * stop recognizing for this session. 3464 | * This session will be discarded, when a new [input]start event is fired. 3465 | * When forced, the recognizer cycle is stopped immediately. 3466 | * @param {Boolean} [force] 3467 | */ 3468 | stop: function(force) { 3469 | this.session.stopped = force ? FORCED_STOP : STOP; 3470 | }, 3471 | 3472 | /** 3473 | * run the recognizers! 3474 | * called by the inputHandler function on every movement of the pointers (touches) 3475 | * it walks through all the recognizers and tries to detect the gesture that is being made 3476 | * @param {Object} inputData 3477 | */ 3478 | recognize: function(inputData) { 3479 | var session = this.session; 3480 | if (session.stopped) { 3481 | return; 3482 | } 3483 | 3484 | // run the touch-action polyfill 3485 | this.touchAction.preventDefaults(inputData); 3486 | 3487 | var recognizer; 3488 | var recognizers = this.recognizers; 3489 | 3490 | // this holds the recognizer that is being recognized. 3491 | // so the recognizer's state needs to be BEGAN, CHANGED, ENDED or RECOGNIZED 3492 | // if no recognizer is detecting a thing, it is set to `null` 3493 | var curRecognizer = session.curRecognizer; 3494 | 3495 | // reset when the last recognizer is recognized 3496 | // or when we're in a new session 3497 | if (!curRecognizer || (curRecognizer && curRecognizer.state & STATE_RECOGNIZED)) { 3498 | curRecognizer = session.curRecognizer = null; 3499 | } 3500 | 3501 | var i = 0; 3502 | while (i < recognizers.length) { 3503 | recognizer = recognizers[i]; 3504 | 3505 | // find out if we are allowed try to recognize the input for this one. 3506 | // 1. allow if the session is NOT forced stopped (see the .stop() method) 3507 | // 2. allow if we still haven't recognized a gesture in this session, or the this recognizer is the one 3508 | // that is being recognized. 3509 | // 3. allow if the recognizer is allowed to run simultaneous with the current recognized recognizer. 3510 | // this can be setup with the `recognizeWith()` method on the recognizer. 3511 | if (session.stopped !== FORCED_STOP && ( // 1 3512 | !curRecognizer || recognizer == curRecognizer || // 2 3513 | recognizer.canRecognizeWith(curRecognizer))) { // 3 3514 | recognizer.recognize(inputData); 3515 | } else { 3516 | recognizer.reset(); 3517 | } 3518 | 3519 | // if the recognizer has been recognizing the input as a valid gesture, we want to store this one as the 3520 | // current active recognizer. but only if we don't already have an active recognizer 3521 | if (!curRecognizer && recognizer.state & (STATE_BEGAN | STATE_CHANGED | STATE_ENDED)) { 3522 | curRecognizer = session.curRecognizer = recognizer; 3523 | } 3524 | i++; 3525 | } 3526 | }, 3527 | 3528 | /** 3529 | * get a recognizer by its event name. 3530 | * @param {Recognizer|String} recognizer 3531 | * @returns {Recognizer|Null} 3532 | */ 3533 | get: function(recognizer) { 3534 | if (recognizer instanceof Recognizer) { 3535 | return recognizer; 3536 | } 3537 | 3538 | var recognizers = this.recognizers; 3539 | for (var i = 0; i < recognizers.length; i++) { 3540 | if (recognizers[i].options.event == recognizer) { 3541 | return recognizers[i]; 3542 | } 3543 | } 3544 | return null; 3545 | }, 3546 | 3547 | /** 3548 | * add a recognizer to the manager 3549 | * existing recognizers with the same event name will be removed 3550 | * @param {Recognizer} recognizer 3551 | * @returns {Recognizer|Manager} 3552 | */ 3553 | add: function(recognizer) { 3554 | if (invokeArrayArg(recognizer, 'add', this)) { 3555 | return this; 3556 | } 3557 | 3558 | // remove existing 3559 | var existing = this.get(recognizer.options.event); 3560 | if (existing) { 3561 | this.remove(existing); 3562 | } 3563 | 3564 | this.recognizers.push(recognizer); 3565 | recognizer.manager = this; 3566 | 3567 | this.touchAction.update(); 3568 | return recognizer; 3569 | }, 3570 | 3571 | /** 3572 | * remove a recognizer by name or instance 3573 | * @param {Recognizer|String} recognizer 3574 | * @returns {Manager} 3575 | */ 3576 | remove: function(recognizer) { 3577 | if (invokeArrayArg(recognizer, 'remove', this)) { 3578 | return this; 3579 | } 3580 | 3581 | recognizer = this.get(recognizer); 3582 | 3583 | // let's make sure this recognizer exists 3584 | if (recognizer) { 3585 | var recognizers = this.recognizers; 3586 | var index = inArray(recognizers, recognizer); 3587 | 3588 | if (index !== -1) { 3589 | recognizers.splice(index, 1); 3590 | this.touchAction.update(); 3591 | } 3592 | } 3593 | 3594 | return this; 3595 | }, 3596 | 3597 | /** 3598 | * bind event 3599 | * @param {String} events 3600 | * @param {Function} handler 3601 | * @returns {EventEmitter} this 3602 | */ 3603 | on: function(events, handler) { 3604 | if (events === undefined) { 3605 | return; 3606 | } 3607 | if (handler === undefined) { 3608 | return; 3609 | } 3610 | 3611 | var handlers = this.handlers; 3612 | each(splitStr(events), function(event) { 3613 | handlers[event] = handlers[event] || []; 3614 | handlers[event].push(handler); 3615 | }); 3616 | return this; 3617 | }, 3618 | 3619 | /** 3620 | * unbind event, leave emit blank to remove all handlers 3621 | * @param {String} events 3622 | * @param {Function} [handler] 3623 | * @returns {EventEmitter} this 3624 | */ 3625 | off: function(events, handler) { 3626 | if (events === undefined) { 3627 | return; 3628 | } 3629 | 3630 | var handlers = this.handlers; 3631 | each(splitStr(events), function(event) { 3632 | if (!handler) { 3633 | delete handlers[event]; 3634 | } else { 3635 | handlers[event] && handlers[event].splice(inArray(handlers[event], handler), 1); 3636 | } 3637 | }); 3638 | return this; 3639 | }, 3640 | 3641 | /** 3642 | * emit event to the listeners 3643 | * @param {String} event 3644 | * @param {Object} data 3645 | */ 3646 | emit: function(event, data) { 3647 | // we also want to trigger dom events 3648 | if (this.options.domEvents) { 3649 | triggerDomEvent(event, data); 3650 | } 3651 | 3652 | // no handlers, so skip it all 3653 | var handlers = this.handlers[event] && this.handlers[event].slice(); 3654 | if (!handlers || !handlers.length) { 3655 | return; 3656 | } 3657 | 3658 | data.type = event; 3659 | data.preventDefault = function() { 3660 | data.srcEvent.preventDefault(); 3661 | }; 3662 | 3663 | var i = 0; 3664 | while (i < handlers.length) { 3665 | handlers[i](data); 3666 | i++; 3667 | } 3668 | }, 3669 | 3670 | /** 3671 | * destroy the manager and unbinds all events 3672 | * it doesn't unbind dom events, that is the user own responsibility 3673 | */ 3674 | destroy: function() { 3675 | this.element && toggleCssProps(this, false); 3676 | 3677 | this.handlers = {}; 3678 | this.session = {}; 3679 | this.input.destroy(); 3680 | this.element = null; 3681 | } 3682 | }; 3683 | 3684 | /** 3685 | * add/remove the css properties as defined in manager.options.cssProps 3686 | * @param {Manager} manager 3687 | * @param {Boolean} add 3688 | */ 3689 | function toggleCssProps(manager, add) { 3690 | var element = manager.element; 3691 | if (!element.style) { 3692 | return; 3693 | } 3694 | var prop; 3695 | each(manager.options.cssProps, function(value, name) { 3696 | prop = prefixed(element.style, name); 3697 | if (add) { 3698 | manager.oldCssProps[prop] = element.style[prop]; 3699 | element.style[prop] = value; 3700 | } else { 3701 | element.style[prop] = manager.oldCssProps[prop] || ''; 3702 | } 3703 | }); 3704 | if (!add) { 3705 | manager.oldCssProps = {}; 3706 | } 3707 | } 3708 | 3709 | /** 3710 | * trigger dom event 3711 | * @param {String} event 3712 | * @param {Object} data 3713 | */ 3714 | function triggerDomEvent(event, data) { 3715 | var gestureEvent = document.createEvent('Event'); 3716 | gestureEvent.initEvent(event, true, true); 3717 | gestureEvent.gesture = data; 3718 | data.target.dispatchEvent(gestureEvent); 3719 | } 3720 | 3721 | assign(Hammer, { 3722 | INPUT_START: INPUT_START, 3723 | INPUT_MOVE: INPUT_MOVE, 3724 | INPUT_END: INPUT_END, 3725 | INPUT_CANCEL: INPUT_CANCEL, 3726 | 3727 | STATE_POSSIBLE: STATE_POSSIBLE, 3728 | STATE_BEGAN: STATE_BEGAN, 3729 | STATE_CHANGED: STATE_CHANGED, 3730 | STATE_ENDED: STATE_ENDED, 3731 | STATE_RECOGNIZED: STATE_RECOGNIZED, 3732 | STATE_CANCELLED: STATE_CANCELLED, 3733 | STATE_FAILED: STATE_FAILED, 3734 | 3735 | DIRECTION_NONE: DIRECTION_NONE, 3736 | DIRECTION_LEFT: DIRECTION_LEFT, 3737 | DIRECTION_RIGHT: DIRECTION_RIGHT, 3738 | DIRECTION_UP: DIRECTION_UP, 3739 | DIRECTION_DOWN: DIRECTION_DOWN, 3740 | DIRECTION_HORIZONTAL: DIRECTION_HORIZONTAL, 3741 | DIRECTION_VERTICAL: DIRECTION_VERTICAL, 3742 | DIRECTION_ALL: DIRECTION_ALL, 3743 | 3744 | Manager: Manager, 3745 | Input: Input, 3746 | TouchAction: TouchAction, 3747 | 3748 | TouchInput: TouchInput, 3749 | MouseInput: MouseInput, 3750 | PointerEventInput: PointerEventInput, 3751 | TouchMouseInput: TouchMouseInput, 3752 | SingleTouchInput: SingleTouchInput, 3753 | 3754 | Recognizer: Recognizer, 3755 | AttrRecognizer: AttrRecognizer, 3756 | Tap: TapRecognizer, 3757 | Pan: PanRecognizer, 3758 | Swipe: SwipeRecognizer, 3759 | Pinch: PinchRecognizer, 3760 | Rotate: RotateRecognizer, 3761 | Press: PressRecognizer, 3762 | 3763 | on: addEventListeners, 3764 | off: removeEventListeners, 3765 | each: each, 3766 | merge: merge, 3767 | extend: extend, 3768 | assign: assign, 3769 | inherit: inherit, 3770 | bindFn: bindFn, 3771 | prefixed: prefixed 3772 | }); 3773 | 3774 | // this prevents errors when Hammer is loaded in the presence of an AMD 3775 | // style loader but by script tag, not by the loader. 3776 | var freeGlobal = (typeof window !== 'undefined' ? window : (typeof self !== 'undefined' ? self : {})); // jshint ignore:line 3777 | freeGlobal.Hammer = Hammer; 3778 | 3779 | if (true) { 3780 | !(__WEBPACK_AMD_DEFINE_RESULT__ = function() { 3781 | return Hammer; 3782 | }.call(exports, __webpack_require__, exports, module), 3783 | __WEBPACK_AMD_DEFINE_RESULT__ !== undefined && (module.exports = __WEBPACK_AMD_DEFINE_RESULT__)); 3784 | } else if (typeof module != 'undefined' && module.exports) { 3785 | module.exports = Hammer; 3786 | } else { 3787 | window[exportName] = Hammer; 3788 | } 3789 | 3790 | })(window, document, 'Hammer'); 3791 | 3792 | 3793 | /***/ }), 3794 | /* 8 */ 3795 | /***/ (function(module, exports, __webpack_require__) { 3796 | 3797 | var __WEBPACK_AMD_DEFINE_FACTORY__, __WEBPACK_AMD_DEFINE_ARRAY__, __WEBPACK_AMD_DEFINE_RESULT__;/* 3798 | 2017 Julian Garnier 3799 | Released under the MIT license 3800 | */ 3801 | var $jscomp$this=this; 3802 | (function(v,p){ true?!(__WEBPACK_AMD_DEFINE_ARRAY__ = [], __WEBPACK_AMD_DEFINE_FACTORY__ = (p), 3803 | __WEBPACK_AMD_DEFINE_RESULT__ = (typeof __WEBPACK_AMD_DEFINE_FACTORY__ === 'function' ? 3804 | (__WEBPACK_AMD_DEFINE_FACTORY__.apply(exports, __WEBPACK_AMD_DEFINE_ARRAY__)) : __WEBPACK_AMD_DEFINE_FACTORY__), 3805 | __WEBPACK_AMD_DEFINE_RESULT__ !== undefined && (module.exports = __WEBPACK_AMD_DEFINE_RESULT__)):"object"===typeof module&&module.exports?module.exports=p():v.anime=p()})(this,function(){function v(a){if(!g.col(a))try{return document.querySelectorAll(a)}catch(b){}}function p(a){return a.reduce(function(a,d){return a.concat(g.arr(d)?p(d):d)},[])}function w(a){if(g.arr(a))return a;g.str(a)&&(a=v(a)||a);return a instanceof NodeList||a instanceof HTMLCollection?[].slice.call(a):[a]}function F(a,b){return a.some(function(a){return a===b})} 3806 | function A(a){var b={},d;for(d in a)b[d]=a[d];return b}function G(a,b){var d=A(a),c;for(c in a)d[c]=b.hasOwnProperty(c)?b[c]:a[c];return d}function B(a,b){var d=A(a),c;for(c in b)d[c]=g.und(a[c])?b[c]:a[c];return d}function S(a){a=a.replace(/^#?([a-f\d])([a-f\d])([a-f\d])$/i,function(a,b,d,h){return b+b+d+d+h+h});var b=/^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(a);a=parseInt(b[1],16);var d=parseInt(b[2],16),b=parseInt(b[3],16);return"rgb("+a+","+d+","+b+")"}function T(a){function b(a,b,c){0> 3807 | c&&(c+=1);1c?b:c<2/3?a+(b-a)*(2/3-c)*6:a}var d=/hsl\((\d+),\s*([\d.]+)%,\s*([\d.]+)%\)/g.exec(a);a=parseInt(d[1])/360;var c=parseInt(d[2])/100,d=parseInt(d[3])/100;if(0==c)c=d=a=d;else{var e=.5>d?d*(1+c):d+c-d*c,l=2*d-e,c=b(l,e,a+1/3),d=b(l,e,a);a=b(l,e,a-1/3)}return"rgb("+255*c+","+255*d+","+255*a+")"}function x(a){if(a=/([\+\-]?[0-9#\.]+)(%|px|pt|em|rem|in|cm|mm|ex|pc|vw|vh|deg|rad|turn)?/.exec(a))return a[2]}function U(a){if(-1=f.currentTime)for(var u=0;ul&&r=n&&(f.began=!0,e("begin")),e("run")):(r<=l&&0!==P&&(c(0),q&&g()),r>=h&&P!==h&&(c(h),q||g()));a>=h&&(f.remaining?(t=m,"alternate"===f.direction&&(f.reversed=!f.reversed)):(f.pause(),"Promise"in window&&(Q(),R=b()),f.completed||(f.completed=!0,e("complete"))), 3818 | k=0);e("update")}a=void 0===a?{}:a;var m,t,k=0,Q=null,R=b(),f=fa(a);f.reset=function(){var a=f.direction,b=f.loop;f.currentTime=0;f.progress=0;f.paused=!0;f.began=!1;f.completed=!1;f.reversed="reverse"===a;f.remaining="alternate"===a&&1===b?2:b;for(a=f.children.length;a--;)b=f.children[a],b.seek(b.offset),b.reset()};f.tick=function(a){m=a;t||(t=m);h((k+m-t)*n.speed)};f.seek=function(a){h(d(a))};f.pause=function(){var a=q.indexOf(f);-1=b&&0<=c&&1>=c){var g=new Float32Array(11);if(b!==d||c!==e)for(var h=0;11>h;++h)g[h]=a(.1*h,b,c);return function(h){if(b===d&&c===e)return h;if(0===h)return 0;if(1===h)return 1;for(var m=0,k=1;10!==k&&g[k]<=h;++k)m+=.1;--k;var k=m+(h-g[k])/(g[k+1]-g[k])*.1,l=3*(1-3*c+3*b)*k*k+2*(3*c-6*b)*k+3*b;if(.001<=l){for(m=0;4>m;++m){l=3*(1-3*c+3*b)*k*k+2*(3*c-6*b)*k+3*b;if(0===l)break; 3822 | var n=a(k,b,c)-h,k=k-n/l}h=k}else if(0===l)h=k;else{var k=m,m=m+.1,f=0;do n=k+(m-k)/2,l=a(n,b,c)-h,0++f);h=n}return a(h,d,e)}}}}(),N=function(){function a(a,b){return 0===a||1===a?a:-Math.pow(2,10*(a-1))*Math.sin(2*(a-1-b/(2*Math.PI)*Math.asin(1))*Math.PI/b)}var b="Quad Cubic Quart Quint Sine Expo Circ Back Elastic".split(" "),d={In:[[.55,.085,.68,.53],[.55,.055,.675,.19],[.895,.03,.685,.22],[.755,.05,.855,.06],[.47,0,.745,.715],[.95,.05,.795,.035],[.6,.04,.98, 3823 | .335],[.6,-.28,.735,.045],a],Out:[[.25,.46,.45,.94],[.215,.61,.355,1],[.165,.84,.44,1],[.23,1,.32,1],[.39,.575,.565,1],[.19,1,.22,1],[.075,.82,.165,1],[.175,.885,.32,1.275],function(b,c){return 1-a(1-b,c)}],InOut:[[.455,.03,.515,.955],[.645,.045,.355,1],[.77,0,.175,1],[.86,0,.07,1],[.445,.05,.55,.95],[1,0,0,1],[.785,.135,.15,.86],[.68,-.55,.265,1.55],function(b,c){return.5>b?a(2*b,c)/2:1-a(-2*b+2,c)/2}]},c={linear:y(.25,.25,.75,.75)},e={},l;for(l in d)e.type=l,d[e.type].forEach(function(a){return function(d, 3824 | e){c["ease"+a.type+b[e]]=g.fnc(d)?d:y.apply($jscomp$this,d)}}(e)),e={type:e.type};return c}(),ia={css:function(a,b,d){return a.style[b]=d},attribute:function(a,b,d){return a.setAttribute(b,d)},object:function(a,b,d){return a[b]=d},transform:function(a,b,d,c,e){c[e]||(c[e]=[]);c[e].push(b+"("+d+")")}},q=[],z=0,ja=function(){function a(){z=requestAnimationFrame(b)}function b(b){var c=q.length;if(c){for(var d=0;dc&&(b.duration=a.duration);a.began=!0;b.children.push(a)});b.reset();b.seek(0);b.autoplay&&b.restart();return b};return b};n.random=function(a,b){return Math.floor(Math.random()*(b-a+1))+a};return n}); 3827 | 3828 | /***/ }), 3829 | /* 9 */ 3830 | /***/ (function(module, exports, __webpack_require__) { 3831 | 3832 | "use strict"; 3833 | 3834 | 3835 | var alphabet = __webpack_require__(0); 3836 | var encode = __webpack_require__(4); 3837 | var decode = __webpack_require__(12); 3838 | var build = __webpack_require__(13); 3839 | var isValid = __webpack_require__(14); 3840 | 3841 | // if you are using cluster or multiple servers use this to make each instance 3842 | // has a unique value for worker 3843 | // Note: I don't know if this is automatically set when using third 3844 | // party cluster solutions such as pm2. 3845 | var clusterWorkerId = __webpack_require__(15) || 0; 3846 | 3847 | /** 3848 | * Set the seed. 3849 | * Highly recommended if you don't want people to try to figure out your id schema. 3850 | * exposed as shortid.seed(int) 3851 | * @param seed Integer value to seed the random alphabet. ALWAYS USE THE SAME SEED or you might get overlaps. 3852 | */ 3853 | function seed(seedValue) { 3854 | alphabet.seed(seedValue); 3855 | return module.exports; 3856 | } 3857 | 3858 | /** 3859 | * Set the cluster worker or machine id 3860 | * exposed as shortid.worker(int) 3861 | * @param workerId worker must be positive integer. Number less than 16 is recommended. 3862 | * returns shortid module so it can be chained. 3863 | */ 3864 | function worker(workerId) { 3865 | clusterWorkerId = workerId; 3866 | return module.exports; 3867 | } 3868 | 3869 | /** 3870 | * 3871 | * sets new characters to use in the alphabet 3872 | * returns the shuffled alphabet 3873 | */ 3874 | function characters(newCharacters) { 3875 | if (newCharacters !== undefined) { 3876 | alphabet.characters(newCharacters); 3877 | } 3878 | 3879 | return alphabet.shuffled(); 3880 | } 3881 | 3882 | /** 3883 | * Generate unique id 3884 | * Returns string id 3885 | */ 3886 | function generate() { 3887 | return build(clusterWorkerId); 3888 | } 3889 | 3890 | // Export all other functions as properties of the generate function 3891 | module.exports = generate; 3892 | module.exports.generate = generate; 3893 | module.exports.seed = seed; 3894 | module.exports.worker = worker; 3895 | module.exports.characters = characters; 3896 | module.exports.decode = decode; 3897 | module.exports.isValid = isValid; 3898 | 3899 | 3900 | /***/ }), 3901 | /* 10 */ 3902 | /***/ (function(module, exports, __webpack_require__) { 3903 | 3904 | "use strict"; 3905 | 3906 | 3907 | // Found this seed-based random generator somewhere 3908 | // Based on The Central Randomizer 1.3 (C) 1997 by Paul Houle (houle@msc.cornell.edu) 3909 | 3910 | var seed = 1; 3911 | 3912 | /** 3913 | * return a random number based on a seed 3914 | * @param seed 3915 | * @returns {number} 3916 | */ 3917 | function getNextValue() { 3918 | seed = (seed * 9301 + 49297) % 233280; 3919 | return seed/(233280.0); 3920 | } 3921 | 3922 | function setSeed(_seed_) { 3923 | seed = _seed_; 3924 | } 3925 | 3926 | module.exports = { 3927 | nextValue: getNextValue, 3928 | seed: setSeed 3929 | }; 3930 | 3931 | 3932 | /***/ }), 3933 | /* 11 */ 3934 | /***/ (function(module, exports, __webpack_require__) { 3935 | 3936 | "use strict"; 3937 | 3938 | 3939 | var crypto = typeof window === 'object' && (window.crypto || window.msCrypto); // IE 11 uses window.msCrypto 3940 | 3941 | function randomByte() { 3942 | if (!crypto || !crypto.getRandomValues) { 3943 | return Math.floor(Math.random() * 256) & 0x30; 3944 | } 3945 | var dest = new Uint8Array(1); 3946 | crypto.getRandomValues(dest); 3947 | return dest[0] & 0x30; 3948 | } 3949 | 3950 | module.exports = randomByte; 3951 | 3952 | 3953 | /***/ }), 3954 | /* 12 */ 3955 | /***/ (function(module, exports, __webpack_require__) { 3956 | 3957 | "use strict"; 3958 | 3959 | var alphabet = __webpack_require__(0); 3960 | 3961 | /** 3962 | * Decode the id to get the version and worker 3963 | * Mainly for debugging and testing. 3964 | * @param id - the shortid-generated id. 3965 | */ 3966 | function decode(id) { 3967 | var characters = alphabet.shuffled(); 3968 | return { 3969 | version: characters.indexOf(id.substr(0, 1)) & 0x0f, 3970 | worker: characters.indexOf(id.substr(1, 1)) & 0x0f 3971 | }; 3972 | } 3973 | 3974 | module.exports = decode; 3975 | 3976 | 3977 | /***/ }), 3978 | /* 13 */ 3979 | /***/ (function(module, exports, __webpack_require__) { 3980 | 3981 | "use strict"; 3982 | 3983 | 3984 | var encode = __webpack_require__(4); 3985 | var alphabet = __webpack_require__(0); 3986 | 3987 | // Ignore all milliseconds before a certain time to reduce the size of the date entropy without sacrificing uniqueness. 3988 | // This number should be updated every year or so to keep the generated id short. 3989 | // To regenerate `new Date() - 0` and bump the version. Always bump the version! 3990 | var REDUCE_TIME = 1459707606518; 3991 | 3992 | // don't change unless we change the algos or REDUCE_TIME 3993 | // must be an integer and less than 16 3994 | var version = 6; 3995 | 3996 | // Counter is used when shortid is called multiple times in one second. 3997 | var counter; 3998 | 3999 | // Remember the last time shortid was called in case counter is needed. 4000 | var previousSeconds; 4001 | 4002 | /** 4003 | * Generate unique id 4004 | * Returns string id 4005 | */ 4006 | function build(clusterWorkerId) { 4007 | 4008 | var str = ''; 4009 | 4010 | var seconds = Math.floor((Date.now() - REDUCE_TIME) * 0.001); 4011 | 4012 | if (seconds === previousSeconds) { 4013 | counter++; 4014 | } else { 4015 | counter = 0; 4016 | previousSeconds = seconds; 4017 | } 4018 | 4019 | str = str + encode(alphabet.lookup, version); 4020 | str = str + encode(alphabet.lookup, clusterWorkerId); 4021 | if (counter > 0) { 4022 | str = str + encode(alphabet.lookup, counter); 4023 | } 4024 | str = str + encode(alphabet.lookup, seconds); 4025 | 4026 | return str; 4027 | } 4028 | 4029 | module.exports = build; 4030 | 4031 | 4032 | /***/ }), 4033 | /* 14 */ 4034 | /***/ (function(module, exports, __webpack_require__) { 4035 | 4036 | "use strict"; 4037 | 4038 | var alphabet = __webpack_require__(0); 4039 | 4040 | function isShortId(id) { 4041 | if (!id || typeof id !== 'string' || id.length < 6 ) { 4042 | return false; 4043 | } 4044 | 4045 | var characters = alphabet.characters(); 4046 | var len = id.length; 4047 | for(var i = 0; i < len;i++) { 4048 | if (characters.indexOf(id[i]) === -1) { 4049 | return false; 4050 | } 4051 | } 4052 | return true; 4053 | } 4054 | 4055 | module.exports = isShortId; 4056 | 4057 | 4058 | /***/ }), 4059 | /* 15 */ 4060 | /***/ (function(module, exports, __webpack_require__) { 4061 | 4062 | "use strict"; 4063 | 4064 | 4065 | module.exports = 0; 4066 | 4067 | 4068 | /***/ }), 4069 | /* 16 */ 4070 | /***/ (function(module, exports, __webpack_require__) { 4071 | 4072 | "use strict"; 4073 | /** 4074 | * Code refactored from Mozilla Developer Network: 4075 | * https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/assign 4076 | */ 4077 | 4078 | 4079 | 4080 | function assign(target, firstSource) { 4081 | if (target === undefined || target === null) { 4082 | throw new TypeError('Cannot convert first argument to object'); 4083 | } 4084 | 4085 | var to = Object(target); 4086 | for (var i = 1; i < arguments.length; i++) { 4087 | var nextSource = arguments[i]; 4088 | if (nextSource === undefined || nextSource === null) { 4089 | continue; 4090 | } 4091 | 4092 | var keysArray = Object.keys(Object(nextSource)); 4093 | for (var nextIndex = 0, len = keysArray.length; nextIndex < len; nextIndex++) { 4094 | var nextKey = keysArray[nextIndex]; 4095 | var desc = Object.getOwnPropertyDescriptor(nextSource, nextKey); 4096 | if (desc !== undefined && desc.enumerable) { 4097 | to[nextKey] = nextSource[nextKey]; 4098 | } 4099 | } 4100 | } 4101 | return to; 4102 | } 4103 | 4104 | function polyfill() { 4105 | if (!Object.assign) { 4106 | Object.defineProperty(Object, 'assign', { 4107 | enumerable: false, 4108 | configurable: true, 4109 | writable: true, 4110 | value: assign 4111 | }); 4112 | } 4113 | } 4114 | 4115 | module.exports = { 4116 | assign: assign, 4117 | polyfill: polyfill 4118 | }; 4119 | 4120 | 4121 | /***/ }) 4122 | /******/ ]); 4123 | }); 4124 | //# sourceMappingURL=toasted.js.map --------------------------------------------------------------------------------