├── .babelrc ├── .eslintignore ├── .eslintrc.js ├── .gitignore ├── LICENSE ├── README.md ├── RELEASE.md ├── build ├── loader-util.js ├── path-util.js ├── template.index.html ├── uglify.config.base.js ├── webpack.config.base.js ├── webpack.config.demo.js ├── webpack.config.dev.js ├── webpack.config.prod.js └── webpack.config.test.js ├── dist ├── demo.1320e4785106378b9e121f0ec76a34aa.min.css ├── demo.ecd3fe850e3573d9d50a.min.js ├── manifest.e0fcc538cee16be5b5d1.min.js ├── vendor.54f01e7571327264504c.min.js ├── vendor.bd1c25d7d835a9a7714785aa4249167b.min.css ├── vue-typer.js └── vue-typer.min.js ├── index.html ├── package.json ├── src ├── demo │ ├── Demo.vue │ ├── assets │ │ ├── demo.gif │ │ └── images │ │ │ ├── documentation-light.png │ │ │ ├── download-light.png │ │ │ └── github-mark-light.png │ ├── components │ │ ├── AppLayout.vue │ │ ├── BadgeBar.vue │ │ ├── CircleLink.vue │ │ ├── CodeBlock.vue │ │ ├── CopyrightFooter.vue │ │ ├── FormCheck.vue │ │ ├── FormInput.vue │ │ ├── FormRadio.vue │ │ ├── HeroHeader.vue │ │ └── LinkBar.vue │ ├── index.js │ ├── lib │ │ ├── bootstrap │ │ │ ├── bootstrap-flex.min.css │ │ │ ├── bootstrap-flex.min.css.map │ │ │ └── index.js │ │ └── prism │ │ │ ├── index.js │ │ │ ├── prism.css │ │ │ └── prism.js │ └── styles │ │ ├── colors.scss │ │ └── demo.scss └── vue-typer │ ├── components │ ├── Caret.vue │ ├── Char.vue │ └── VueTyper.vue │ ├── index.js │ ├── styles │ ├── caret-animations.scss │ └── typer-colors.scss │ └── utils │ ├── random-int.js │ ├── shallow-equals.js │ └── shuffle.js └── test └── unit ├── .eslintrc ├── index.js ├── karma.conf.js ├── specs └── VueTyper.spec.js └── utils ├── mount.js └── wait.js /.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": ["es2015"], 3 | "plugins": ["transform-runtime"], 4 | "comments": false, 5 | "env": { 6 | "test": { 7 | "plugins": ["istanbul"] 8 | } 9 | } 10 | } -------------------------------------------------------------------------------- /.eslintignore: -------------------------------------------------------------------------------- 1 | src/demo/lib 2 | -------------------------------------------------------------------------------- /.eslintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | root: true, 3 | parser: 'babel-eslint', 4 | parserOptions: { 5 | sourceType: 'module' 6 | }, 7 | // https://github.com/feross/standard/blob/master/RULES.md#javascript-standard-style 8 | extends: 'standard', 9 | plugins: [ 10 | 'html' // required to lint *.vue files 11 | ], 12 | // add your custom rules here 13 | 'rules': { 14 | // allow no-spaces between function name and argument parethesis list 15 | 'space-before-function-paren': 0, 16 | // allow paren-less arrow functions 17 | 'arrow-parens': 0, 18 | // allow debugger during development 19 | 'no-debugger': process.env.NODE_ENV === 'production' ? 2 : 0 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | .vscode 3 | node_modules 4 | *.log 5 | dist/*.map 6 | test/**/coverage 7 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2016 Chris Nguyen 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. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |

2 | VueTyper demo gif 3 |

4 | Vue component that simulates a user typing, selecting, and erasing text. 5 |

6 | Play with vue-typer in this interactive demo. 7 |

8 |

9 | 10 |

11 | Downloads 12 | Version 13 | License 14 |

15 | 16 | - [Getting Started](#getting-started) 17 | - [Usage](#usage) 18 | - [Props](#props) 19 | - [text](#text) 20 | - [repeat](#repeat) 21 | - [shuffle](#shuffle) 22 | - [initialAction](#initialaction) 23 | - [preTypeDelay](#pretypedelay) 24 | - [typeDelay](#typedelay) 25 | - [preEraseDelay](#preerasedelay) 26 | - [eraseDelay](#erasedelay) 27 | - [eraseStyle](#erasestyle) 28 | - [eraseOnComplete](#eraseoncomplete) 29 | - [caretAnimation](#caretanimation) 30 | - [Events](#events) 31 | - [typed](#typed) 32 | - [typed-char](#typed-char) 33 | - [erased](#erased) 34 | - [completed](#completed) 35 | - [Styles](#styles) 36 | - [Contribution Guide](#contribution-guide) 37 | - [Changelog](#changelog) 38 | - [TODO](#todo) 39 | - [License](#license) 40 | 41 | ## Getting Started 42 | 43 | > VueTyper has a single dependency to [lodash.split](https://github.com/lodash/lodash/blob/master/split.js) to support emojis and other multi-codepoint Unicode characters. Apart from this, VueTyper does not have any direct dependencies to any other library or framework -- not even to Vue itself! All required Vue API calls are made through Vue's `this.$*` context methods. This means VueTyper can only execute within a Vue application context, but in exchange, it does not need to pull in Vue, which keeps VueTyper lightweight. 44 | 45 | ### Prerequisites 46 | - Vue v2.x 47 | - VueTyper has not been tested in Vue v1.x. ([See here for migration instructions from Vue 1.x to 2.x.](https://vuejs.org/v2/guide/migration.html)) 48 | 49 | ### Installation 50 | #### npm 51 | Use this method if you wish to import/require VueTyper as a module. 52 | ``` 53 | npm install --save vue-typer 54 | ``` 55 | 56 | #### CDN 57 | Use this method if you wish to access VueTyper globally via `window.VueTyper`. 58 | ```html 59 | 60 | ``` 61 | 62 | ## Usage 63 | After installing VueTyper, you may choose to register it either globally or locally. [What's the difference? See the Vue documentation here.](https://vuejs.org/v2/guide/components.html#Registration) 64 | 65 | #### Local Registration 66 | 1. Import the VueTyper component directly from your Vue component file: 67 | ```javascript 68 | // ES6 69 | import { VueTyper } from 'vue-typer' 70 | // CommonJS 71 | var VueTyper = require('vue-typer').VueTyper 72 | // Global 73 | var VueTyper = window.VueTyper.VueTyper 74 | ``` 75 | 76 | 2. Register it as a local component in your Vue component options: 77 | ```javascript 78 | var MyComponent = { 79 | // ... 80 | components: { 81 | // ES6; property shorthand + Vue should automatically dasherize the key for us 82 | VueTyper 83 | // pre-ES6 84 | 'vue-typer': VueTyper 85 | } 86 | } 87 | ``` 88 | 89 | 3. Use vue-typer in your Vue component's template: 90 | 91 | ```html 92 | 93 | ``` 94 | 95 | #### Global Registration 96 | 1. Import the VueTyper plugin in your application entry point: 97 | ```javascript 98 | // ES6 99 | import VueTyperPlugin from 'vue-typer' 100 | // CommonJS 101 | var VueTyperPlugin = require('vue-typer').default 102 | // Global 103 | var VueTyperPlugin = window.VueTyper.default 104 | ``` 105 | 106 | 2. Register the VueTyper plugin with Vue 107 | ```javascript 108 | Vue.use(VueTyperPlugin) 109 | ``` 110 | 111 | 3. Now you can freely use vue-typer in any Vue component template: 112 | ```html 113 | 114 | ``` 115 | 116 | ## Props 117 | It may be helpful to play around with these props in the [interactive demo](https://cngu.github.io/vue-typer/#playground). 118 | 119 | #### `text` 120 | - **type**: `String || Array` 121 | - **required** 122 | - **validator**: Non-empty 123 | - **Usage**: 124 | 125 | ```html 126 | 127 | ``` 128 | 129 | Either a single string, or an ordered list of strings, for VueTyper to type. Strings will not be trimmed. 130 | 131 | - **Note**: Dynamically changing this value after VueTyper has mounted will cause VueTyper to reset itself and start typing from scratch. 132 | - **See also**: [`shuffle`](#shuffle) 133 | 134 | #### `repeat` 135 | - **type**: `Number` 136 | - **default**: `Infinity` 137 | - **validator**: Non-negative 138 | - **Usage**: 139 | 140 | ```html 141 | 142 | ``` 143 | 144 | Number of _extra_ times to type `text` after the first time. Setting 0 will type `text` once; 1 will type twice; Infinity will type forever. 145 | 146 | - **Note**: Dynamically changing this value after VueTyper has mounted will cause VueTyper to reset itself and start typing from scratch. 147 | 148 | #### `shuffle` 149 | - **type**: `Boolean` 150 | - **default**: `false` 151 | - **Usage**: 152 | 153 | ```html 154 | 155 | ``` 156 | 157 | Randomly shuffles `text` ([using the Fisher-Yates shuffle](https://en.wikipedia.org/wiki/Fisher%E2%80%93Yates_shuffle)) before typing it. If `repeat > 0`, `text` will always be shuffled before repeated typings. `text` is _not_ shuffled after every word is typed. This implies that: 158 | - all strings in `text` will be typed the same number of times, but just in a shuffled order 159 | - the frequencies of the order of strings typed will have equal distributions, e.g.: 160 | - given `text=['a','b']`, a,b will be printed 50% of the time, and b,a the other 50% 161 | - given `text=['a','b','c']`, there are 3!=6 possible permutations, and they will each be printed 100%/6=16.7% of the time 162 | - the only scenarios where the same word can be typed twice in a row are when: 163 | 1. `text` contains duplicate strings, or 164 | 2. `repeat > 0`, `text` is typed where the last word is W, and the next repeat typing shuffled `text` such that it starts with W. 165 | 166 | - **Note**: Dynamically changing this value after VueTyper has mounted will cause VueTyper to reset itself and start typing from scratch. 167 | 168 | #### `initialAction` 169 | - **type**: `String` 170 | - **default**: `"typing"` 171 | - **validator**: `"typing"` || `"erasing"` 172 | - **Usage**: 173 | 174 | ```html 175 | 176 | ``` 177 | 178 | `typing` starts VueTyper off in the "typing" state; there will be empty space as VueTyper begins to type the first string in `text`. 179 | 180 | `erasing` starts VueTyper off in the "erasing" state; the first string in `text` will already be typed and visible as VueTyper begins to erase it. 181 | 182 | #### `preTypeDelay` 183 | - **type**: `Number` 184 | - **default**: `70` 185 | - **validator**: Non-negative 186 | - **Usage**: 187 | 188 | ```html 189 | 190 | ``` 191 | 192 | Milliseconds to wait before typing the first character of every string in `text`. 193 | 194 | This is useful to have an idle period to show a blank space for a period of time before VueTyper types the first character. 195 | 196 | #### `typeDelay` 197 | - **type**: `Number` 198 | - **default**: `70` 199 | - **validator**: Non-negative 200 | - **Usage**: 201 | 202 | ```html 203 | 204 | ``` 205 | 206 | Milliseconds to wait after typing a character, until the next character is typed. 207 | 208 | #### `preEraseDelay` 209 | - **type**: `Number` 210 | - **default**: `2000` 211 | - **validator**: Non-negative 212 | - **Usage**: 213 | 214 | ```html 215 | 216 | ``` 217 | 218 | Milliseconds to wait after a string is fully typed, until the first erase action (i.e. backspace, highlight) is performed. 219 | 220 | This is useful to have an idle period that gives users time to read the typed string before it is erased. 221 | 222 | #### `eraseDelay` 223 | - **type**: `Number` 224 | - **default**: `250` 225 | - **validator**: Non-negative 226 | - **Usage**: 227 | 228 | ```html 229 | 230 | ``` 231 | 232 | Milliseconds to wait after performing an erase action (i.e. backspace, highlight), until the next erase action can start. 233 | 234 | #### `eraseStyle` 235 | - **type**: `String` 236 | - **default**: `"select-all"` 237 | - **validator**: `"backspace"` || `"select-back"` || `"select-all"` || `"clear"` 238 | - **Usage**: 239 | 240 | ```html 241 | 242 | ``` 243 | 244 | `backspace` erases one character at a time, simulating the backspace key. 245 | 246 | `select-back` highlights backward one character at a time, simulating Shift+LeftArrow, and erases everything once all characters are highlighted. 247 | 248 | `select-all` immediately highlights all characters at once, simulating Ctrl/Cmd+A, and erases all characters afterwards. 249 | 250 | `clear` immediately erases all characters at once; the typed string simply disappears. 251 | 252 | #### `eraseOnComplete` 253 | - **type**: `Boolean` 254 | - **default**: `false` 255 | - **Usage**: 256 | 257 | ```html 258 | 259 | ``` 260 | 261 | By default, after VueTyper completes all its typing (i.e. it finishes typing all strings in `text`, `repeat+1` times), the last typed string will not be erased and stay visible. Enabling this flag will tell VueTyper to erase the final string as well. 262 | 263 | - **Note**: Has no effect if `repeat === Infinity`. 264 | 265 | #### `caretAnimation` 266 | - **type**: `String` 267 | - **default**: `"blink"` 268 | - **validator**: `"solid"` || `"blink"` || `"smooth"` || `"phase"` || `"expand"` 269 | - **Usage**: 270 | 271 | ```html 272 | 273 | ``` 274 | 275 | Specifies a built-in caret animation to use, similar to Sublime and VS Code animations. 276 | 277 | - **Note**: Alternatively, custom animations can be applied via CSS. 278 | 279 | - **See also**: [Styles](#styles), [Example Custom Caret Animation](https://cngu.github.io/vue-typer#style-showcase) 280 | 281 | ## Events 282 | #### `typed` 283 | - **Event data**: 284 | - `String` typedString 285 | - **Usage**: 286 | ```html 287 | 288 | ``` 289 | ```javascript 290 | { 291 | ... 292 | methods: { 293 | onTyped: function(typedString) { 294 | // handle typed string 295 | } 296 | } 297 | } 298 | ``` 299 | 300 | Emitted everytime VueTyper finishes typing a string. 301 | 302 | #### `typed-char` 303 | - **Event data**: 304 | - `String` typedChar 305 | - `Number` typedCharIndex 306 | - **Usage**: 307 | ```html 308 | 309 | ``` 310 | ```javascript 311 | { 312 | ... 313 | methods: { 314 | onTypedChar: function(typedChar, typedCharIndex) { 315 | // handle typed character at the given index 316 | // call #1: 'w', 0 317 | // call #2: 'a', 1 318 | // call #3: 't', 2 319 | // ... 320 | } 321 | } 322 | } 323 | ``` 324 | 325 | Emitted everytime VueTyper finishes typing a single character. 326 | 327 | #### `erased` 328 | - **Event data**: 329 | - `String` erasedString 330 | - **Usage**: 331 | ```html 332 | 333 | ``` 334 | ```javascript 335 | { 336 | ... 337 | methods: { 338 | onErased: function(erasedString) { 339 | // handle erased string 340 | } 341 | } 342 | } 343 | ``` 344 | 345 | Emitted everytime VueTyper finishes erasing a string. 346 | 347 | #### `completed` 348 | - **Usage**: 349 | ```html 350 | 351 | ``` 352 | ```javascript 353 | { 354 | ... 355 | methods: { 356 | onComplete: function() { 357 | // handle event when VueTyper has finished all typing/erasing 358 | } 359 | } 360 | } 361 | ``` 362 | 363 | Emitted when VueTyper has finished typing all words in [`text`](#text), [`repeat`](#repeat)`+1` times. 364 | 365 | - **Note**: If [`eraseOnComplete`](#eraseoncomplete) is enabled, the final typed string must also be erased before this event is emitted. 366 | 367 | ## Styles 368 | To keep the separation of concern between component code and styles, VueTyper can be fully styled through CSS (as opposed to props). 369 | 370 | The following is a skeleton selector structure to override the style of each component of VueTyper. 371 | 372 | - **Usage**: 373 | ```css 374 | /* SCSS */ 375 | .vue-typer { 376 | /* Styles for the vue-typer container 377 | e.g. font-family, font-size */ 378 | 379 | .custom.char { 380 | /* Styles for each character 381 | e.g. color, background-color */ 382 | 383 | &.typed { 384 | /* Styles specific to typed characters 385 | i.e. characters to the left of the caret */ 386 | } 387 | &.selected { 388 | /* Styles specific to selected characters 389 | i.e. characters to the right of the caret while VueTyper's 390 | 'eraseStyle' is set to a selection-based style */ 391 | } 392 | &.erased { 393 | /* Styles specific to erased characters 394 | i.e. characters to the right of the caret while VueTyper's 395 | 'eraseStyle' is set to a non-selection-based style */ 396 | } 397 | } 398 | 399 | .custom.caret { 400 | /* Styles for the caret 401 | e.g. background-color, animation, display */ 402 | 403 | &.pre-type { 404 | /* Styles for the caret when it is idle before typing 405 | i.e. before a string starts being typed, during 'preTypeDelay' */ 406 | } 407 | &.pre-erase { 408 | /* Styles for the caret when it is idle before erasing 409 | i.e. before a string starts being erased, during 'preEraseDelay' */ 410 | } 411 | &.idle { 412 | /* Styles for the caret when it is idle, but VueTyper has not yet completed typing 413 | i.e. when 'pre-type' or 'pre-erase' is set, but not 'complete' */ 414 | } 415 | &.typing { 416 | /* Styles for the caret while VueTyper is typing 417 | i.e. when the caret is moving forwards */ 418 | } 419 | &.selecting { 420 | /* Styles for the caret while VueTyper is selecting 421 | i.e. when the caret is moving backwards and 'eraseStyle' is 422 | set to a selection-based style */ 423 | } 424 | &.erasing { 425 | /* Styles for the caret while VueTyper is erasing 426 | i.e. when the caret is moving backwards and 'eraseStyle' is 427 | set to a non-selection-based style */ 428 | } 429 | &.complete { 430 | /* Styles for the idle caret when VueTyper has finished all typing/erasing */ 431 | } 432 | } 433 | } 434 | ``` 435 | 436 | - **Note**: Some of the default styles above make things hidden using `display: none;`. If you wish to make it visible again, use `display: inline-block;`. Do not use `block`. 437 | - **See also**: [CSS Examples](https://cngu.github.io/vue-typer#style-showcase) 438 | 439 | ## Contribution Guide 440 | 1. Make all changes on the `develop` branch. 441 | 2. Update the demo page to showcase new APIs or features. 442 | 3. Add unit tests. 443 | 4. Update this README if necessary. 444 | 5. Submit a PR! 445 | 446 | ## Changelog 447 | Changes for each release will be documented [here](https://github.com/cngu/vue-typer/releases). 448 | 449 | ## TODO 450 | - Update to latest webpack 451 | - Remove Bootstrap usage in demo app 452 | - Consider marking lodash.split as a peer dependency via webpack externals (webpack-node-externals may be overkill?) 453 | - Revisit community discussions around the best way to obtain deterministic hashes so we can remove HashedModuleIdsPlugin 454 | - Potential features (pull requests are welcome!): 455 | - start typing only when VueTyper is on-screen; potentially pause typing when off-screen 456 | - smarter typing algorithm: erase only up to the longest common starting substring 457 | - is it worth it to eliminate time-drifting from setInterval? If so, it could be a self-correcting interval (implemented as a series of timeouts) 458 | - See submitted [feature requests](https://github.com/cngu/vue-typer/issues?q=is%3Aissue+is%3Aopen+label%3A%22feature+request%22) 459 | - Vue documentation considers rendering-specific tests to still be 'unit' tests. Should we split this out into 'integration' tests? 460 | 461 | ## License 462 | 463 | [MIT](http://opensource.org/licenses/MIT) 464 | 465 | Copyright © 2016-Present, Chris Nguyen. All rights reserved. 466 | -------------------------------------------------------------------------------- /RELEASE.md: -------------------------------------------------------------------------------- 1 | 0. Ensure that `dist/` and `index.html` are not committed on `develop`. 2 | ```bash 3 | npm run clean 4 | ``` 5 | 6 | 1. Update version in package.json on `develop`. 7 | 8 | 2. Create RC commit on `develop` with new version number: 9 | ```bash 10 | git add package.json 11 | git commit -m "RC MAJOR.MINOR.PATCH[ - optional message]" 12 | git push 13 | ``` 14 | 15 | 3. Switch to `master`: 16 | ```bash 17 | git checkout master 18 | ``` 19 | 20 | 4. Merge `develop` into `master`: 21 | ```bash 22 | git merge --no-commit develop 23 | ``` 24 | 25 | 5. Test out the demo page locally: 26 | ```bash 27 | npm run dev 28 | ``` 29 | 30 | 6. Build new package and demo page, and ensure all tests pass: 31 | ```bash 32 | npm run build 33 | ``` 34 | 35 | 7. Ensure that the following pieces of information are still correct in the auto-generated comment at the top of vue-typer.js and vue-typer.min.js: 36 | - Package name 37 | - Version number 38 | - Copyright date 39 | - License 40 | 41 | 8. Ensure that the paths in `index.html` correctly point to the built files in `dist/` 42 | 43 | 9. Commit and tag with the same message: 44 | ```bash 45 | git add dist index.html 46 | git commit -m "[release] vMAJOR.MINOR.PATCH" 47 | git tag -a vMAJOR.MINOR.PATCH -m "[release] vMAJOR.MINOR.PATCH" 48 | ``` 49 | 50 | 10. Publish to npm: 51 | ```bash 52 | npm publish 53 | ``` 54 | 55 | 11. Push to github: 56 | ```bash 57 | git push --follow-tags 58 | ``` 59 | 60 | 12. Add release notes on Github. 61 | -------------------------------------------------------------------------------- /build/loader-util.js: -------------------------------------------------------------------------------- 1 | var ExtractTextPlugin = require('extract-text-webpack-plugin') 2 | var pathUtil = require('./path-util.js') 3 | 4 | var vueTyperStyles = pathUtil.getPathFromRoot('src/vue-typer/styles') 5 | var demoStyles = pathUtil.getPathFromRoot('src/demo/styles') 6 | 7 | var cssLoader = 'css-loader?minimize' 8 | var sassLoader = `sass-loader?includePaths[]=${vueTyperStyles}&includePaths[]=${demoStyles}` 9 | 10 | module.exports = { 11 | getCssLoader: function() { 12 | if (process.env.NODE_ENV === 'production') { 13 | return `style-loader!${cssLoader}` 14 | } 15 | return ExtractTextPlugin.extract({ 16 | use: cssLoader, 17 | fallback: 'style-loader' 18 | }) 19 | }, 20 | getScssLoader: function() { 21 | if (process.env.NODE_ENV === 'production') { 22 | return `style-loader!${cssLoader}!${sassLoader}` 23 | } 24 | return ExtractTextPlugin.extract({ 25 | use: [cssLoader, `${sassLoader}`], 26 | fallback: 'style-loader' 27 | }) 28 | }, 29 | getVueCssLoader: function() { 30 | if (process.env.NODE_ENV === 'production') { 31 | return `vue-style-loader!${cssLoader}` 32 | } 33 | return ExtractTextPlugin.extract({ 34 | use: cssLoader, 35 | fallback: 'vue-style-loader' 36 | }) 37 | }, 38 | getVueScssLoader: function() { 39 | if (process.env.NODE_ENV === 'production') { 40 | return `vue-style-loader!${cssLoader}!${sassLoader}` 41 | } 42 | return ExtractTextPlugin.extract({ 43 | use: [cssLoader, `${sassLoader}`] 44 | }) 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /build/path-util.js: -------------------------------------------------------------------------------- 1 | var path = require('path') 2 | var projectRoot = path.resolve(__dirname, '../') 3 | 4 | module.exports = { 5 | getRoot: function() { 6 | return projectRoot 7 | }, 8 | getPathFromRoot: function(relativePath) { 9 | return path.join(projectRoot, relativePath) 10 | }, 11 | getPathsFromRoot: function(relativePaths) { 12 | return relativePaths.map(this.getPathFromRoot) 13 | }, 14 | getLibPath: function(project, libName) { 15 | return path.join(projectRoot, `src/${project}/lib/${libName}/index.js`) 16 | }, 17 | getPublicImageAssetPath: function() { 18 | return process.env.NODE_ENV === 'development' ? '/' : 'dist/' 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /build/template.index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | VueTyper Demo 8 | 9 | 10 | 11 |
12 | 13 | 14 | -------------------------------------------------------------------------------- /build/uglify.config.base.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | test: /\.min\.js$/, 3 | // Must set this explicitly in webpack 2: https://github.com/shama/webpack-stream/issues/81#issuecomment-243436780 4 | sourceMap: true 5 | } 6 | -------------------------------------------------------------------------------- /build/webpack.config.base.js: -------------------------------------------------------------------------------- 1 | var webpack = require('webpack') 2 | var ExtractTextPlugin = require('extract-text-webpack-plugin') 3 | var autoprefixer = require('autoprefixer') 4 | var pathUtil = require('./path-util') 5 | var loaderUtil = require('./loader-util') 6 | 7 | const config = { 8 | output: { 9 | path: pathUtil.getPathFromRoot('dist') 10 | }, 11 | resolve: { 12 | extensions: ['.js', '.vue'] 13 | }, 14 | module: { 15 | rules: [ 16 | { 17 | enforce: 'pre', 18 | test: /\.(js|vue)$/, 19 | loader: 'eslint-loader', 20 | include: pathUtil.getPathsFromRoot(['src', 'test']), 21 | options: { 22 | formatter: require('eslint-friendly-formatter') 23 | } 24 | }, 25 | { 26 | test: /\.vue$/, 27 | loader: 'vue-loader', 28 | include: pathUtil.getPathsFromRoot(['src', 'test']), 29 | options: { 30 | loaders: { 31 | css: loaderUtil.getVueCssLoader(), 32 | scss: loaderUtil.getVueScssLoader() 33 | }, 34 | postcss: [ 35 | autoprefixer({ browsers: ['last 2 versions'] }) 36 | ] 37 | } 38 | }, 39 | { 40 | test: /\.js$/, 41 | loader: 'babel-loader', 42 | include: pathUtil.getPathsFromRoot(['src', 'test']) 43 | }, 44 | { 45 | test: /\.css$/, 46 | loader: loaderUtil.getCssLoader(), 47 | include: pathUtil.getPathsFromRoot(['src', 'test']) 48 | }, 49 | { 50 | test: /\.scss$/, 51 | loader: loaderUtil.getScssLoader(), 52 | include: pathUtil.getPathsFromRoot(['src', 'test']) 53 | }, 54 | { 55 | test: /\.png$/, 56 | include: pathUtil.getPathFromRoot('src/demo/assets/images'), 57 | loader: 'url-loader', 58 | options: { 59 | name: '[name].[hash].[ext]', 60 | limit: 10000, 61 | publicPath: pathUtil.getPublicImageAssetPath() 62 | } 63 | } 64 | ] 65 | }, 66 | plugins: [ 67 | // http://vuejs.github.io/vue-loader/en/workflow/production.html 68 | new webpack.DefinePlugin({ 69 | 'process.env': { 70 | NODE_ENV: JSON.stringify(process.env.NODE_ENV) 71 | } 72 | }), 73 | new webpack.NoEmitOnErrorsPlugin() 74 | ] 75 | } 76 | 77 | // Only demo builds require ExtractTextPlugin to allow style caching separately from the bundle. 78 | // In the production build, we must bundle the CSS along with the code. 79 | if (process.env.NODE_ENV !== 'production') { 80 | config.plugins.push(new ExtractTextPlugin('[name].[contenthash].min.css')) 81 | } 82 | 83 | module.exports = config 84 | -------------------------------------------------------------------------------- /build/webpack.config.demo.js: -------------------------------------------------------------------------------- 1 | var webpack = require('webpack') 2 | var merge = require('webpack-merge') 3 | var HtmlWebpackPlugin = require('html-webpack-plugin') 4 | 5 | var pathUtil = require('./path-util') 6 | var baseWebpackConfig = require('./webpack.config.base') 7 | var baseUglifyConfig = require('./uglify.config.base') 8 | 9 | module.exports = merge(baseWebpackConfig, { 10 | entry: { 11 | demo: pathUtil.getPathFromRoot('src/demo/index.js'), 12 | vendor: ['vue', pathUtil.getLibPath('demo', 'bootstrap'), pathUtil.getLibPath('demo', 'prism')] 13 | }, 14 | output: { 15 | filename: '[name].[chunkhash].min.js' 16 | }, 17 | devtool: '#source-map', 18 | plugins: [ 19 | new webpack.optimize.CommonsChunkPlugin({ 20 | name: ['vendor', 'manifest'] 21 | }), 22 | // Replace module IDs with the hash of the module pathnames. This solves the issue 23 | // where module IDs change between builds when code-changes in one bundle and cause 24 | // other bundles to be re-emitted even if their code hasn't changed, e.g. importing 25 | // a new module in demo.js causes a new vendor.js bundle to be emitted even if no 26 | // vendor code has changed, and vice versa. Note that this slightly increases the 27 | // size of the bundle, as the hashes are longer than IDs. 28 | // https://github.com/webpack/webpack/issues/1315 29 | // An alternative is NamedModulesPlugin, but this leaks/exposes the pathnames. 30 | new webpack.HashedModuleIdsPlugin(), 31 | new webpack.optimize.UglifyJsPlugin(baseUglifyConfig), 32 | new webpack.optimize.OccurrenceOrderPlugin(), 33 | new HtmlWebpackPlugin({ 34 | title: 'vue-typer demo', 35 | template: 'build/template.index.html', 36 | filename: '../index.html', 37 | inject: 'body', 38 | minify: { 39 | removeComments: true, 40 | collapseWhitespace: false, 41 | removeAttributeQuotes: false 42 | }, 43 | // Necessary to consistently work with multiple chunks via CommonsChunkPlugin 44 | chunksSortMode: 'dependency' 45 | }) 46 | ] 47 | }) -------------------------------------------------------------------------------- /build/webpack.config.dev.js: -------------------------------------------------------------------------------- 1 | var webpack = require('webpack') 2 | var merge = require('webpack-merge') 3 | var HtmlWebpackPlugin = require('html-webpack-plugin') 4 | 5 | var pathUtil = require('./path-util') 6 | var baseWebpackConfig = require('./webpack.config.base') 7 | 8 | module.exports = merge(baseWebpackConfig, { 9 | entry: { 10 | demo: pathUtil.getPathFromRoot('src/demo/index.js'), 11 | vendor: ['vue', pathUtil.getLibPath('demo', 'bootstrap'), pathUtil.getLibPath('demo', 'prism')] 12 | }, 13 | output: { 14 | filename: '[name].js', 15 | publicPath: "/" 16 | }, 17 | // Ideally #eval-source-map, but breakpoints don't work: https://github.com/webpack/webpack/issues/2145 18 | devtool: '#source-map', 19 | plugins: [ 20 | new webpack.optimize.CommonsChunkPlugin({ 21 | name: ['vendor', 'manifest'] 22 | }), 23 | new HtmlWebpackPlugin({ 24 | title: 'vue-typer demo dev server', 25 | template: 'build/template.index.html', 26 | inject: 'body' 27 | }) 28 | ] 29 | }) -------------------------------------------------------------------------------- /build/webpack.config.prod.js: -------------------------------------------------------------------------------- 1 | var webpack = require('webpack') 2 | var merge = require('webpack-merge') 3 | var HtmlWebpackPlugin = require('html-webpack-plugin') 4 | 5 | var pathUtil = require('./path-util') 6 | var baseWebpackConfig = require('./webpack.config.base') 7 | var baseUglifyConfig = require('./uglify.config.base') 8 | 9 | var packageJson = require('../package.json') 10 | var nameAndVersion = packageJson.name + ' v' + packageJson.version 11 | var copyright = 'Copyright 2016-' + new Date().getFullYear() + ' ' + packageJson.author 12 | var license = 'Released under the ' + packageJson.license + ' license.' 13 | var bannerComment = nameAndVersion + '\n' + copyright + '\n' + license 14 | 15 | module.exports = merge(baseWebpackConfig, { 16 | entry: { 17 | 'vue-typer': pathUtil.getPathFromRoot('src/vue-typer/index.js'), 18 | 'vue-typer.min': pathUtil.getPathFromRoot('src/vue-typer/index.js') 19 | }, 20 | output: { 21 | filename: '[name].js', 22 | library: 'VueTyper', 23 | libraryTarget: 'umd' 24 | }, 25 | // devtool: '#source-map', 26 | plugins: [ 27 | // http://vuejs.github.io/vue-loader/en/workflow/production.html 28 | new webpack.optimize.UglifyJsPlugin(baseUglifyConfig), 29 | new webpack.optimize.OccurrenceOrderPlugin(), 30 | new webpack.BannerPlugin(bannerComment) 31 | ] 32 | }) -------------------------------------------------------------------------------- /build/webpack.config.test.js: -------------------------------------------------------------------------------- 1 | var webpack = require('webpack') 2 | var merge = require('webpack-merge') 3 | var pathUtil = require('./path-util.js') 4 | var baseConfig = require('./webpack.config.base') 5 | 6 | var webpackConfig = merge(baseConfig, { 7 | resolve: { 8 | alias: { 9 | // We want to use the standalone build for tests so we can use the 'template' option 10 | 'vue$': 'vue/dist/vue.common.js' 11 | } 12 | }, 13 | // use eval for karma-sourcemap-loader 14 | devtool: '#eval' 15 | }) 16 | 17 | module.exports = webpackConfig 18 | -------------------------------------------------------------------------------- /dist/demo.1320e4785106378b9e121f0ec76a34aa.min.css: -------------------------------------------------------------------------------- 1 | html{height:100%;-webkit-font-smoothing:antialiased}html body{height:100%;margin:0;background:#eee}.demo #output-panel .demo-typer-container[data-v-6d53ee08]{height:100%}.demo #text-panel .form-group[data-v-6d53ee08]{margin-bottom:0}.demo #text-panel .form-group textarea[data-v-6d53ee08]{width:100%}.demo .shrink-text[data-v-6d53ee08]{font-size:.9rem}@keyframes rocking{0%,to{transform:rotate(-10deg)}50%{transform:rotate(10deg)}}main .state-typer{font-family:Arial,Helvetica Neue,Helvetica,sans-serif}main .state-typer .custom.char.typed{color:#009688}main .state-typer .custom.char.selected{color:#e91e63}main .state-typer .custom.caret{animation:rocking 1s ease-in-out 0s infinite}main .state-typer .custom.caret.typing{background-color:#009688}main .state-typer .custom.caret.selecting{display:inline-block;background-color:#e91e63}main .code-typer{font-family:monospace}main .code-typer .custom.char{color:#d4d4bd;background-color:#1e1e1e}main .code-typer .custom.char.selected{background-color:#264f78}main .code-typer .custom.caret{width:10px;background-color:#3f51b5}main .ghost-typer{font-family:Copperplate,Copperplate Gothic Light,fantasy}main .ghost-typer .custom.char.typed{color:#607d8b}main .ghost-typer .custom.char.selected{color:#607d8b;background-color:transparent;text-decoration:line-through}main .ghost-typer .custom.caret{display:none}span.vue-typer[data-v-c41bac74]{cursor:default;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}span.vue-typer span.left[data-v-c41bac74],span.vue-typer span.right[data-v-c41bac74]{display:inline}@keyframes vue-typer-caret-blink{50%{opacity:0}to{opacity:1}}@keyframes vue-typer-caret-smooth{0%,20%{opacity:1}60%,to{opacity:0}}@keyframes vue-typer-caret-phase{0%,20%{opacity:1}90%,to{opacity:0}}@keyframes vue-typer-caret-expand{0%,20%{transform:scaleY(1)}80%,to{transform:scaleY(0)}}.vue-typer-caret-blink[data-v-a16e0f02]{animation:vue-typer-caret-blink 1s step-start 0s infinite}.vue-typer-caret-smooth[data-v-a16e0f02]{animation:vue-typer-caret-smooth .5s ease-in-out 0s infinite alternate}.vue-typer-caret-phase[data-v-a16e0f02]{animation:vue-typer-caret-phase .5s ease-in-out 0s infinite alternate}.vue-typer-caret-expand[data-v-a16e0f02]{animation:vue-typer-caret-expand .5s ease-in-out 0s infinite alternate}span.caret[data-v-a16e0f02]:empty:before{content:"\200B"}span[data-v-a16e0f02]{display:inline-block;width:1px}.idle[data-v-a16e0f02],.typing[data-v-a16e0f02]{background-color:#000}.selecting[data-v-a16e0f02]{display:none;background-color:#000}.erasing[data-v-a16e0f02]{background-color:#000}.complete[data-v-a16e0f02]{display:none;background-color:#000}.char[data-v-302772ec]{display:inline-block;white-space:pre-wrap}.newline[data-v-302772ec]{display:inline}.typed[data-v-302772ec]{color:#000;background-color:transparent}.selected[data-v-302772ec]{color:#000;background-color:#accef7}.erased[data-v-302772ec]{display:none}.app-layout main[data-v-322ce952]{margin-bottom:8rem}.app-layout main #playground[data-v-322ce952]{margin-bottom:4rem}.app-layout main h4[data-v-322ce952]{height:1.1em;margin-bottom:1em;color:#35495e}.app-layout main .card[data-v-322ce952]{padding:1em;margin-bottom:0}header[data-v-d376d9aa]{display:-ms-flexbox;display:flex;-ms-flex-direction:column;flex-direction:column;-ms-flex-align:center;align-items:center;color:#fff;background:#35495e;padding:2rem 0}header h1[data-v-d376d9aa]{margin-bottom:2rem}header .link-bar[data-v-d376d9aa]{margin-bottom:1rem}header h1 .title-typer{font-family:Source Sans Pro,Helvetica Neue,Arial,sans-serif;font-weight:300}header h1 .title-typer .custom.char{color:#fff}header h1 .title-typer .custom.caret{background-color:#fff}header h1 .title-typer .custom.caret.complete{display:inline-block}.link-bar img[data-v-9cbc8336]{width:50px;height:50px;padding:8px}a[data-v-4ed29124]{position:relative;display:inline-block;margin:15px 30px;cursor:pointer;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;border-radius:50%;transition:box-shadow .2s}a[data-v-4ed29124]:after{content:"";position:absolute;top:0;left:0;width:100%;height:100%;padding:0;border-radius:50%;box-shadow:0 0 0 2px #fff;transition:transform .2s,opacity .2s}a[data-v-4ed29124]:hover{box-shadow:0 0 0 4px #fff}a[data-v-4ed29124]:hover:after{transform:scale(.8);opacity:.5}.badge-bar a[data-v-6512f7f4]{margin:0 3px}footer[data-v-0582c86e]{padding:2rem;color:#fff;background:#41b883;text-align:center}footer a[data-v-0582c86e]{color:#fff;font-weight:700;text-decoration:none}.shift-up[data-v-8e4ab660]{margin-top:-2px}.col-form-label[data-v-06d6e692]{padding-top:.4rem}.col-lg-6[data-v-67442243]{padding-right:0} 2 | /*# sourceMappingURL=demo.1320e4785106378b9e121f0ec76a34aa.min.css.map*/ -------------------------------------------------------------------------------- /dist/demo.ecd3fe850e3573d9d50a.min.js: -------------------------------------------------------------------------------- 1 | webpackJsonp([0],{"+aK1":function(e,t){},"0T+P":function(e,t,n){n("i3yy");var r=n("VU/8")(n("mti1"),n("bC94"),"data-v-06d6e692",null);r.options.__file="/Users/cngu/dev/vue-typer/src/demo/components/FormInput.vue",r.esModule&&Object.keys(r.esModule).some(function(e){return"default"!==e&&"__esModule"!==e})&&console.error("named exports are not supported in *.vue files."),r.options.functional&&console.error("[vue-loader] FormInput.vue: functional components are not supported with templates, they should use render functions."),e.exports=r.exports},"0aME":function(e,t,n){n("j57+");var r=n("VU/8")(n("erKa"),n("OepV"),"data-v-42c2d6cc",null);r.options.__file="/Users/cngu/dev/vue-typer/src/demo/components/CodeBlock.vue",r.esModule&&Object.keys(r.esModule).some(function(e){return"default"!==e&&"__esModule"!==e})&&console.error("named exports are not supported in *.vue files."),r.options.functional&&console.error("[vue-loader] CodeBlock.vue: functional components are not supported with templates, they should use render functions."),e.exports=r.exports},"1JWU":function(e,t){e.exports="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAGAAAABgAgMAAACf9p+rAAAADFBMVEUAAAD///////////84wDuoAAAABHRSTlMA/wCA4LPRVwAAAIhJREFUeAHtzbEJw0AMQFEveaTLIBlJQqVG0RKeIhAwvNpNMNxvX/GPG+3Wr7g64uombNgwi15AC2+ghA+Qwul8XAjtQigXQroQYlwI7UIoF0K6EGJcCO1CKBdCuhBiXAjtQigXQroQYlwI7UIoF0K6EGJYhNAsQigWISQLwR4Ka9kzgWzDX+AL65Wl6DEWiToAAAAASUVORK5CYII="},"1osy":function(e,t,n){n("Y9A6");var r=n("VU/8")(n("pd3l"),n("GqHW"),"data-v-c41bac74",null);r.options.__file="/Users/cngu/dev/vue-typer/src/vue-typer/components/VueTyper.vue",r.esModule&&Object.keys(r.esModule).some(function(e){return"default"!==e&&"__esModule"!==e})&&console.error("named exports are not supported in *.vue files."),r.options.functional&&console.error("[vue-loader] VueTyper.vue: functional components are not supported with templates, they should use render functions."),e.exports=r.exports},"2vxi":function(e,t,n){n("Gk9R");var r=n("VU/8")(n("Awc8"),n("zf0g"),"data-v-302772ec",null);r.options.__file="/Users/cngu/dev/vue-typer/src/vue-typer/components/Char.vue",r.esModule&&Object.keys(r.esModule).some(function(e){return"default"!==e&&"__esModule"!==e})&&console.error("named exports are not supported in *.vue files."),r.options.functional&&console.error("[vue-loader] Char.vue: functional components are not supported with templates, they should use render functions."),e.exports=r.exports},"30KW":function(e,t,n){var r=n("kM2E"),o=n("7KvD").isFinite;r(r.S,"Number",{isFinite:function(e){return"number"==typeof e&&o(e)}})},"3IRH":function(e,t){e.exports=function(e){return e.webpackPolyfill||(e.deprecate=function(){},e.paths=[],e.children||(e.children=[]),Object.defineProperty(e,"loaded",{enumerable:!0,get:function(){return e.l}}),Object.defineProperty(e,"id",{enumerable:!0,get:function(){return e.i}}),e.webpackPolyfill=1),e}},"4Vle":function(e,t){},"59Ki":function(e,t){e.exports="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAGAAAABgCAQAAABIkb+zAAABQUlEQVR4Ae3VPUpDQRxF8ekS3IoIohvMFiRBg+hWtHEFFoG4C7v3/JhrOaWBMy+XgXtuO80PBv4lpZRSOiVd60FHzaJtVHrutGdrPaoKRAgcsNarQJTAAffiUQIAXKpKdgIA3InHCQDwrj5tEAEAPtWngggAMPUCIAIAzB0BnGAHcIIdwAl2ACfYAZxgB3CCHYAJBgAgAMBXfwAn+AGc4Adwgh/ACX4AJfgBkOAHQIIf8B8BA77PCGiEYQGNMCygEfwAXgAB8AL4CSCAAJYsgAAK3OKAXw4IIIAAAgjAFgfUAFABBBBAAKMfMnFAAAEEEEAAvgIIIIAAvAUwy9nEAR9yduCAvZxtOeBGvqquOKDoSa52Kj0AF3qToxetKKARnnXeqnZaqXBA2632OmrW0k06aNv+PgCMsQACQAsggAD+AOE0ggxCDzgSAAAAAElFTkSuQmCC"},"5C9N":function(e,t,n){e.exports={render:function(){var e=this,t=e.$createElement,n=e._self._c||t;return n("div",{staticClass:"app-layout"},[e._t("header"),n("main",{staticClass:"container"},[n("section",{staticClass:"row",attrs:{id:"playground"}},[n("h4",{staticClass:"col-xs-12 text-xs-center"},[e._v("VueTyper Playground")]),n("div",{staticClass:"card col-xs-12 col-lg-6",attrs:{id:"output-panel"}},[e._t("main-playground-output")],2),n("div",{staticClass:"card col-xs-12 col-lg-6",attrs:{id:"text-panel"}},[e._t("main-playground-text")],2),n("div",{staticClass:"card col-xs-12 col-lg-6",attrs:{id:"config-panel"}},[e._t("main-playground-config")],2),n("div",{staticClass:"card col-xs-12 col-lg-6",attrs:{id:"code-panel"}},[e._t("main-playground-code")],2)]),n("section",{staticClass:"row",attrs:{id:"style-showcase"}},[n("h4",{staticClass:"col-xs-12 text-xs-center"},[e._v("VueTyper is also stylable with CSS!")]),n("div",{staticClass:"col-xs"},[n("div",{staticClass:"row"},[n("div",{staticClass:"card col-xs-12 col-lg-4"},[e._t("style-showcase-panel-1")],2),n("div",{staticClass:"card col-xs-12 col-lg-4"},[e._t("style-showcase-panel-2")],2),n("div",{staticClass:"card col-xs-12 col-lg-4"},[e._t("style-showcase-panel-3")],2)])])])]),e._t("footer")],2)},staticRenderFns:[]},e.exports.render._withStripped=!0},"5fl1":function(e,t,n){n("DpAd");var r=n("VU/8")(n("GAYh"),n("tfFV"),"data-v-4ed29124",null);r.options.__file="/Users/cngu/dev/vue-typer/src/demo/components/CircleLink.vue",r.esModule&&Object.keys(r.esModule).some(function(e){return"default"!==e&&"__esModule"!==e})&&console.error("named exports are not supported in *.vue files."),r.options.functional&&console.error("[vue-loader] CircleLink.vue: functional components are not supported with templates, they should use render functions."),e.exports=r.exports},"5jpQ":function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.default={props:{value:String,label:String,options:Array,model:String},computed:{localModel:function(){return this.model}}}},"6qCy":function(e,t,n){n("BAUM");var r=n("VU/8")(n("SJM8"),n("LIZD"),"data-v-a16e0f02",null);r.options.__file="/Users/cngu/dev/vue-typer/src/vue-typer/components/Caret.vue",r.esModule&&Object.keys(r.esModule).some(function(e){return"default"!==e&&"__esModule"!==e})&&console.error("named exports are not supported in *.vue files."),r.options.functional&&console.error("[vue-loader] Caret.vue: functional components are not supported with templates, they should use render functions."),e.exports=r.exports},"91BW":function(e,t,n){n("Zsqw"),n("RMxe");var r=n("VU/8")(n("NWVg"),n("XPSn"),"data-v-6d53ee08",null);r.options.__file="/Users/cngu/dev/vue-typer/src/demo/Demo.vue",r.esModule&&Object.keys(r.esModule).some(function(e){return"default"!==e&&"__esModule"!==e})&&console.error("named exports are not supported in *.vue files."),r.options.functional&&console.error("[vue-loader] Demo.vue: functional components are not supported with templates, they should use render functions."),e.exports=r.exports},"9dTX":function(e,t,n){e.exports={render:function(){var e=this,t=e.$createElement;e._self._c;return e._m(0)},staticRenderFns:[function(){var e=this,t=e.$createElement,n=e._self._c||t;return n("footer",[n("small",[e._v("Released under the "),n("a",{attrs:{href:"https://opensource.org/licenses/MIT",target:"_blank"}},[e._v("MIT License")]),n("br"),e._v("Copyright © 2016-2017 Chris Nguyen")])])}]},e.exports.render._withStripped=!0},AMV0:function(e,t,n){e.exports={default:n("k2Ib"),__esModule:!0}},Awc8:function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.default={props:{val:{type:String,default:""}},computed:{classes:function(){return{newline:0===this.val.indexOf("\n")}}}}},BAUM:function(e,t){},C47m:function(e,t,n){n("xT9Q");var r=n("VU/8")(null,n("9dTX"),"data-v-0582c86e",null);r.options.__file="/Users/cngu/dev/vue-typer/src/demo/components/CopyrightFooter.vue",r.esModule&&Object.keys(r.esModule).some(function(e){return"default"!==e&&"__esModule"!==e})&&console.error("named exports are not supported in *.vue files."),r.options.functional&&console.error("[vue-loader] CopyrightFooter.vue: functional components are not supported with templates, they should use render functions."),e.exports=r.exports},"CxC/":function(e,t,n){"use strict";function r(e){return e&&e.__esModule?e:{default:e}}n("oJ8v");var o=n("/5sW"),s=r(o),a=n("91BW"),i=r(a);new s.default({el:"#demo",render:function(e){return e(i.default)}})},DE6i:function(e,t,n){n("+aK1");var r=n("VU/8")(n("Z4s0"),n("RQdl"),"data-v-9cbc8336",null);r.options.__file="/Users/cngu/dev/vue-typer/src/demo/components/LinkBar.vue",r.esModule&&Object.keys(r.esModule).some(function(e){return"default"!==e&&"__esModule"!==e})&&console.error("named exports are not supported in *.vue files."),r.options.functional&&console.error("[vue-loader] LinkBar.vue: functional components are not supported with templates, they should use render functions."),e.exports=r.exports},DpAd:function(e,t){},E6Cn:function(e,t,n){n("zGu/"),n("iWFa");var r=n("VU/8")(n("L7bS"),n("NYV3"),"data-v-d376d9aa",null);r.options.__file="/Users/cngu/dev/vue-typer/src/demo/components/HeroHeader.vue",r.esModule&&Object.keys(r.esModule).some(function(e){return"default"!==e&&"__esModule"!==e})&&console.error("named exports are not supported in *.vue files."),r.options.functional&&console.error("[vue-loader] HeroHeader.vue: functional components are not supported with templates, they should use render functions."),e.exports=r.exports},E8JD:function(e,t,n){n("T/1P");var r=n("VU/8")(n("ZKVD"),n("K/7M"),"data-v-8e4ab660",null);r.options.__file="/Users/cngu/dev/vue-typer/src/demo/components/FormCheck.vue",r.esModule&&Object.keys(r.esModule).some(function(e){return"default"!==e&&"__esModule"!==e})&&console.error("named exports are not supported in *.vue files."),r.options.functional&&console.error("[vue-loader] FormCheck.vue: functional components are not supported with templates, they should use render functions."),e.exports=r.exports},Ejat:function(e,t,n){n("MytS");var r=n("VU/8")(null,n("R+mQ"),"data-v-6512f7f4",null);r.options.__file="/Users/cngu/dev/vue-typer/src/demo/components/BadgeBar.vue",r.esModule&&Object.keys(r.esModule).some(function(e){return"default"!==e&&"__esModule"!==e})&&console.error("named exports are not supported in *.vue files."),r.options.functional&&console.error("[vue-loader] BadgeBar.vue: functional components are not supported with templates, they should use render functions."),e.exports=r.exports},GAYh:function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.default={props:{href:String}}},Gk9R:function(e,t){},GqHW:function(e,t,n){e.exports={render:function(){var e=this,t=e.$createElement,n=e._self._c||t;return n("span",{staticClass:"vue-typer"},[n("span",{staticClass:"left"},e._l(e.numLeftChars,function(t){return n("char",{staticClass:"custom typed",attrs:{val:e.currentTextArray[t-1]}})})),n("caret",{class:e.caretClasses,attrs:{animation:e.caretAnimation}}),n("span",{staticClass:"right"},e._l(e.numRightChars,function(t){return n("char",{staticClass:"custom",class:e.rightCharClasses,attrs:{val:e.currentTextArray[e.numLeftChars+t-1]}})}))],1)},staticRenderFns:[]},e.exports.render._withStripped=!0},H73T:function(e,t,n){"use strict";function r(e){return e&&e.__esModule?e:{default:e}}function o(e){return"number"==typeof e&&!(0,u.default)(e)&&(0,i.default)(e)}function s(e,t){return o(e)&&o(t)&&e<=t}Object.defineProperty(t,"__esModule",{value:!0});var a=n("AMV0"),i=r(a),l=n("MICi"),u=r(l);t.default=function(e,t){return s(e,t)?(e=Math.ceil(e),t=Math.floor(t),Math.floor(Math.random()*(t-e+1))+e):-1}},"K/7M":function(e,t,n){e.exports={render:function(){var e=this,t=e.$createElement,n=e._self._c||t;return n("div",{staticClass:"form-group row flex-items-xs-center"},[n("label",{staticClass:"col-xs-4 col-md-3 col-lg-6 flex-xs-middle shift-up",attrs:{for:e.label}},[e._v(e._s(e.label))]),n("div",{staticClass:"col-xs-4 col-md-3 col-lg-6"},[n("div",{staticClass:"form-check"},[n("label",{staticClass:"form-check-label"},[n("input",{staticClass:"form-check-input",attrs:{id:e.label,type:"checkbox"},domProps:{value:e.value},on:{change:function(t){e.$emit("input",t.target.checked)}}})])])])])},staticRenderFns:[]},e.exports.render._withStripped=!0},K4R9:function(e,t,n){n("NA/8"),e.exports=n("FeBl").Number.isNaN},L7bS:function(e,t,n){"use strict";function r(e){return e&&e.__esModule?e:{default:e}}Object.defineProperty(t,"__esModule",{value:!0});var o=n("bE5q"),s=n("DE6i"),a=r(s),i=n("Ejat"),l=r(i);t.default={components:{VueTyper:o.VueTyper,LinkBar:a.default,BadgeBar:l.default}}},LIZD:function(e,t,n){e.exports={render:function(){var e=this,t=e.$createElement;return(e._self._c||t)("span",{staticClass:"caret custom",class:e.animationClass})},staticRenderFns:[]},e.exports.render._withStripped=!0},LwYq:function(e,t,n){n("i9x0");var r=n("VU/8")(null,n("5C9N"),"data-v-322ce952",null);r.options.__file="/Users/cngu/dev/vue-typer/src/demo/components/AppLayout.vue",r.esModule&&Object.keys(r.esModule).some(function(e){return"default"!==e&&"__esModule"!==e})&&console.error("named exports are not supported in *.vue files."),r.options.functional&&console.error("[vue-loader] AppLayout.vue: functional components are not supported with templates, they should use render functions."),e.exports=r.exports},MICi:function(e,t,n){e.exports={default:n("K4R9"),__esModule:!0}},MytS:function(e,t){},"NA/8":function(e,t,n){var r=n("kM2E");r(r.S,"Number",{isNaN:function(e){return e!=e}})},NWVg:function(e,t,n){"use strict";function r(e){return e&&e.__esModule?e:{default:e}}Object.defineProperty(t,"__esModule",{value:!0});var o=n("MICi"),s=r(o),a=n("bE5q"),i=n("LwYq"),l=r(i),u=n("E6Cn"),c=r(u),p=n("C47m"),d=r(p),f=n("E8JD"),h=r(f),m=n("0T+P"),y=r(m),v=n("ue+v"),x=r(v),g=n("0aME"),A=r(g);t.default={components:{VueTyper:a.VueTyper,AppLayout:l.default,HeroHeader:c.default,CopyrightFooter:d.default,FormCheck:h.default,FormInput:y.default,FormRadio:x.default,CodeBlock:A.default},data:function(){return{textModel:["Arya Stark","Jon Snow","Daenerys Targaryen","Melisandre","Tyrion Lannister"].join("\n"),repeatModel:1/0,shuffle:!1,initialAction:"typing",typeDelay:70,preTypeDelay:70,eraseDelay:250,preEraseDelay:2e3,eraseStyle:"select-all",eraseOnComplete:!1,caretAnimation:"blink"}},computed:{text:function(){return this.textModel.split("\n")},repeat:function(){var e=parseInt(this.repeatModel);return(0,s.default)(e)?1/0:e},playgroundDemoCode:function(){return"\n \n "},stateDemoStyleCode:function(){return"\n @keyframes rocking {\n 0%,100% {transform: rotateZ(-10deg);},\n 50% {transform: rotateZ(10deg);}\n }\n\n .vue-typer {\n font-family: Arial, 'Helvetica Neue', Helvetica, sans-serif;\n }\n .vue-typer .custom.char.typed {\n color: #009688;\n }\n .vue-typer .custom.char.selected {\n color: #E91E63;\n }\n\n .vue-typer .custom.caret {\n animation: rocking 1s ease-in-out 0s infinite;\n }\n .vue-typer .custom.caret.typing {\n background-color: #009688;\n }\n .vue-typer .custom.caret.selecting {\n display: inline-block;\n background-color: #E91E63;\n }\n "},codeDemoStyleCode:function(){return"\n .vue-typer {\n font-family: monospace;\n }\n\n .vue-typer .custom.char {\n color: #D4D4BD;\n background-color: #1E1E1E;\n }\n .vue-typer .custom.char.selected {\n background-color: #264F78;\n }\n\n .vue-typer .custom.caret {\n width: 10px;\n background-color: #3F51B5;\n }\n "},ghostDemoStyleCode:function(){return"\n .vue-typer {\n font-family: Copperplate, 'Copperplate Gothic Light', fantasy;\n }\n\n .vue-typer .custom.char.typed {\n color: #607D8B;\n }\n .vue-typer .custom.char.selected {\n color: #607D8B;\n background-color: transparent;\n text-decoration: line-through;\n }\n\n .vue-typer .custom.caret {\n display: none;\n }\n "}}}},NYV3:function(e,t,n){e.exports={render:function(){var e=this,t=e.$createElement,n=e._self._c||t;return n("header",{staticClass:"jumbotron jumbotron-fluid"},[n("h1",{staticClass:"display-4"},[n("vue-typer",{staticClass:"title-typer",attrs:{text:"VueTyper",repeat:0,"pre-type-delay":1e3,"type-delay":400,"caret-animation":"smooth"}})],1),n("link-bar",{staticClass:"link-bar"}),n("badge-bar")],1)},staticRenderFns:[]},e.exports.render._withStripped=!0},OepV:function(e,t,n){e.exports={render:function(){var e=this,t=e.$createElement,n=e._self._c||t;return n("pre",{staticClass:"code-block"},[n("code",{ref:"codeBlock",class:e.languageClass})])},staticRenderFns:[]},e.exports.render._withStripped=!0},"R+mQ":function(e,t,n){e.exports={render:function(){var e=this,t=e.$createElement;e._self._c;return e._m(0)},staticRenderFns:[function(){var e=this,t=e.$createElement,n=e._self._c||t;return n("span",{staticClass:"badge-bar"},[n("a",{attrs:{href:"https://www.npmjs.com/package/vue-typer",target:"_blank"}},[n("img",{attrs:{src:"https://img.shields.io/npm/dt/vue-typer.svg",alt:"Downloads"}})]),n("a",{attrs:{href:"https://www.npmjs.com/package/vue-typer",target:"_blank"}},[n("img",{attrs:{src:"https://img.shields.io/npm/v/vue-typer.svg",alt:"Version"}})]),n("a",{attrs:{href:"https://www.npmjs.com/package/vue-typer",target:"_blank"}},[n("img",{attrs:{src:"https://img.shields.io/npm/l/vue-typer.svg",alt:"License"}})])])}]},e.exports.render._withStripped=!0},RMxe:function(e,t){},RQdl:function(e,t,n){e.exports={render:function(){var e=this,t=e.$createElement,r=e._self._c||t;return r("div",{staticClass:"link-bar"},[r("circle-link",{attrs:{href:"https://github.com/cngu/vue-typer"}},[r("img",{attrs:{src:n("oGlG")}})]),r("circle-link",{attrs:{href:"https://github.com/cngu/vue-typer/blob/master/README.md#getting-started"}},[r("img",{attrs:{src:n("59Ki")}})]),r("circle-link",{attrs:{href:"https://api.github.com/repos/cngu/vue-typer/zipball"}},[r("img",{attrs:{src:n("1JWU")}})])],1)},staticRenderFns:[]},e.exports.render._withStripped=!0},SHlX:function(e,t,n){"use strict";function r(e,t,n){if(t!==n){var r=e[t];e[t]=e[n],e[n]=r}}Object.defineProperty(t,"__esModule",{value:!0});var o=n("H73T"),s=function(e){return e&&e.__esModule?e:{default:e}}(o);t.default=function(e){if(e instanceof Array)for(var t=e.length-1;t>0;t--){var n=(0,s.default)(0,t);r(e,t,n)}}},SJM8:function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0});t.default={props:{animation:{type:String,default:"blink",validator:function(e){return/^solid$|^blink$|^smooth$|^phase$|^expand$/.test(e)}}},computed:{animationClass:function(){return"vue-typer-caret-"+this.animation}}}},"T/1P":function(e,t){},"VU/8":function(e,t){e.exports=function(e,t,n,r){var o,s=e=e||{},a=typeof e.default;"object"!==a&&"function"!==a||(o=e,s=e.default);var i="function"==typeof s?s.options:s;if(t&&(i.render=t.render,i.staticRenderFns=t.staticRenderFns),n&&(i._scopeId=n),r){var l=i.computed||(i.computed={});Object.keys(r).forEach(function(e){var t=r[e];l[e]=function(){return t}})}return{esModule:o,exports:s,options:i}}},XPSn:function(e,t,n){e.exports={render:function(){var e=this,t=e.$createElement,n=e._self._c||t;return n("app-layout",{staticClass:"demo"},[n("hero-header",{slot:"header"}),n("template",{slot:"main-playground-output"},[n("h3",{staticClass:"demo-typer-container row flex-items-xs-center flex-items-xs-middle"},[n("vue-typer",{staticClass:"demo-typer",attrs:{text:e.text,repeat:e.repeat,shuffle:e.shuffle,"initial-action":e.initialAction,"pre-type-delay":e.preTypeDelay,"type-delay":e.typeDelay,"pre-erase-delay":e.preEraseDelay,"erase-delay":e.eraseDelay,"erase-style":e.eraseStyle,"erase-on-complete":e.eraseOnComplete,"caret-animation":e.caretAnimation}})],1)]),n("template",{slot:"main-playground-text"},[n("div",{staticClass:"form-group"},[n("label",{attrs:{for:"text"}},[e._v("List of words to type:")]),n("textarea",{directives:[{name:"model",rawName:"v-model",value:e.textModel,expression:"textModel"}],attrs:{id:"text",placeholder:"text",rows:3},domProps:{value:e.textModel},on:{input:function(t){t.target.composing||(e.textModel=t.target.value)}}})])]),n("template",{slot:"main-playground-config"},[n("div",{staticClass:"row"},[n("div",{staticClass:"col-xs-12 col-lg-6",attrs:{id:"general-config"}},[n("form-input",{attrs:{label:"repeat"},model:{value:e.repeatModel,callback:function(t){e.repeatModel=t},expression:"repeatModel"}}),n("form-check",{attrs:{label:"shuffle"},model:{value:e.shuffle,callback:function(t){e.shuffle=t},expression:"shuffle"}}),n("form-check",{staticClass:"shrink-text",attrs:{label:"eraseOnComplete"},model:{value:e.eraseOnComplete,callback:function(t){e.eraseOnComplete=t},expression:"eraseOnComplete"}}),n("form-radio",{attrs:{model:e.initialAction,label:"initialAction",options:["typing","erasing"]},model:{value:e.initialAction,callback:function(t){e.initialAction=t},expression:"initialAction"}})],1),n("div",{staticClass:"col-xs-12 col-lg-6",attrs:{id:"delay-config"}},[n("form-input",{attrs:{label:"preTypeDelay",type:"number"},model:{value:e.preTypeDelay,callback:function(t){e.preTypeDelay=e._n(t)},expression:"preTypeDelay"}}),n("form-input",{attrs:{label:"typeDelay",type:"number"},model:{value:e.typeDelay,callback:function(t){e.typeDelay=e._n(t)},expression:"typeDelay"}}),n("form-input",{attrs:{label:"preEraseDelay",type:"number"},model:{value:e.preEraseDelay,callback:function(t){e.preEraseDelay=e._n(t)},expression:"preEraseDelay"}}),n("form-input",{attrs:{label:"eraseDelay",type:"number"},model:{value:e.eraseDelay,callback:function(t){e.eraseDelay=e._n(t)},expression:"eraseDelay"}})],1),n("div",{staticClass:"col-xs-12 col-lg-6",attrs:{id:"erase-style-config"}},[n("form-radio",{attrs:{model:e.eraseStyle,label:"eraseStyle",options:["backspace","select-back","select-all","clear"]},model:{value:e.eraseStyle,callback:function(t){e.eraseStyle=t},expression:"eraseStyle"}})],1),n("div",{staticClass:"col-xs-12 col-lg-6",attrs:{id:"caret-config"}},[n("form-radio",{attrs:{model:e.caretAnimation,label:"caretAnimation",options:["solid","blink","smooth","phase","expand"]},model:{value:e.caretAnimation,callback:function(t){e.caretAnimation=t},expression:"caretAnimation"}})],1)])]),n("template",{slot:"main-playground-code"},[n("code-block",{attrs:{code:e.playgroundDemoCode,language:"html"}})],1),n("template",{slot:"style-showcase-panel-1"},[n("h4",{staticClass:"text-xs-center"},[n("vue-typer",{staticClass:"state-typer",attrs:{text:"Katniss Everdeen","pre-type-delay":1e3,"type-delay":160,"pre-erase-delay":2e3,"erase-delay":80,"erase-style":"select-back","caret-animation":"solid"}})],1),n("code-block",{attrs:{code:e.stateDemoStyleCode,language:"css"}})],1),n("template",{slot:"style-showcase-panel-2"},[n("h4",{staticClass:"text-xs-center"},[n("vue-typer",{staticClass:"code-typer",attrs:{text:"Katniss Everdeen","pre-type-delay":1e3,"type-delay":160,"pre-erase-delay":2e3,"erase-delay":1280,"erase-style":"select-all","caret-animation":"blink"}})],1),n("code-block",{attrs:{code:e.codeDemoStyleCode,language:"css"}})],1),n("template",{slot:"style-showcase-panel-3"},[n("h4",{staticClass:"card-title text-xs-center"},[n("vue-typer",{staticClass:"ghost-typer",attrs:{text:"Katniss Everdeen","pre-type-delay":1e3,"type-delay":160,"pre-erase-delay":2e3,"erase-delay":80,"erase-style":"select-back"}})],1),n("code-block",{attrs:{code:e.ghostDemoStyleCode,language:"css"}})],1),n("copyright-footer",{slot:"footer"})],2)},staticRenderFns:[]},e.exports.render._withStripped=!0},Y9A6:function(e,t){},"YI+A":function(e,t,n){(function(e,n){function r(e){return e.split("")}function o(e){return G.test(e)}function s(e){return o(e)?a(e):r(e)}function a(e){return e.match(F)||[]}function i(e){return v(e)&&W.call(e)==S}function l(e,t,n){var r=-1,o=e.length;t<0&&(t=-t>o?0:o+t),n=n>o?o:n,n<0&&(n+=o),o=t>n?0:n-t>>>0,t>>>=0;for(var s=Array(o);++r=r?e:l(e,t,n)}function p(e,t){return!!(t=null==t?M:t)&&("number"==typeof e||k.test(e))&&e>-1&&e%1==0&&e-1&&e%1==0&&e<=M}function v(e){var t=typeof e;return!!e&&("object"==t||"function"==t)}function x(e){return!!e&&"object"==typeof e}function g(e){return"symbol"==typeof e||x(e)&&W.call(e)==_}function A(e){return null==e?"":u(e)}function C(e,t,n){return n&&"number"!=typeof n&&d(e,t,n)&&(t=n=void 0),(n=void 0===n?E:n>>>0)?(e=A(e),e&&("string"==typeof t||null!=t&&!ee(t))&&!(t=u(t))&&o(e)?c(s(e),0,n):e.split(t,n)):[]}var b=1/0,M=9007199254740991,E=4294967295,T="[object Function]",I="[object GeneratorFunction]",S="[object RegExp]",_="[object Symbol]",k=/^(?:0|[1-9]\d*)$/,D="[\\u0300-\\u036f\\ufe20-\\ufe23\\u20d0-\\u20f0]",w="\\ud83c[\\udffb-\\udfff]",j="(?:\\ud83c[\\udde6-\\uddff]){2}",R="[\\ud800-\\udbff][\\udc00-\\udfff]",B="(?:[\\u0300-\\u036f\\ufe20-\\ufe23\\u20d0-\\u20f0]|\\ud83c[\\udffb-\\udfff])?",O="(?:\\u200d(?:"+["[^\\ud800-\\udfff]",j,R].join("|")+")[\\ufe0e\\ufe0f]?"+B+")*",N="[\\ufe0e\\ufe0f]?"+B+O,V="(?:"+["[^\\ud800-\\udfff]"+D+"?",D,j,R,"[\\ud800-\\udfff]"].join("|")+")",F=RegExp(w+"(?="+w+")|"+V+N,"g"),G=RegExp("[\\u200d\\ud800-\\udfff\\u0300-\\u036f\\ufe20-\\ufe23\\u20d0-\\u20f0\\ufe0e\\ufe0f]"),L="object"==typeof e&&e&&e.Object===Object&&e,Y="object"==typeof self&&self&&self.Object===Object&&self,U=L||Y||Function("return this")(),Q="object"==typeof t&&t&&!t.nodeType&&t,Z=Q&&"object"==typeof n&&n&&!n.nodeType&&n,H=Z&&Z.exports===Q,K=H&&L.process,J=function(){try{return K&&K.binding("util")}catch(e){}}(),P=J&&J.isRegExp,X=Object.prototype,W=X.toString,z=U.Symbol,q=z?z.prototype:void 0,$=q?q.toString:void 0,ee=P?function(e){return function(t){return e(t)}}(P):i;n.exports=C}).call(t,n("DuR2"),n("3IRH")(e))},Z4s0:function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0});var r=n("5fl1"),o=function(e){return e&&e.__esModule?e:{default:e}}(r);t.default={components:{CircleLink:o.default}}},ZKVD:function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.default={props:{value:Boolean,label:String}}},Zsqw:function(e,t){},bC94:function(e,t,n){e.exports={render:function(){var e=this,t=e.$createElement,n=e._self._c||t;return n("div",{staticClass:"form-group row flex-items-xs-center"},[n("label",{staticClass:"col-form-label col-xs-4 col-md-3 col-lg-6",attrs:{for:e.label}},[e._v(e._s(e.label))]),n("div",{staticClass:"col-xs-4 col-md-3 col-lg-6"},[n("input",{staticClass:"form-control",attrs:{id:e.label,type:e.type},domProps:{value:e.value},on:{input:function(t){e.$emit("input",t.target.value)}}})])])},staticRenderFns:[]},e.exports.render._withStripped=!0},bE5q:function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.VueTyper=void 0;var r=n("1osy"),o=function(e){return e&&e.__esModule?e:{default:e}}(r);t.VueTyper=o.default;t.default={install:function(e){e.component("vue-typer",o.default)}}},erKa:function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0});var r=n("cmFa"),o=function(e){return e&&e.__esModule?e:{default:e}}(r),s={html:"markup",css:"css"};t.default={props:{code:String,language:String},computed:{languageClass:function(){return"language-"+s[this.language]}},mounted:function(){this.highlightCode()},methods:{highlightCode:function(){var e=this.$refs.codeBlock;e&&(e.innerHTML="",e.appendChild(document.createTextNode(this.code)),o.default.highlightElement(e))}},watch:{code:function(){this.highlightCode()}}}},i3yy:function(e,t){},i9x0:function(e,t){},iWFa:function(e,t){},"j57+":function(e,t){},k2Ib:function(e,t,n){n("30KW"),e.exports=n("FeBl").Number.isFinite},mti1:function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.default={props:{value:{},label:String,type:String}}},oGlG:function(e,t){e.exports="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAHgAAAB4CAYAAAA5ZDbSAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAyRpVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUuNS1jMDIxIDc5LjE1NDkxMSwgMjAxMy8xMC8yOS0xMTo0NzoxNiAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wTU09Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9tbS8iIHhtbG5zOnN0UmVmPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvc1R5cGUvUmVzb3VyY2VSZWYjIiB4bWxuczp4bXA9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC8iIHhtcE1NOkRvY3VtZW50SUQ9InhtcC5kaWQ6RERCMUIwQTM4NkNFMTFFM0FBNTJFRTMzNTJEMUJDNDYiIHhtcE1NOkluc3RhbmNlSUQ9InhtcC5paWQ6RERCMUIwQTI4NkNFMTFFM0FBNTJFRTMzNTJEMUJDNDYiIHhtcDpDcmVhdG9yVG9vbD0iQWRvYmUgUGhvdG9zaG9wIENTNiAoTWFjaW50b3NoKSI+IDx4bXBNTTpEZXJpdmVkRnJvbSBzdFJlZjppbnN0YW5jZUlEPSJ4bXAuaWlkOkU1MTc4QTMyOTlBMDExRTI5QTE1QkMxMDQ2QTg5MDREIiBzdFJlZjpkb2N1bWVudElEPSJ4bXAuZGlkOjJBNDE0QUJDOTlBMTExRTI5QTE1QkMxMDQ2QTg5MDREIi8+IDwvcmRmOkRlc2NyaXB0aW9uPiA8L3JkZjpSREY+IDwveDp4bXBtZXRhPiA8P3hwYWNrZXQgZW5kPSJyIj8+8kSqyAAADD5JREFUeNrsXQ2QlVUZfllYUBe2YCuQFNel9Q9EcVEQSA3xB2pTSVcESjELnZomBW0ya5w0m1GyzKSmtEYDc6hGohRDrUGQZUko0EARCAXK+FEwXFz2yvY+fO/d+fbu/fm++533+7n3PDPPwC6Xc77zPvc7P+95z3t6dHR0kEXpoleJtGMwcwTzE8w6Zi1zELNG2JfZJ+P/tDEPMPcK32JuY25lbmauZ/476YbpkcA3+BjmucxxwlHMAUp1vc18ifmisJnZagU2jyHMKcxJzPOzvI1hAW/9MuYS5pPMN6zAxeNjzOnMq5mjY/qMLcyFzPnMXVZgb7iQOYt5ObMyIT1hO/MPzJ8xn7cCZ5/sTWXeKpOlJAOTs/uYTzBT5S4whJ3BvIM5tMRWKFuYd0v3nSpHgT/NnMs8pcSXoq8xZzOfKheBT2I+wLy0zHwOzzC/LoKHhooQ68KE6XYZo8pNXJI2rxMbVJbaG3wa83HmGWRBIvQ05oakv8E9mF9hrrHidsEZYpOvio0S+QbD//tL5lVWz7z4HXMmOX7xxAhcz1wkXbNFYWxkXsZ8PQld9HjmKiuuL5wqNhsfd4GbyHHVDbCa+cYAsV1TXAXGOPIbZm+rVdHoLTa8Pm4C3yQTqgqrkRFNHhGbxmKSNVPEtTCPLwa1bVCBm6RLsW+uDg4zryFnzzl0gcfLpMCOubo4RM4e+YowBa6Xab2dLYcDxIaNKWadXIzA8FCtlrWbRXiAM+Qc8unx8jt2wm/6KytuJDhVbN9DU2BsHFwZ8EH3keNof1n+XurYJ21Fm/cHLOtK0UCli4brcS0FD1n9DHWNbjhOJhHYL4U/9uiEC3qQnAC8Z2QSusP1b43MxQHLR+huA/OfJgXGBvXfKPiWHyYLOHHQnuPfq8mJ0UJUZdKC7/CWIqoSMVjv5rHjf5n9A9aF/eSz89jRdxd9G5nZz11S4KFgmHlSF4LcWxIg7Gp51hHy7O/m+Wy72CAoYJ9vmBqDT2Z+25AxXvDxWXRxOKLyOXLOC8UNW2VMHCPP6hXLDdV/h2gTuIv+M/NiQw/VIOO4X2DcnyNftFxzgDdkXHqVuZOcg2MgDpa9J2Njm6s8jPVV5BxOGyz8ODlRnsOYJ+QZA+9h3st8v0gbvGTInkuZlwQRGKGtfzL0MO1i0PYAZcDBAkf8cOZK6RGWy/hnOiIC6/3TyfHYnUfOQTd8gW6gYJGRlfKFMxV4lzlp9SxwL2nQSYYe5M08b4XftTh4OOQuOT2cmah3u6weTOB1WeGk/I7BMwyKC7xlqJyOCMRNC2uq3v8YfK560crXJKtSBnHT60MLB6bPGEOr3n4ExkGwoVaHxABaXe1H4DkKD3GU1aETGt66W70KPJF0vEgnWF07MUShzNNFu4IC36jUqIHMflbbIzYYqFT2TYUERtqEzypVjqXNWVbfIzbQOq7SKBrmFHgG6Z58m2j1VbVBZeaSKVPgJuXGNVp91W3QlEtgJBDTzmZzt9VX3Qaj3Utct8CXK1d8Fzkn6codsMF3leu4LJvAkxQrXBVCo5KEu8QmWpjcObOVzQakB0S0hUYGuQ9kjbbR6toF2JbELphGvlBsaSKkuTX9Bo8jvfSAD1lxs+JVsY0G+oimnV30WKWKsCH+PatlTtxDxQUNeMFYt8DjlCr5NcU0h2NMsEtspIFx7jF4L+kcQ8GUfbXVMS9wWkEjuBBzqhoIjDikHQoVbCW75egVW8QPYRrHoYvWij9+2urmGUuUyh0BgeuVCl9hdYvcVvUQuFapcDv2Rm+rWi2BERr7ptXNM2CrlJbAgxQKRljoB1Y3z4C4OxXKHQSBaxQK/p/VzDc0jtLWaAm83+rlGwe0BNaIk+pp9fINjU2HfhBYI0tOX6uXb2iEFffWym9VZfXyjWqNQrUEtrmzYmIz+KI1EkYfki7HXm3q/UXDtmGlRsEppW/jYKubZwwmnXDlVIXikuZEq5tn1CmVu7+C9HJV1VndIn8Z9kHg3UqFj7K6ecbZSuXuhsA7lQofa3WL3FY7NQU+k5xwXIvCPoMRmgJvVioc7soJVr+CmEB6rt3NEHiT4sNPsfoVxBWKZW+CowPpfLYrVYBtQ+w3t1odswJDGLIPaR2MPx5vMCIq9ypVgAefbnXMiemK4iJsdkfaF71GsRG3kL20Ixt6iW20cCRdYtrwKxUrwiGra62e3fB50r39vNkt8IvKjcEZnGqraSeqxSaaWOEWGD+0KVaGidb9VtdO/Ih0gh3TaMsUGFtVy5UbhVu8plltjyRJmalcx3LRtMvk548hNO5hcpJ8lytw4u/nIdTTmQLanU4Ymei2hVA5Ut4jwXhLmYmLk5ZLQ5qL1JKTIL3LG4xfhHHcpFoaenEZiYv8J8+GJO7qtLiUZX26IMRZJE7U3UmlHWKLtiFt0lMUXhrHx90/ZGZ8/yg5u0uVIRoBSzRc9rSuxMRFysJ5pJ97zA2cCYPreVeuNxib/4simHjAk/YT0snCGjYQnfELcjxJo0OuexFlpMzIdmfDBcy/+ii0WWZtKBjZArB5jS2wXkV+AzFM/JSSdfwUyUU/SU6m3qYIh50JmdrlupQDV9+M9FAgbg/5EHU/SYiu/mbmbCo+3hepl56QL8/fKX4huD1lyYekY1Mp+iBDDHFndvvm5RAYi3Gv2V9uZ34/y0IbnpTH5I0cGfDhcR3cC9Jb4Iq9Vyj8iy0xtuE6n1HSS0HcD8foCwff9nyvAqN7RaIur0lUHiDnqrU215pvgMyUEZKykFzp9QwB25xbZD39TTJ/Ewsmmj+WttRJTxVXwA7YuOge4w6Bc/DaDn/YyByZUcYVzGXMY+VP0ziQpU6TbGC+3xF/XJerDfkaV8Fc77OiVuYlrjKGMXczJzFrmNsNN2yWorhpfi3m4r4sWmV9/kJX28ED4zcdEu5HQlbzbHvMkynPNWxFTCrOIv1LsjCZQtLQuN56PpnypGEqFGmxhPzfXYgrY35PXe8OqBJXHcaIRw017D4K5wY0rBDujam4T1OBHFtebh/FRAt3GPrNRovdqfQFH8fIpAj37OG2TORKPjlAwxDMN5DCu02trziB4nT3Eya0w2SCRcW+wekZ2neKeIBG18y5VTxWt8nyppGCBdz/hcK9Ku+A1Bkn3FlIXK8CA/dTcXfe/sBVBxwXy6S7xloSV9duKLJxKyMwaJwy98G1O9fLB70KnBLnh9+35hTqfssI7uPFjseD5By6wpfgkI8yEai/NAKjxiWp+UHRImVSYOA1cT/6xeyMn58jJ7LjoHTdc8TN9y1ydpYyg+T3iGcM9xyMkS/NPyIw7LaYCHyzOKG8oYh14fwi1mrn5invROazzAeZR8nv+jOHMPu5PjeKOZd5fghr32ysjcGad4Hf5y6moVXMdT4frJnZM0d5dcw98rkG+d158rsNIjZ+t1Y+Mz8igT8SsbhwOvX1+9zFnDh4T5Y/fg6Oj5FZXzYgcfjx5ISRrnGNM0jQ+S+Xfxt3AV3KvD6irjEVYbe8R2zuOxuel3VwLmA35XnydxcuIjfmUTKBnaN3IppUTSx25RDkzBC27qb69CY9JNP7ygQKHMUzw7bTgiwLgx4KW8z8gk+RMatGQMFFCRO4KgJxYdtAIVQmTv0tkHHRj8jDZS2Lvdwbyd8xjmOp9JOdwpazyECUa5AxOBM46/pYgC8N3G6vyHpzn6yHEeuEdMfYuKgl54o8BBL0p/AjOmpl0hfWm2skhNlkCls8EJKqLfQ58UpjKHmPIOlTom/uQZnXLDZVoOmD2dha/BTp33Z2dAmKC5tdaFJcDYFJxtVzInInJhXrxWbNpgvWSq2AszHYVHjUalcQiF4dS67zREkQGIDH6zrmDfJ3i+72+ZJMqNTsE0ZylEfICchusZp2GcYQT/awdkVhZb9BNj1EdNxC4UZixHGWPEdssSmMCsNMb4TgtR+SE534ZBmKizafRk6AQ2iXhkWRvwqTiSmyJFhbBsLiXNVF0uZtYVceZYIyBLEhNusa8h8Ok4SUTBulbWjjc1E9RNQZ6OAnxQlC+KZx7HKVx//3dgTP6jXNVIu0Zbi07XCUBjbpizYFBAekz9lm81itoeiyySOytCGH+L8l51zzyjgZM44Cp4EN9qvI2cRAcAE2HnC4+ctaTgEPqCXn9P4F8maix1kg4r4TRyPGWWCLEhiDLZTxfwEGAIg2ItsKhKpcAAAAAElFTkSuQmCC"},oJ8v:function(e,t){},pd3l:function(e,t,n){"use strict";function r(e){return e&&e.__esModule?e:{default:e}}Object.defineProperty(t,"__esModule",{value:!0});var o=n("fZjL"),s=r(o),a=n("6qCy"),i=r(a),l=n("2vxi"),u=r(l),c=n("slMF"),p=r(c),d=n("SHlX"),f=r(d),h=n("YI+A"),m=r(h),y={IDLE:"idle",TYPING:"typing",ERASING:"erasing",COMPLETE:"complete"},v={BACKSPACE:"backspace",SELECT_BACK:"select-back",SELECT_ALL:"select-all",CLEAR:"clear"};t.default={name:"VueTyper",components:{Caret:i.default,Char:u.default},props:{text:{type:[String,Array],required:!0,validator:function(e){return"string"==typeof e?e.length>0:e.every(function(e){return"string"==typeof e&&e.length>0})}},repeat:{type:Number,default:1/0,validator:function(e){return e>=0}},shuffle:{type:Boolean,default:!1},initialAction:{type:String,default:y.TYPING,validator:function(e){return!!e.match("^"+y.TYPING+"|"+y.ERASING+"$")}},preTypeDelay:{type:Number,default:70,validator:function(e){return e>=0}},typeDelay:{type:Number,default:70,validator:function(e){return e>=0}},preEraseDelay:{type:Number,default:2e3,validator:function(e){return e>=0}},eraseDelay:{type:Number,default:250,validator:function(e){return e>=0}},eraseStyle:{type:String,default:v.SELECT_ALL,validator:function(e){return(0,s.default)(v).some(function(t){return v[t]===e})}},eraseOnComplete:{type:Boolean,default:!1},caretAnimation:String},data:function(){return{state:y.IDLE,nextState:null,spool:[],spoolIndex:-1,previousTextIndex:-1,currentTextIndex:-1,repeatCounter:0,actionTimeout:0,actionInterval:0}},computed:{caretClasses:function(){var e=this.state===y.IDLE;return{idle:e,"pre-type":e&&this.nextState===y.TYPING,"pre-erase":e&&this.nextState===y.ERASING,typing:this.state===y.TYPING,selecting:this.state===y.ERASING&&this.isSelectionBasedEraseStyle,erasing:this.state===y.ERASING&&!this.isSelectionBasedEraseStyle,complete:this.state===y.COMPLETE}},rightCharClasses:function(){return{selected:this.state===y.ERASING&&this.isSelectionBasedEraseStyle,erased:this.state!==y.ERASING||this.state===y.ERASING&&!this.isSelectionBasedEraseStyle}},isSelectionBasedEraseStyle:function(){return!!this.eraseStyle.match("^"+v.SELECT_BACK+"|"+v.SELECT_ALL+"$")},isEraseAllStyle:function(){return!!this.eraseStyle.match("^"+v.CLEAR+"|"+v.SELECT_ALL+"$")},isDoneTyping:function(){return this.currentTextIndex>=this.currentTextLength},isDoneErasing:function(){return this.isSelectionBasedEraseStyle?this.currentTextIndex<=0&&this.previousTextIndex<=0:this.currentTextIndex<=0},onLastWord:function(){return this.spoolIndex===this.spool.length-1},shouldRepeat:function(){return this.repeatCounter=0&&this.spoolIndex0}),this.spool=e}this.repeatCounter=0,this.resetSpool(),this.initialAction===y.TYPING?this.startTyping():this.initialAction===y.ERASING&&(this.moveCaretToEnd(),this.onTyped())},reset:function(){this.cancelCurrentAction(),this.init()},resetSpool:function(){this.spoolIndex=0,this.shuffle&&this.spool.length>1&&(0,f.default)(this.spool)},cancelCurrentAction:function(){this.actionInterval&&(clearInterval(this.actionInterval),this.actionInterval=0),this.actionTimeout&&(clearTimeout(this.actionTimeout),this.actionTimeout=0)},shiftCaret:function(e){this.previousTextIndex=this.currentTextIndex;var t=this.currentTextIndex+e;this.currentTextIndex=Math.min(Math.max(t,0),this.currentTextLength)},moveCaretToStart:function(){this.previousTextIndex=this.currentTextIndex,this.currentTextIndex=0},moveCaretToEnd:function(){this.previousTextIndex=this.currentTextIndex,this.currentTextIndex=this.currentTextLength},typeStep:function(){if(!this.isDoneTyping){this.shiftCaret(1);var e=this.previousTextIndex,t=this.currentTextArray[e];this.$emit("typed-char",t,e)}this.isDoneTyping&&(this.cancelCurrentAction(),this.$nextTick(this.onTyped))},eraseStep:function(){this.isDoneErasing||(this.isEraseAllStyle?this.moveCaretToStart():this.shiftCaret(-1)),this.isDoneErasing&&(this.cancelCurrentAction(),this.$nextTick(this.onErased))},startTyping:function(){var e=this;this.actionTimeout||this.actionInterval||(this.moveCaretToStart(),this.state=y.IDLE,this.nextState=y.TYPING,this.actionTimeout=setTimeout(function(){e.state=y.TYPING,e.typeStep(),e.isDoneTyping||(e.actionInterval=setInterval(e.typeStep,e.typeDelay))},this.preTypeDelay))},startErasing:function(){var e=this;this.actionTimeout||this.actionInterval||(this.moveCaretToEnd(),this.state=y.IDLE,this.nextState=y.ERASING,this.actionTimeout=setTimeout(function(){e.state=y.ERASING,e.eraseStep(),e.isDoneErasing||(e.actionInterval=setInterval(e.eraseStep,e.eraseDelay))},this.preEraseDelay))},onTyped:function(){this.$emit("typed",this.currentText),this.onLastWord?this.eraseOnComplete||this.shouldRepeat?this.startErasing():this.onComplete():this.startErasing()},onErased:function(){this.$emit("erased",this.currentText),this.onLastWord?this.shouldRepeat?(this.repeatCounter++,this.resetSpool(),this.startTyping()):this.onComplete():(this.spoolIndex++,this.startTyping())},onComplete:function(){this.state=y.COMPLETE,this.nextState=null,this.$emit("completed")}},watch:{text:function(e,t){e===t||(0,p.default)(e,t)||this.reset()},repeat:function(){this.reset()},shuffle:function(){this.reset()}}}},slMF:function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.default=function(e,t){if(!Array.isArray(e)||!Array.isArray(t))return!1;if(e.length!==t.length)return!1;for(var n=0;nn.parts.length&&(r.parts.length=n.parts.length)}else{for(var u=[],i=0;i0?r:n)(t)}},function(t,e,n){var r=n(36),i=n(9);t.exports=function(t){return r(i(t))}},function(t,e,n){n(63);var r=n(7)(n(19),n(60),"data-v-c41bac74",null);t.exports=r.exports},function(t,e,n){"use strict";Object.defineProperty(e,"__esModule",{value:!0}),e.VueTyper=void 0;var r=n(12),i=function(t){return t&&t.__esModule?t:{default:t}}(r);e.VueTyper=i.default;e.default={install:function(t){t.component("vue-typer",i.default)}}},function(t,e,n){"use strict";function r(t){return t&&t.__esModule?t:{default:t}}function i(t){return"number"==typeof t&&!(0,c.default)(t)&&(0,a.default)(t)}function o(t,e){return i(t)&&i(e)&&t<=e}Object.defineProperty(e,"__esModule",{value:!0});var u=n(20),a=r(u),s=n(21),c=r(s);e.default=function(t,e){return o(t,e)?(t=Math.ceil(t),e=Math.floor(e),Math.floor(Math.random()*(e-t+1))+t):-1}},function(t,e,n){"use strict";Object.defineProperty(e,"__esModule",{value:!0}),e.default=function(t,e){if(!Array.isArray(t)||!Array.isArray(e))return!1;if(t.length!==e.length)return!1;for(var n=0;n0;e--){var n=(0,o.default)(0,e);r(t,e,n)}}},function(t,e,n){"use strict";Object.defineProperty(e,"__esModule",{value:!0});e.default={props:{animation:{type:String,default:"blink",validator:function(t){return/^solid$|^blink$|^smooth$|^phase$|^expand$/.test(t)}}},computed:{animationClass:function(){return"vue-typer-caret-"+this.animation}}}},function(t,e,n){"use strict";Object.defineProperty(e,"__esModule",{value:!0}),e.default={props:{val:{type:String,default:""}},computed:{classes:function(){return{newline:0===this.val.indexOf("\n")}}}}},function(t,e,n){"use strict";function r(t){return t&&t.__esModule?t:{default:t}}Object.defineProperty(e,"__esModule",{value:!0});var i=n(22),o=r(i),u=n(56),a=r(u),s=n(57),c=r(s),f=n(15),l=r(f),p=n(16),d=r(p),h=n(55),y=r(h),v={IDLE:"idle",TYPING:"typing",ERASING:"erasing",COMPLETE:"complete"},x={BACKSPACE:"backspace",SELECT_BACK:"select-back",SELECT_ALL:"select-all",CLEAR:"clear"};e.default={name:"VueTyper",components:{Caret:a.default,Char:c.default},props:{text:{type:[String,Array],required:!0,validator:function(t){return"string"==typeof t?t.length>0:t.every(function(t){return"string"==typeof t&&t.length>0})}},repeat:{type:Number,default:1/0,validator:function(t){return t>=0}},shuffle:{type:Boolean,default:!1},initialAction:{type:String,default:v.TYPING,validator:function(t){return!!t.match("^"+v.TYPING+"|"+v.ERASING+"$")}},preTypeDelay:{type:Number,default:70,validator:function(t){return t>=0}},typeDelay:{type:Number,default:70,validator:function(t){return t>=0}},preEraseDelay:{type:Number,default:2e3,validator:function(t){return t>=0}},eraseDelay:{type:Number,default:250,validator:function(t){return t>=0}},eraseStyle:{type:String,default:x.SELECT_ALL,validator:function(t){return(0,o.default)(x).some(function(e){return x[e]===t})}},eraseOnComplete:{type:Boolean,default:!1},caretAnimation:String},data:function(){return{state:v.IDLE,nextState:null,spool:[],spoolIndex:-1,previousTextIndex:-1,currentTextIndex:-1,repeatCounter:0,actionTimeout:0,actionInterval:0}},computed:{caretClasses:function(){var t=this.state===v.IDLE;return{idle:t,"pre-type":t&&this.nextState===v.TYPING,"pre-erase":t&&this.nextState===v.ERASING,typing:this.state===v.TYPING,selecting:this.state===v.ERASING&&this.isSelectionBasedEraseStyle,erasing:this.state===v.ERASING&&!this.isSelectionBasedEraseStyle,complete:this.state===v.COMPLETE}},rightCharClasses:function(){return{selected:this.state===v.ERASING&&this.isSelectionBasedEraseStyle,erased:this.state!==v.ERASING||this.state===v.ERASING&&!this.isSelectionBasedEraseStyle}},isSelectionBasedEraseStyle:function(){return!!this.eraseStyle.match("^"+x.SELECT_BACK+"|"+x.SELECT_ALL+"$")},isEraseAllStyle:function(){return!!this.eraseStyle.match("^"+x.CLEAR+"|"+x.SELECT_ALL+"$")},isDoneTyping:function(){return this.currentTextIndex>=this.currentTextLength},isDoneErasing:function(){return this.isSelectionBasedEraseStyle?this.currentTextIndex<=0&&this.previousTextIndex<=0:this.currentTextIndex<=0},onLastWord:function(){return this.spoolIndex===this.spool.length-1},shouldRepeat:function(){return this.repeatCounter=0&&this.spoolIndex0}),this.spool=t}this.repeatCounter=0,this.resetSpool(),this.initialAction===v.TYPING?this.startTyping():this.initialAction===v.ERASING&&(this.moveCaretToEnd(),this.onTyped())},reset:function(){this.cancelCurrentAction(),this.init()},resetSpool:function(){this.spoolIndex=0,this.shuffle&&this.spool.length>1&&(0,d.default)(this.spool)},cancelCurrentAction:function(){this.actionInterval&&(clearInterval(this.actionInterval),this.actionInterval=0),this.actionTimeout&&(clearTimeout(this.actionTimeout),this.actionTimeout=0)},shiftCaret:function(t){this.previousTextIndex=this.currentTextIndex;var e=this.currentTextIndex+t;this.currentTextIndex=Math.min(Math.max(e,0),this.currentTextLength)},moveCaretToStart:function(){this.previousTextIndex=this.currentTextIndex,this.currentTextIndex=0},moveCaretToEnd:function(){this.previousTextIndex=this.currentTextIndex,this.currentTextIndex=this.currentTextLength},typeStep:function(){if(!this.isDoneTyping){this.shiftCaret(1);var t=this.previousTextIndex,e=this.currentTextArray[t];this.$emit("typed-char",e,t)}this.isDoneTyping&&(this.cancelCurrentAction(),this.$nextTick(this.onTyped))},eraseStep:function(){this.isDoneErasing||(this.isEraseAllStyle?this.moveCaretToStart():this.shiftCaret(-1)),this.isDoneErasing&&(this.cancelCurrentAction(),this.$nextTick(this.onErased))},startTyping:function(){var t=this;this.actionTimeout||this.actionInterval||(this.moveCaretToStart(),this.state=v.IDLE,this.nextState=v.TYPING,this.actionTimeout=setTimeout(function(){t.state=v.TYPING,t.typeStep(),t.isDoneTyping||(t.actionInterval=setInterval(t.typeStep,t.typeDelay))},this.preTypeDelay))},startErasing:function(){var t=this;this.actionTimeout||this.actionInterval||(this.moveCaretToEnd(),this.state=v.IDLE,this.nextState=v.ERASING,this.actionTimeout=setTimeout(function(){t.state=v.ERASING,t.eraseStep(),t.isDoneErasing||(t.actionInterval=setInterval(t.eraseStep,t.eraseDelay))},this.preEraseDelay))},onTyped:function(){this.$emit("typed",this.currentText),this.onLastWord?this.eraseOnComplete||this.shouldRepeat?this.startErasing():this.onComplete():this.startErasing()},onErased:function(){this.$emit("erased",this.currentText),this.onLastWord?this.shouldRepeat?(this.repeatCounter++,this.resetSpool(),this.startTyping()):this.onComplete():(this.spoolIndex++,this.startTyping())},onComplete:function(){this.state=v.COMPLETE,this.nextState=null,this.$emit("completed")}},watch:{text:function(t,e){t===e||(0,l.default)(t,e)||this.reset()},repeat:function(){this.reset()},shuffle:function(){this.reset()}}}},function(t,e,n){t.exports={default:n(23),__esModule:!0}},function(t,e,n){t.exports={default:n(24),__esModule:!0}},function(t,e,n){t.exports={default:n(25),__esModule:!0}},function(t,e,n){n(49),t.exports=n(0).Number.isFinite},function(t,e,n){n(50),t.exports=n(0).Number.isNaN},function(t,e,n){n(51),t.exports=n(0).Object.keys},function(t,e){t.exports=function(t){if("function"!=typeof t)throw TypeError(t+" is not a function!");return t}},function(t,e,n){var r=n(5);t.exports=function(t){if(!r(t))throw TypeError(t+" is not an object!");return t}},function(t,e,n){var r=n(11),i=n(45),o=n(44);t.exports=function(t){return function(e,n,u){var a,s=r(e),c=i(s.length),f=o(u,c);if(t&&n!=n){for(;c>f;)if((a=s[f++])!=a)return!0}else for(;c>f;f++)if((t||f in s)&&s[f]===n)return t||f||0;return!t&&-1}}},function(t,e){var n={}.toString;t.exports=function(t){return n.call(t).slice(8,-1)}},function(t,e,n){var r=n(26);t.exports=function(t,e,n){if(r(t),void 0===e)return t;switch(n){case 1:return function(n){return t.call(e,n)};case 2:return function(n,r){return t.call(e,n,r)};case 3:return function(n,r,i){return t.call(e,n,r,i)}}return function(){return t.apply(e,arguments)}}},function(t,e,n){var r=n(5),i=n(1).document,o=r(i)&&r(i.createElement);t.exports=function(t){return o?i.createElement(t):{}}},function(t,e){t.exports="constructor,hasOwnProperty,isPrototypeOf,propertyIsEnumerable,toLocaleString,toString,valueOf".split(",")},function(t,e){var n={}.hasOwnProperty;t.exports=function(t,e){return n.call(t,e)}},function(t,e,n){var r=n(37),i=n(41);t.exports=n(2)?function(t,e,n){return r.f(t,e,i(1,n))}:function(t,e,n){return t[e]=n,t}},function(t,e,n){t.exports=!n(2)&&!n(4)(function(){return 7!=Object.defineProperty(n(31)("div"),"a",{get:function(){return 7}}).a})},function(t,e,n){var r=n(29);t.exports=Object("z").propertyIsEnumerable(0)?Object:function(t){return"String"==r(t)?t.split(""):Object(t)}},function(t,e,n){var r=n(27),i=n(35),o=n(47),u=Object.defineProperty;e.f=n(2)?Object.defineProperty:function(t,e,n){if(r(t),e=o(e,!0),r(n),i)try{return u(t,e,n)}catch(t){}if("get"in n||"set"in n)throw TypeError("Accessors not supported!");return"value"in n&&(t[e]=n.value),t}},function(t,e,n){var r=n(33),i=n(11),o=n(28)(!1),u=n(42)("IE_PROTO");t.exports=function(t,e){var n,a=i(t),s=0,c=[];for(n in a)n!=u&&r(a,n)&&c.push(n);for(;e.length>s;)r(a,n=e[s++])&&(~o(c,n)||c.push(n));return c}},function(t,e,n){var r=n(38),i=n(32);t.exports=Object.keys||function(t){return r(t,i)}},function(t,e,n){var r=n(3),i=n(0),o=n(4);t.exports=function(t,e){var n=(i.Object||{})[t]||Object[t],u={};u[t]=e(n),r(r.S+r.F*o(function(){n(1)}),"Object",u)}},function(t,e){t.exports=function(t,e){return{enumerable:!(1&t),configurable:!(2&t),writable:!(4&t),value:e}}},function(t,e,n){var r=n(43)("keys"),i=n(48);t.exports=function(t){return r[t]||(r[t]=i(t))}},function(t,e,n){var r=n(1),i=r["__core-js_shared__"]||(r["__core-js_shared__"]={});t.exports=function(t){return i[t]||(i[t]={})}},function(t,e,n){var r=n(10),i=Math.max,o=Math.min;t.exports=function(t,e){return t=r(t),t<0?i(t+e,0):o(t,e)}},function(t,e,n){var r=n(10),i=Math.min;t.exports=function(t){return t>0?i(r(t),9007199254740991):0}},function(t,e,n){var r=n(9);t.exports=function(t){return Object(r(t))}},function(t,e,n){var r=n(5);t.exports=function(t,e){if(!r(t))return t;var n,i;if(e&&"function"==typeof(n=t.toString)&&!r(i=n.call(t)))return i;if("function"==typeof(n=t.valueOf)&&!r(i=n.call(t)))return i;if(!e&&"function"==typeof(n=t.toString)&&!r(i=n.call(t)))return i;throw TypeError("Can't convert object to primitive value")}},function(t,e){var n=0,r=Math.random();t.exports=function(t){return"Symbol(".concat(void 0===t?"":t,")_",(++n+r).toString(36))}},function(t,e,n){var r=n(3),i=n(1).isFinite;r(r.S,"Number",{isFinite:function(t){return"number"==typeof t&&i(t)}})},function(t,e,n){var r=n(3);r(r.S,"Number",{isNaN:function(t){return t!=t}})},function(t,e,n){var r=n(46),i=n(39);n(40)("keys",function(){return function(t){return i(r(t))}})},function(t,e,n){e=t.exports=n(6)(),e.push([t.i,".char[data-v-302772ec]{display:inline-block;white-space:pre-wrap}.newline[data-v-302772ec]{display:inline}.typed[data-v-302772ec]{color:#000;background-color:transparent}.selected[data-v-302772ec]{color:#000;background-color:#accef7}.erased[data-v-302772ec]{display:none}",""])},function(t,e,n){e=t.exports=n(6)(),e.push([t.i,'@keyframes vue-typer-caret-blink{50%{opacity:0}to{opacity:1}}@keyframes vue-typer-caret-smooth{0%,20%{opacity:1}60%,to{opacity:0}}@keyframes vue-typer-caret-phase{0%,20%{opacity:1}90%,to{opacity:0}}@keyframes vue-typer-caret-expand{0%,20%{transform:scaleY(1)}80%,to{transform:scaleY(0)}}.vue-typer-caret-blink[data-v-a16e0f02]{animation:vue-typer-caret-blink 1s step-start 0s infinite}.vue-typer-caret-smooth[data-v-a16e0f02]{animation:vue-typer-caret-smooth .5s ease-in-out 0s infinite alternate}.vue-typer-caret-phase[data-v-a16e0f02]{animation:vue-typer-caret-phase .5s ease-in-out 0s infinite alternate}.vue-typer-caret-expand[data-v-a16e0f02]{animation:vue-typer-caret-expand .5s ease-in-out 0s infinite alternate}span.caret[data-v-a16e0f02]:empty:before{content:"\\200B"}span[data-v-a16e0f02]{display:inline-block;width:1px}.idle[data-v-a16e0f02],.typing[data-v-a16e0f02]{background-color:#000}.selecting[data-v-a16e0f02]{display:none;background-color:#000}.erasing[data-v-a16e0f02]{background-color:#000}.complete[data-v-a16e0f02]{display:none;background-color:#000}',""])},function(t,e,n){e=t.exports=n(6)(),e.push([t.i,"span.vue-typer[data-v-c41bac74]{cursor:default;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}span.vue-typer span.left[data-v-c41bac74],span.vue-typer span.right[data-v-c41bac74]{display:inline}",""])},function(t,e,n){(function(t,n){function r(t){return t.split("")}function i(t){return B.test(t)}function o(t){return i(t)?u(t):r(t)}function u(t){return t.match(G)||[]}function a(t){return x(t)&&H.call(t)==j}function s(t,e,n){var r=-1,i=t.length;e<0&&(e=-e>i?0:i+e),n=n>i?i:n,n<0&&(n+=i),i=e>n?0:n-e>>>0,e>>>=0;for(var o=Array(i);++r=r?t:s(t,e,n)}function l(t,e){return!!(e=null==e?E:e)&&("number"==typeof t||O.test(t))&&t>-1&&t%1==0&&t-1&&t%1==0&&t<=E}function x(t){var e=typeof t;return!!t&&("object"==e||"function"==e)}function m(t){return!!t&&"object"==typeof t}function g(t){return"symbol"==typeof t||m(t)&&H.call(t)==A}function b(t){return null==t?"":c(t)}function T(t,e,n){return n&&"number"!=typeof n&&p(t,e,n)&&(e=n=void 0),(n=void 0===n?C:n>>>0)?(t=b(t),t&&("string"==typeof e||null!=e&&!tt(e))&&!(e=c(e))&&i(t)?f(o(t),0,n):t.split(e,n)):[]}var S=1/0,E=9007199254740991,C=4294967295,_="[object Function]",I="[object GeneratorFunction]",j="[object RegExp]",A="[object Symbol]",O=/^(?:0|[1-9]\d*)$/,N="[\\u0300-\\u036f\\ufe20-\\ufe23\\u20d0-\\u20f0]",L="\\ud83c[\\udffb-\\udfff]",M="(?:\\ud83c[\\udde6-\\uddff]){2}",w="[\\ud800-\\udbff][\\udc00-\\udfff]",P="(?:[\\u0300-\\u036f\\ufe20-\\ufe23\\u20d0-\\u20f0]|\\ud83c[\\udffb-\\udfff])?",R="(?:\\u200d(?:"+["[^\\ud800-\\udfff]",M,w].join("|")+")[\\ufe0e\\ufe0f]?"+P+")*",k="[\\ufe0e\\ufe0f]?"+P+R,D="(?:"+["[^\\ud800-\\udfff]"+N+"?",N,M,w,"[\\ud800-\\udfff]"].join("|")+")",G=RegExp(L+"(?="+L+")|"+D+k,"g"),B=RegExp("[\\u200d\\ud800-\\udfff\\u0300-\\u036f\\ufe20-\\ufe23\\u20d0-\\u20f0\\ufe0e\\ufe0f]"),$="object"==typeof t&&t&&t.Object===Object&&t,F="object"==typeof self&&self&&self.Object===Object&&self,Y=$||F||Function("return this")(),U="object"==typeof e&&e&&!e.nodeType&&e,W=U&&"object"==typeof n&&n&&!n.nodeType&&n,V=W&&W.exports===U,K=V&&$.process,q=function(){try{return K&&K.binding("util")}catch(t){}}(),z=q&&q.isRegExp,J=Object.prototype,H=J.toString,Q=Y.Symbol,X=Q?Q.prototype:void 0,Z=X?X.toString:void 0,tt=z?function(t){return function(e){return t(e)}}(z):a;n.exports=T}).call(e,n(65),n(66)(t))},function(t,e,n){n(62);var r=n(7)(n(17),n(59),"data-v-a16e0f02",null);t.exports=r.exports},function(t,e,n){n(61);var r=n(7)(n(18),n(58),"data-v-302772ec",null);t.exports=r.exports},function(t,e){t.exports={render:function(){var t=this,e=t.$createElement;return(t._self._c||e)("span",{staticClass:"char",class:t.classes},[t._v(t._s(t.val))])},staticRenderFns:[]}},function(t,e){t.exports={render:function(){var t=this,e=t.$createElement;return(t._self._c||e)("span",{staticClass:"caret custom",class:t.animationClass})},staticRenderFns:[]}},function(t,e){t.exports={render:function(){var t=this,e=t.$createElement,n=t._self._c||e;return n("span",{staticClass:"vue-typer"},[n("span",{staticClass:"left"},t._l(t.numLeftChars,function(e){return n("char",{staticClass:"custom typed",attrs:{val:t.currentTextArray[e-1]}})})),n("caret",{class:t.caretClasses,attrs:{animation:t.caretAnimation}}),n("span",{staticClass:"right"},t._l(t.numRightChars,function(e){return n("char",{staticClass:"custom",class:t.rightCharClasses,attrs:{val:t.currentTextArray[t.numLeftChars+e-1]}})}))],1)},staticRenderFns:[]}},function(t,e,n){var r=n(52);"string"==typeof r&&(r=[[t.i,r,""]]),r.locals&&(t.exports=r.locals);n(8)("3bfdc45b",r,!0)},function(t,e,n){var r=n(53);"string"==typeof r&&(r=[[t.i,r,""]]),r.locals&&(t.exports=r.locals);n(8)("0dba035c",r,!0)},function(t,e,n){var r=n(54);"string"==typeof r&&(r=[[t.i,r,""]]),r.locals&&(t.exports=r.locals);n(8)("0f4cea8e",r,!0)},function(t,e){t.exports=function(t,e){for(var n=[],r={},i=0;i 2 | 3 | 4 | 5 | 6 | 7 | VueTyper Demo 8 | 9 | 10 | 11 |
12 | 13 | 14 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "vue-typer", 3 | "version": "1.2.0", 4 | "description": "Vue component that simulates a user typing, selecting, and erasing text.", 5 | "main": "dist/vue-typer.min.js", 6 | "files": [ 7 | "dist/vue-typer.js", 8 | "dist/vue-typer.min.js" 9 | ], 10 | "scripts": { 11 | "dev": "NODE_ENV='development' webpack-dev-server --config build/webpack.config.dev.js --hot --inline --open --compress", 12 | "dev:lan": "npm run dev -- --public $(ipconfig getifaddr en0):8080 --host 0.0.0.0", 13 | "demo": "webpack --config build/webpack.config.demo.js", 14 | "prod": "NODE_ENV='production' webpack --config build/webpack.config.prod.js", 15 | "build": "npm run lint && npm run test && npm run clean && npm run demo && npm run prod", 16 | "unit": "npm run unit:watch -- --single-run", 17 | "unit:watch": "NODE_ENV='test' karma start test/unit/karma.conf.js", 18 | "unit:debug": "npm run unit:watch -- --browsers Chrome", 19 | "test": "npm run unit", 20 | "lint": "NODE_ENV='production' eslint --format node_modules/eslint-friendly-formatter --ext .js,.vue src", 21 | "clean": "rimraf ./dist ./index.html" 22 | }, 23 | "repository": { 24 | "type": "git", 25 | "url": "git+https://github.com/cngu/vue-typer.git" 26 | }, 27 | "keywords": [ 28 | "vue-typer", 29 | "vue", 30 | "vuejs", 31 | "vue.js", 32 | "component", 33 | "typer", 34 | "typewriter", 35 | "type", 36 | "auto" 37 | ], 38 | "author": "Chris Nguyen", 39 | "license": "MIT", 40 | "bugs": { 41 | "url": "https://github.com/cngu/vue-typer/issues" 42 | }, 43 | "homepage": "https://github.com/cngu/vue-typer#readme", 44 | "dependencies": { 45 | "lodash.split": "^4.4.2" 46 | }, 47 | "devDependencies": { 48 | "autoprefixer": "^6.5.4", 49 | "babel-core": "^6.21.0", 50 | "babel-eslint": "^7.1.1", 51 | "babel-loader": "^6.2.10", 52 | "babel-plugin-istanbul": "^4.1.1", 53 | "babel-plugin-transform-runtime": "^6.15.0", 54 | "babel-preset-es2015": "^6.18.0", 55 | "chai": "^3.5.0", 56 | "css-loader": "^0.26.1", 57 | "eslint": "^3.12.2", 58 | "eslint-config-standard": "^6.2.1", 59 | "eslint-friendly-formatter": "^2.0.6", 60 | "eslint-loader": "^1.6.1", 61 | "eslint-plugin-html": "^1.7.0", 62 | "eslint-plugin-promise": "^3.4.0", 63 | "eslint-plugin-standard": "^2.0.1", 64 | "extract-text-webpack-plugin": "^2.1.0", 65 | "file-loader": "^0.9.0", 66 | "html-webpack-plugin": "^2.24.1", 67 | "karma": "^1.3.0", 68 | "karma-chrome-launcher": "^2.0.0", 69 | "karma-coverage": "^1.1.1", 70 | "karma-mocha": "^1.3.0", 71 | "karma-phantomjs-launcher": "^1.0.2", 72 | "karma-sinon-chai": "^1.2.4", 73 | "karma-sourcemap-loader": "^0.3.7", 74 | "karma-spec-reporter": "0.0.26", 75 | "karma-webpack": "^2.0.3", 76 | "mocha": "^3.2.0", 77 | "node-sass": "^4.1.1", 78 | "phantomjs-prebuilt": "^2.1.14", 79 | "pug": "^2.0.0-beta6", 80 | "rimraf": "^2.5.4", 81 | "sass-loader": "^4.1.1", 82 | "sinon": "^2.1.0", 83 | "sinon-chai": "^2.8.0", 84 | "style-loader": "^0.13.1", 85 | "url-loader": "^0.5.7", 86 | "vue": "^2.1.8", 87 | "vue-loader": "^10.0.2", 88 | "vue-template-compiler": "^2.1.6", 89 | "webpack": "^2.4.1", 90 | "webpack-dev-server": "^2.4.4", 91 | "webpack-merge": "^1.1.2" 92 | } 93 | } 94 | -------------------------------------------------------------------------------- /src/demo/Demo.vue: -------------------------------------------------------------------------------- 1 | 91 | 92 | 217 | 218 | 241 | 242 | 309 | -------------------------------------------------------------------------------- /src/demo/assets/demo.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cngu/vue-typer/db830cb8cd43327c86fdb3779b41b5cdc0e44417/src/demo/assets/demo.gif -------------------------------------------------------------------------------- /src/demo/assets/images/documentation-light.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cngu/vue-typer/db830cb8cd43327c86fdb3779b41b5cdc0e44417/src/demo/assets/images/documentation-light.png -------------------------------------------------------------------------------- /src/demo/assets/images/download-light.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cngu/vue-typer/db830cb8cd43327c86fdb3779b41b5cdc0e44417/src/demo/assets/images/download-light.png -------------------------------------------------------------------------------- /src/demo/assets/images/github-mark-light.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cngu/vue-typer/db830cb8cd43327c86fdb3779b41b5cdc0e44417/src/demo/assets/images/github-mark-light.png -------------------------------------------------------------------------------- /src/demo/components/AppLayout.vue: -------------------------------------------------------------------------------- 1 | 34 | 35 | -------------------------------------------------------------------------------- /src/demo/components/BadgeBar.vue: -------------------------------------------------------------------------------- 1 | 10 | 11 | 18 | -------------------------------------------------------------------------------- /src/demo/components/CircleLink.vue: -------------------------------------------------------------------------------- 1 | 5 | 6 | 13 | 14 | 49 | -------------------------------------------------------------------------------- /src/demo/components/CodeBlock.vue: -------------------------------------------------------------------------------- 1 | 5 | 6 | 44 | 45 | -------------------------------------------------------------------------------- /src/demo/components/CopyrightFooter.vue: -------------------------------------------------------------------------------- 1 | 8 | 9 | 25 | -------------------------------------------------------------------------------- /src/demo/components/FormCheck.vue: -------------------------------------------------------------------------------- 1 | 14 | 15 | 23 | 24 | 29 | -------------------------------------------------------------------------------- /src/demo/components/FormInput.vue: -------------------------------------------------------------------------------- 1 | 12 | 13 | 22 | 23 | 28 | -------------------------------------------------------------------------------- /src/demo/components/FormRadio.vue: -------------------------------------------------------------------------------- 1 | 15 | 16 | 31 | 32 | 37 | -------------------------------------------------------------------------------- /src/demo/components/HeroHeader.vue: -------------------------------------------------------------------------------- 1 | 8 | 9 | 20 | 21 | 43 | 44 | -------------------------------------------------------------------------------- /src/demo/components/LinkBar.vue: -------------------------------------------------------------------------------- 1 | 10 | 11 | 20 | 21 | -------------------------------------------------------------------------------- /src/demo/index.js: -------------------------------------------------------------------------------- 1 | import './styles/demo.scss' 2 | 3 | import Vue from 'vue' 4 | import Demo from './Demo' 5 | 6 | // eslint-disable-next-line no-new 7 | new Vue({ 8 | el: '#demo', 9 | render: createElement => createElement(Demo) 10 | }) 11 | -------------------------------------------------------------------------------- /src/demo/lib/bootstrap/index.js: -------------------------------------------------------------------------------- 1 | import './bootstrap-flex.min.css' 2 | -------------------------------------------------------------------------------- /src/demo/lib/prism/index.js: -------------------------------------------------------------------------------- 1 | import './prism.css' 2 | import Prism from './prism' 3 | 4 | export default Prism 5 | -------------------------------------------------------------------------------- /src/demo/lib/prism/prism.css: -------------------------------------------------------------------------------- 1 | /* http://prismjs.com/download.html?themes=prism&languages=markup+css&plugins=previewer-base+previewer-color+normalize-whitespace */ 2 | /** 3 | * prism.js default theme for JavaScript, CSS and HTML 4 | * Based on dabblet (http://dabblet.com) 5 | * @author Lea Verou 6 | */ 7 | 8 | code[class*="language-"], 9 | pre[class*="language-"] { 10 | color: black; 11 | background: none; 12 | text-shadow: 0 1px white; 13 | font-family: Consolas, Monaco, 'Andale Mono', 'Ubuntu Mono', monospace; 14 | text-align: left; 15 | white-space: pre; 16 | word-spacing: normal; 17 | word-break: normal; 18 | word-wrap: normal; 19 | line-height: 1.5; 20 | 21 | -moz-tab-size: 4; 22 | -o-tab-size: 4; 23 | tab-size: 4; 24 | 25 | -webkit-hyphens: none; 26 | -moz-hyphens: none; 27 | -ms-hyphens: none; 28 | hyphens: none; 29 | } 30 | 31 | pre[class*="language-"]::-moz-selection, pre[class*="language-"] ::-moz-selection, 32 | code[class*="language-"]::-moz-selection, code[class*="language-"] ::-moz-selection { 33 | text-shadow: none; 34 | background: #b3d4fc; 35 | } 36 | 37 | pre[class*="language-"]::selection, pre[class*="language-"] ::selection, 38 | code[class*="language-"]::selection, code[class*="language-"] ::selection { 39 | text-shadow: none; 40 | background: #b3d4fc; 41 | } 42 | 43 | @media print { 44 | code[class*="language-"], 45 | pre[class*="language-"] { 46 | text-shadow: none; 47 | } 48 | } 49 | 50 | /* Code blocks */ 51 | pre[class*="language-"] { 52 | padding: 1em; 53 | margin: .5em 0; 54 | overflow: auto; 55 | } 56 | 57 | :not(pre) > code[class*="language-"], 58 | pre[class*="language-"] { 59 | background: #f5f2f0; 60 | } 61 | 62 | /* Inline code */ 63 | :not(pre) > code[class*="language-"] { 64 | padding: .1em; 65 | border-radius: .3em; 66 | white-space: normal; 67 | } 68 | 69 | .token.comment, 70 | .token.prolog, 71 | .token.doctype, 72 | .token.cdata { 73 | color: slategray; 74 | } 75 | 76 | .token.punctuation { 77 | color: #999; 78 | } 79 | 80 | .namespace { 81 | opacity: .7; 82 | } 83 | 84 | .token.property, 85 | .token.tag, 86 | .token.boolean, 87 | .token.number, 88 | .token.constant, 89 | .token.symbol, 90 | .token.deleted { 91 | color: #905; 92 | } 93 | 94 | .token.selector, 95 | .token.attr-name, 96 | .token.string, 97 | .token.char, 98 | .token.builtin, 99 | .token.inserted { 100 | color: #690; 101 | } 102 | 103 | .token.operator, 104 | .token.entity, 105 | .token.url, 106 | .language-css .token.string, 107 | .style .token.string { 108 | color: #a67f59; 109 | background: hsla(0, 0%, 100%, .5); 110 | } 111 | 112 | .token.atrule, 113 | .token.attr-value, 114 | .token.keyword { 115 | color: #07a; 116 | } 117 | 118 | .token.function { 119 | color: #DD4A68; 120 | } 121 | 122 | .token.regex, 123 | .token.important, 124 | .token.variable { 125 | color: #e90; 126 | } 127 | 128 | .token.important, 129 | .token.bold { 130 | font-weight: bold; 131 | } 132 | .token.italic { 133 | font-style: italic; 134 | } 135 | 136 | .token.entity { 137 | cursor: help; 138 | } 139 | 140 | .prism-previewer, 141 | .prism-previewer:before, 142 | .prism-previewer:after { 143 | position: absolute; 144 | pointer-events: none; 145 | } 146 | .prism-previewer, 147 | .prism-previewer:after { 148 | left: 50%; 149 | } 150 | .prism-previewer { 151 | margin-top: -48px; 152 | width: 32px; 153 | height: 32px; 154 | margin-left: -16px; 155 | 156 | opacity: 0; 157 | -webkit-transition: opacity .25s; 158 | -o-transition: opacity .25s; 159 | transition: opacity .25s; 160 | } 161 | .prism-previewer.flipped { 162 | margin-top: 0; 163 | margin-bottom: -48px; 164 | } 165 | .prism-previewer:before, 166 | .prism-previewer:after { 167 | content: ''; 168 | position: absolute; 169 | pointer-events: none; 170 | } 171 | .prism-previewer:before { 172 | top: -5px; 173 | right: -5px; 174 | left: -5px; 175 | bottom: -5px; 176 | border-radius: 10px; 177 | border: 5px solid #fff; 178 | box-shadow: 0 0 3px rgba(0, 0, 0, 0.5) inset, 0 0 10px rgba(0, 0, 0, 0.75); 179 | } 180 | 181 | .prism-previewer:after { 182 | top: 100%; 183 | width: 0; 184 | height: 0; 185 | margin: 5px 0 0 -7px; 186 | border: 7px solid transparent; 187 | border-color: rgba(255, 0, 0, 0); 188 | border-top-color: #fff; 189 | } 190 | .prism-previewer.flipped:after { 191 | top: auto; 192 | bottom: 100%; 193 | margin-top: 0; 194 | margin-bottom: 5px; 195 | border-top-color: rgba(255, 0, 0, 0); 196 | border-bottom-color: #fff; 197 | } 198 | .prism-previewer.active { 199 | opacity: 1; 200 | } 201 | .prism-previewer-color { 202 | background-image: linear-gradient(45deg, #bbb 25%, transparent 25%, transparent 75%, #bbb 75%, #bbb), linear-gradient(45deg, #bbb 25%, #eee 25%, #eee 75%, #bbb 75%, #bbb); 203 | background-size: 10px 10px; 204 | background-position: 0 0, 5px 5px; 205 | } 206 | .prism-previewer-color:before { 207 | background-color: inherit; 208 | background-clip: padding-box; 209 | } 210 | 211 | -------------------------------------------------------------------------------- /src/demo/lib/prism/prism.js: -------------------------------------------------------------------------------- 1 | /* http://prismjs.com/download.html?themes=prism&languages=markup+css&plugins=previewer-base+previewer-color+normalize-whitespace */ 2 | var _self="undefined"!=typeof window?window:"undefined"!=typeof WorkerGlobalScope&&self instanceof WorkerGlobalScope?self:{},Prism=function(){var e=/\blang(?:uage)?-(\w+)\b/i,t=0,n=_self.Prism={util:{encode:function(e){return e instanceof a?new a(e.type,n.util.encode(e.content),e.alias):"Array"===n.util.type(e)?e.map(n.util.encode):e.replace(/&/g,"&").replace(/e.length)break e;if(!(v instanceof a)){u.lastIndex=0;var b=u.exec(v),k=1;if(!b&&h&&m!=r.length-1){if(u.lastIndex=y,b=u.exec(e),!b)break;for(var w=b.index+(c?b[1].length:0),_=b.index+b[0].length,A=m,P=y,j=r.length;j>A&&_>P;++A)P+=r[A].length,w>=P&&(++m,y=P);if(r[m]instanceof a||r[A-1].greedy)continue;k=A-m,v=e.slice(y,P),b.index-=y}if(b){c&&(f=b[1].length);var w=b.index+f,b=b[0].slice(f),_=w+b.length,x=v.slice(0,w),O=v.slice(_),S=[m,k];x&&S.push(x);var N=new a(l,g?n.tokenize(b,g):b,d,b,h);S.push(N),O&&S.push(O),Array.prototype.splice.apply(r,S)}}}}}return r},hooks:{all:{},add:function(e,t){var a=n.hooks.all;a[e]=a[e]||[],a[e].push(t)},run:function(e,t){var a=n.hooks.all[e];if(a&&a.length)for(var r,i=0;r=a[i++];)r(t)}}},a=n.Token=function(e,t,n,a,r){this.type=e,this.content=t,this.alias=n,this.length=0|(a||"").length,this.greedy=!!r};if(a.stringify=function(e,t,r){if("string"==typeof e)return e;if("Array"===n.util.type(e))return e.map(function(n){return a.stringify(n,t,e)}).join("");var i={type:e.type,content:a.stringify(e.content,t,r),tag:"span",classes:["token",e.type],attributes:{},language:t,parent:r};if("comment"==i.type&&(i.attributes.spellcheck="true"),e.alias){var l="Array"===n.util.type(e.alias)?e.alias:[e.alias];Array.prototype.push.apply(i.classes,l)}n.hooks.run("wrap",i);var o=Object.keys(i.attributes).map(function(e){return e+'="'+(i.attributes[e]||"").replace(/"/g,""")+'"'}).join(" ");return"<"+i.tag+' class="'+i.classes.join(" ")+'"'+(o?" "+o:"")+">"+i.content+""},!_self.document)return _self.addEventListener?(_self.addEventListener("message",function(e){var t=JSON.parse(e.data),a=t.language,r=t.code,i=t.immediateClose;_self.postMessage(n.highlight(r,n.languages[a],a)),i&&_self.close()},!1),_self.Prism):_self.Prism;var r=document.currentScript||[].slice.call(document.getElementsByTagName("script")).pop();return r&&(n.filename=r.src,document.addEventListener&&!r.hasAttribute("data-manual")&&("loading"!==document.readyState?window.requestAnimationFrame?window.requestAnimationFrame(n.highlightAll):window.setTimeout(n.highlightAll,16):document.addEventListener("DOMContentLoaded",n.highlightAll))),_self.Prism}();"undefined"!=typeof module&&module.exports&&(module.exports=Prism),"undefined"!=typeof global&&(global.Prism=Prism); 3 | Prism.languages.markup={comment://,prolog:/<\?[\w\W]+?\?>/,doctype://i,cdata://i,tag:{pattern:/<\/?(?!\d)[^\s>\/=$<]+(?:\s+[^\s>\/=]+(?:=(?:("|')(?:\\\1|\\?(?!\1)[\w\W])*\1|[^\s'">=]+))?)*\s*\/?>/i,inside:{tag:{pattern:/^<\/?[^\s>\/]+/i,inside:{punctuation:/^<\/?/,namespace:/^[^\s>\/:]+:/}},"attr-value":{pattern:/=(?:('|")[\w\W]*?(\1)|[^\s>]+)/i,inside:{punctuation:/[=>"']/}},punctuation:/\/?>/,"attr-name":{pattern:/[^\s>\/]+/,inside:{namespace:/^[^\s>\/:]+:/}}}},entity:/&#?[\da-z]{1,8};/i},Prism.hooks.add("wrap",function(a){"entity"===a.type&&(a.attributes.title=a.content.replace(/&/,"&"))}),Prism.languages.xml=Prism.languages.markup,Prism.languages.html=Prism.languages.markup,Prism.languages.mathml=Prism.languages.markup,Prism.languages.svg=Prism.languages.markup; 4 | Prism.languages.css={comment:/\/\*[\w\W]*?\*\//,atrule:{pattern:/@[\w-]+?.*?(;|(?=\s*\{))/i,inside:{rule:/@[\w-]+/}},url:/url\((?:(["'])(\\(?:\r\n|[\w\W])|(?!\1)[^\\\r\n])*\1|.*?)\)/i,selector:/[^\{\}\s][^\{\};]*?(?=\s*\{)/,string:{pattern:/("|')(\\(?:\r\n|[\w\W])|(?!\1)[^\\\r\n])*\1/,greedy:!0},property:/(\b|\B)[\w-]+(?=\s*:)/i,important:/\B!important\b/i,"function":/[-a-z0-9]+(?=\()/i,punctuation:/[(){};:]/},Prism.languages.css.atrule.inside.rest=Prism.util.clone(Prism.languages.css),Prism.languages.markup&&(Prism.languages.insertBefore("markup","tag",{style:{pattern:/()[\w\W]*?(?=<\/style>)/i,lookbehind:!0,inside:Prism.languages.css,alias:"language-css"}}),Prism.languages.insertBefore("inside","attr-value",{"style-attr":{pattern:/\s*style=("|').*?\1/i,inside:{"attr-name":{pattern:/^\s*style/i,inside:Prism.languages.markup.tag.inside},punctuation:/^\s*=\s*['"]|['"]\s*$/,"attr-value":{pattern:/.+/i,inside:Prism.languages.css}},alias:"language-css"}},Prism.languages.markup.tag)); 5 | !function(){if("undefined"!=typeof self&&self.Prism&&self.document&&Function.prototype.bind){var t=function(t){var e=0,s=0,i=t;if(i.parentNode){do e+=i.offsetLeft,s+=i.offsetTop;while((i=i.offsetParent)&&i.nodeType<9);i=t;do e-=i.scrollLeft,s-=i.scrollTop;while((i=i.parentNode)&&!/body/i.test(i.nodeName))}return{top:s,right:innerWidth-e-t.offsetWidth,bottom:innerHeight-s-t.offsetHeight,left:e}},e=/(?:^|\s)token(?=$|\s)/,s=/(?:^|\s)active(?=$|\s)/g,i=/(?:^|\s)flipped(?=$|\s)/g,o=function(t,e,s,i){this._elt=null,this._type=t,this._clsRegexp=RegExp("(?:^|\\s)"+t+"(?=$|\\s)"),this._token=null,this.updater=e,this._mouseout=this.mouseout.bind(this),this.initializer=i;var n=this;s||(s=["*"]),"Array"!==Prism.util.type(s)&&(s=[s]),s.forEach(function(t){"string"!=typeof t&&(t=t.lang),o.byLanguages[t]||(o.byLanguages[t]=[]),o.byLanguages[t].indexOf(n)<0&&o.byLanguages[t].push(n)}),o.byType[t]=this};o.prototype.init=function(){this._elt||(this._elt=document.createElement("div"),this._elt.className="prism-previewer prism-previewer-"+this._type,document.body.appendChild(this._elt),this.initializer&&this.initializer())},o.prototype.check=function(t){do if(e.test(t.className)&&this._clsRegexp.test(t.className))break;while(t=t.parentNode);t&&t!==this._token&&(this._token=t,this.show())},o.prototype.mouseout=function(){this._token.removeEventListener("mouseout",this._mouseout,!1),this._token=null,this.hide()},o.prototype.show=function(){if(this._elt||this.init(),this._token)if(this.updater.call(this._elt,this._token.textContent)){this._token.addEventListener("mouseout",this._mouseout,!1);var e=t(this._token);this._elt.className+=" active",e.top-this._elt.offsetHeight>0?(this._elt.className=this._elt.className.replace(i,""),this._elt.style.top=e.top+"px",this._elt.style.bottom=""):(this._elt.className+=" flipped",this._elt.style.bottom=e.bottom+"px",this._elt.style.top=""),this._elt.style.left=e.left+Math.min(200,this._token.offsetWidth/2)+"px"}else this.hide()},o.prototype.hide=function(){this._elt.className=this._elt.className.replace(s,"")},o.byLanguages={},o.byType={},o.initEvents=function(t,e){var s=[];o.byLanguages[e]&&(s=s.concat(o.byLanguages[e])),o.byLanguages["*"]&&(s=s.concat(o.byLanguages["*"])),t.addEventListener("mouseover",function(t){var e=t.target;s.forEach(function(t){t.check(e)})},!1)},Prism.plugins.Previewer=o,Prism.hooks.add("after-highlight",function(t){(o.byLanguages["*"]||o.byLanguages[t.language])&&o.initEvents(t.element,t.language)})}}(); 6 | !function(){if(("undefined"==typeof self||self.Prism)&&("undefined"==typeof global||global.Prism)){var e={css:!0,less:!0,markup:{lang:"markup",before:"punctuation",inside:"inside",root:Prism.languages.markup&&Prism.languages.markup.tag.inside["attr-value"]},sass:[{lang:"sass",before:"punctuation",inside:"inside",root:Prism.languages.sass&&Prism.languages.sass["variable-line"]},{lang:"sass",inside:"inside",root:Prism.languages.sass&&Prism.languages.sass["property-line"]}],scss:!0,stylus:[{lang:"stylus",before:"hexcode",inside:"rest",root:Prism.languages.stylus&&Prism.languages.stylus["property-declaration"].inside},{lang:"stylus",before:"hexcode",inside:"rest",root:Prism.languages.stylus&&Prism.languages.stylus["variable-declaration"].inside}]};Prism.hooks.add("before-highlight",function(a){if(a.language&&e[a.language]&&!e[a.language].initialized){var i=e[a.language];"Array"!==Prism.util.type(i)&&(i=[i]),i.forEach(function(i){var r,l,n,s;i===!0?(r="important",l=a.language,i=a.language):(r=i.before||"important",l=i.inside||i.lang,n=i.root||Prism.languages,s=i.skip,i=a.language),!s&&Prism.languages[i]&&(Prism.languages.insertBefore(l,r,{color:/\B#(?:[0-9a-f]{3}){1,2}\b|\b(?:rgb|hsl)\(\s*\d{1,3}\s*,\s*\d{1,3}%?\s*,\s*\d{1,3}%?\s*\)\B|\b(?:rgb|hsl)a\(\s*\d{1,3}\s*,\s*\d{1,3}%?\s*,\s*\d{1,3}%?\s*,\s*(?:0|0?\.\d+|1)\s*\)\B|\b(?:AliceBlue|AntiqueWhite|Aqua|Aquamarine|Azure|Beige|Bisque|Black|BlanchedAlmond|Blue|BlueViolet|Brown|BurlyWood|CadetBlue|Chartreuse|Chocolate|Coral|CornflowerBlue|Cornsilk|Crimson|Cyan|DarkBlue|DarkCyan|DarkGoldenRod|DarkGray|DarkGreen|DarkKhaki|DarkMagenta|DarkOliveGreen|DarkOrange|DarkOrchid|DarkRed|DarkSalmon|DarkSeaGreen|DarkSlateBlue|DarkSlateGray|DarkTurquoise|DarkViolet|DeepPink|DeepSkyBlue|DimGray|DodgerBlue|FireBrick|FloralWhite|ForestGreen|Fuchsia|Gainsboro|GhostWhite|Gold|GoldenRod|Gray|Green|GreenYellow|HoneyDew|HotPink|IndianRed|Indigo|Ivory|Khaki|Lavender|LavenderBlush|LawnGreen|LemonChiffon|LightBlue|LightCoral|LightCyan|LightGoldenRodYellow|LightGray|LightGreen|LightPink|LightSalmon|LightSeaGreen|LightSkyBlue|LightSlateGray|LightSteelBlue|LightYellow|Lime|LimeGreen|Linen|Magenta|Maroon|MediumAquaMarine|MediumBlue|MediumOrchid|MediumPurple|MediumSeaGreen|MediumSlateBlue|MediumSpringGreen|MediumTurquoise|MediumVioletRed|MidnightBlue|MintCream|MistyRose|Moccasin|NavajoWhite|Navy|OldLace|Olive|OliveDrab|Orange|OrangeRed|Orchid|PaleGoldenRod|PaleGreen|PaleTurquoise|PaleVioletRed|PapayaWhip|PeachPuff|Peru|Pink|Plum|PowderBlue|Purple|Red|RosyBrown|RoyalBlue|SaddleBrown|Salmon|SandyBrown|SeaGreen|SeaShell|Sienna|Silver|SkyBlue|SlateBlue|SlateGray|Snow|SpringGreen|SteelBlue|Tan|Teal|Thistle|Tomato|Turquoise|Violet|Wheat|White|WhiteSmoke|Yellow|YellowGreen)\b/i},n),a.grammar=Prism.languages[i],e[a.language]={initialized:!0})})}}),Prism.plugins.Previewer&&new Prism.plugins.Previewer("color",function(e){return this.style.backgroundColor="",this.style.backgroundColor=e,!!this.style.backgroundColor})}}(); 7 | !function(){function e(e){this.defaults=r({},e)}function n(e){return e.replace(/-(\w)/g,function(e,n){return n.toUpperCase()})}function t(e){for(var n=0,t=0;tn&&(o[s]="\n"+o[s],a=l)}r[i]=o.join("")}return r.join("\n")}},Prism.plugins.NormalizeWhitespace=new e({"remove-trailing":!0,"remove-indent":!0,"left-trim":!0,"right-trim":!0}),Prism.hooks.add("before-sanity-check",function(e){var n=e.element.parentNode,t=/\bno-whitespace-normalization\b/;if(!(!e.code||!n||"pre"!==n.nodeName.toLowerCase()||e.settings&&e.settings["whitespace-normalization"]===!1||t.test(n.className)||t.test(e.element.className))){for(var r=n.childNodes,i="",o="",a=!1,s=Prism.plugins.NormalizeWhitespace,l=0;l 2 | span.caret.custom(:class='animationClass') 3 | 4 | 5 | 26 | 27 | 65 | -------------------------------------------------------------------------------- /src/vue-typer/components/Char.vue: -------------------------------------------------------------------------------- 1 | 5 | 6 | 23 | 24 | 53 | -------------------------------------------------------------------------------- /src/vue-typer/components/VueTyper.vue: -------------------------------------------------------------------------------- 1 | 16 | 17 | 404 | 405 | 415 | -------------------------------------------------------------------------------- /src/vue-typer/index.js: -------------------------------------------------------------------------------- 1 | import VueTyperComponent from './components/VueTyper' 2 | 3 | export const VueTyper = VueTyperComponent 4 | 5 | export default { 6 | install(Vue) { 7 | Vue.component('vue-typer', VueTyperComponent) 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /src/vue-typer/styles/caret-animations.scss: -------------------------------------------------------------------------------- 1 | @keyframes vue-typer-caret-blink { 2 | 50% { 3 | opacity: 0; 4 | } 5 | 100% { 6 | opacity: 1; 7 | } 8 | } 9 | 10 | @keyframes vue-typer-caret-smooth { 11 | 0%, 20% { 12 | opacity: 1; 13 | } 14 | 60%, 100% { 15 | opacity: 0; 16 | } 17 | } 18 | 19 | @keyframes vue-typer-caret-phase { 20 | 0%, 20% { 21 | opacity: 1; 22 | } 23 | 90%, 100% { 24 | opacity: 0; 25 | } 26 | } 27 | 28 | @keyframes vue-typer-caret-expand { 29 | 0%, 20% { 30 | transform: scaleY(1); 31 | } 32 | 80%, 100% { 33 | transform: scaleY(0); 34 | } 35 | } 36 | 37 | .vue-typer-caret-blink { 38 | animation: vue-typer-caret-blink 1s step-start 0s infinite; 39 | } 40 | 41 | .vue-typer-caret-smooth { 42 | animation: vue-typer-caret-smooth 0.5s ease-in-out 0s infinite alternate; 43 | } 44 | 45 | .vue-typer-caret-phase { 46 | animation: vue-typer-caret-phase 0.5s ease-in-out 0s infinite alternate; 47 | } 48 | 49 | .vue-typer-caret-expand { 50 | animation: vue-typer-caret-expand 0.5s ease-in-out 0s infinite alternate; 51 | } 52 | -------------------------------------------------------------------------------- /src/vue-typer/styles/typer-colors.scss: -------------------------------------------------------------------------------- 1 | $char-typed-color: black; 2 | $char-selected-color: black; 3 | 4 | $char-typed-background-color: transparent; 5 | $char-selected-background-color: #ACCEF7; 6 | 7 | $caret-idle-color: black; 8 | $caret-typing-color: black; 9 | $caret-selecting-color: black; 10 | $caret-erasing-color: black; 11 | $caret-complete-color: black; 12 | -------------------------------------------------------------------------------- /src/vue-typer/utils/random-int.js: -------------------------------------------------------------------------------- 1 | function validNumber(val) { 2 | return typeof val === 'number' && !Number.isNaN(val) && Number.isFinite(val) 3 | } 4 | 5 | function validRange(lower, upper) { 6 | return validNumber(lower) && validNumber(upper) && lower <= upper 7 | } 8 | 9 | /** 10 | * @param min - Minimum random int 11 | * @param max - Maximum random int 12 | * @returns a random int in the range [min, max], or -1 if either of the following conditions are met: 13 | * - min and/or max are not of type 'number', NaN, or Infinity 14 | * - min > max 15 | */ 16 | export default (min, max) => { 17 | if (!validRange(min, max)) { 18 | return -1 19 | } 20 | 21 | // Since we're generating random integers, rounded the arguments to the closest int within the range 22 | min = Math.ceil(min) 23 | max = Math.floor(max) 24 | 25 | return Math.floor(Math.random() * (max - min + 1)) + min 26 | } 27 | -------------------------------------------------------------------------------- /src/vue-typer/utils/shallow-equals.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Performs a shallow comparison between 2 arrays. 3 | * @param {Array} a1 4 | * @param {Array} a2 5 | * @returns true if the array contents are strictly equal (===); false otherwise 6 | */ 7 | export default (a1, a2) => { 8 | if (!Array.isArray(a1) || !Array.isArray(a2)) { 9 | return false 10 | } 11 | 12 | if (a1.length !== a2.length) { 13 | return false 14 | } 15 | 16 | for (let i = 0; i < a1.length; i++) { 17 | if (a1[i] !== a2[i]) { 18 | return false 19 | } 20 | } 21 | 22 | return true 23 | } 24 | -------------------------------------------------------------------------------- /src/vue-typer/utils/shuffle.js: -------------------------------------------------------------------------------- 1 | import randomInt from './random-int' 2 | 3 | function swap(a, i, j) { 4 | if (i === j) { 5 | return 6 | } 7 | const temp = a[i] 8 | a[i] = a[j] 9 | a[j] = temp 10 | } 11 | 12 | /** 13 | * Performs an in-place shuffle. 14 | * Implemented using the Fisher-Yates/Knuth shuffle algorithm. 15 | * @param list - Array of items to shuffle in-place. 16 | */ 17 | export default (list) => { 18 | if (!(list instanceof Array)) { 19 | return 20 | } 21 | 22 | for (let i = list.length - 1; i > 0; i--) { 23 | let randomIndex = randomInt(0, i) 24 | swap(list, i, randomIndex) 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /test/unit/.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "env": { 3 | "mocha": true 4 | }, 5 | "globals": { 6 | "describe": true, 7 | "it": true, 8 | "expect": true, 9 | "sinon": true 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /test/unit/index.js: -------------------------------------------------------------------------------- 1 | // Polyfill fn.bind() for PhantomJS 2 | /* eslint-disable no-extend-native */ 3 | Function.prototype.bind = require('function-bind') 4 | 5 | // require all test files (files that ends with .spec.js) 6 | const testsContext = require.context('./specs', true, /\.spec$/) 7 | testsContext.keys().forEach(testsContext) 8 | 9 | // require all src files except index.js for coverage. 10 | // you can also change this to match only the subset of files that 11 | // you want coverage for. 12 | const srcContext = require.context('../../src/vue-typer', true, /^\.\/(?!index(\.js)?$)/) 13 | srcContext.keys().forEach(srcContext) 14 | -------------------------------------------------------------------------------- /test/unit/karma.conf.js: -------------------------------------------------------------------------------- 1 | // This is a karma config file. For more details see 2 | // http://karma-runner.github.io/0.13/config/configuration-file.html 3 | // we are also using it with karma-webpack 4 | // https://github.com/webpack/karma-webpack 5 | 6 | var webpackConfig = require('../../build/webpack.config.test') 7 | 8 | module.exports = function(config) { 9 | config.set({ 10 | // to run in additional browsers: 11 | // 1. install corresponding karma launcher 12 | // http://karma-runner.github.io/0.13/config/browsers.html 13 | // 2. add it to the `browsers` array below. 14 | browsers: ['PhantomJS'], 15 | frameworks: ['mocha', 'sinon-chai'], 16 | reporters: ['spec', 'coverage'], 17 | files: ['./index.js'], 18 | preprocessors: { 19 | './index.js': ['webpack', 'sourcemap'] 20 | }, 21 | webpack: webpackConfig, 22 | webpackMiddleware: { 23 | noInfo: true 24 | }, 25 | coverageReporter: { 26 | dir: './coverage', 27 | reporters: [ 28 | { type: 'lcov', subdir: '.' }, 29 | { type: 'text-summary' } 30 | ] 31 | } 32 | }) 33 | } 34 | -------------------------------------------------------------------------------- /test/unit/specs/VueTyper.spec.js: -------------------------------------------------------------------------------- 1 | import VueTyper from '../../../src/vue-typer/components/VueTyper' 2 | import _mount from '../utils/mount' 3 | import { waitUntilRendered, wait as _wait } from '../utils/wait' 4 | 5 | describe('VueTyper.vue', function() { 6 | let clock 7 | 8 | function mount(propsData) { 9 | return _mount(VueTyper, propsData) 10 | } 11 | 12 | function wait(ms, next) { 13 | _wait(clock, ms, next) 14 | } 15 | 16 | beforeEach(function() { 17 | clock = sinon.useFakeTimers() 18 | }) 19 | afterEach(function() { 20 | clock.restore() 21 | }) 22 | 23 | it('should have a name so it is identifiable in the Vue debugger', function() { 24 | expect(VueTyper.name).to.equal('VueTyper') 25 | }) 26 | 27 | describe('Unicode support', function() { 28 | let vm 29 | 30 | function _mount(text) { 31 | vm = mount({ text }) 32 | } 33 | 34 | function assertTextLength(length) { 35 | return expect(vm.currentTextLength).to.equal(length) 36 | } 37 | 38 | describe('Emojis', function() { 39 | const emojiTestData = { 40 | '1': ['💙', '⛳', '⛈'], 41 | '2': ['❤️', '💩'], 42 | '3': ['✍🏻', '🔥'], 43 | '4': ['👍🏻', '🤳🏻'], 44 | '5': ['💅🏻', '👨‍⚖️'], 45 | '7': ['👩🏻‍🎤', '👩🏻‍✈️'], 46 | '8': ['👩‍❤️‍👩', '👨‍👩‍👧'], 47 | '9': ['👩‍👩‍👦'], 48 | '11': ['👩‍❤️‍💋‍👩', '👨‍👩‍👧‍👦'] 49 | } 50 | 51 | for (let emojiCodepoint in emojiTestData) { 52 | describe(`should properly count ${emojiCodepoint} codepoint emojis`, function() { 53 | let emojiList = emojiTestData[emojiCodepoint] 54 | for (let emoji of emojiList) { 55 | it(`${emoji} has length 1`, function() { 56 | _mount(emoji) 57 | assertTextLength(1) 58 | }) 59 | } 60 | }) 61 | } 62 | }) 63 | }) 64 | 65 | describe('Repeat and EraseOnComplete', function() { 66 | // eslint-disable-next-line one-var 67 | const preTypeDelay = 1, preEraseDelay = 1, typeDelay = 1, eraseDelay = 1 68 | let vm 69 | function createOptions(repeat, eraseOnComplete) { 70 | return { text: 'a', repeat, eraseOnComplete, preTypeDelay, typeDelay, preEraseDelay, eraseDelay } 71 | } 72 | 73 | it('should not repeat and should not erase final text', function(done) { 74 | vm = mount(createOptions(0, false)) 75 | wait(preTypeDelay, () => { 76 | expect(vm.state).to.equal('complete') 77 | done() 78 | }) 79 | }) 80 | 81 | it('should not repeat and should erase final text', function(done) { 82 | vm = mount(createOptions(0, true)) 83 | wait(preTypeDelay, () => { 84 | // assert that we're not done yet, we still have to erase the final text! 85 | expect(vm.state).not.to.equal('complete') 86 | 87 | // preEraseDelay = select-all, eraseDelay = actual erase 88 | wait(preEraseDelay + eraseDelay, () => { 89 | expect(vm.state).to.equal('complete') 90 | done() 91 | }) 92 | }) 93 | }) 94 | 95 | it('should repeat as many times as specified and should not erase final text', function(done) { 96 | vm = mount(createOptions(1, false)) 97 | 98 | // type first time 99 | wait(preTypeDelay, () => { 100 | // select-all and erase first time 101 | wait(preEraseDelay + eraseDelay, () => { 102 | // assert that we're not done yet, we still have to repeat one more time! 103 | expect(vm.state).not.to.equal('complete') 104 | 105 | // type second time 106 | wait(preTypeDelay, () => { 107 | // assert that we're not done yet, we still have to erase the final text! 108 | expect(vm.state).to.equal('complete') 109 | done() 110 | }) 111 | }) 112 | }) 113 | }) 114 | 115 | it('should repeat as many times as specified and erase final text', function(done) { 116 | vm = mount(createOptions(1, true)) 117 | 118 | // type first time 119 | wait(preTypeDelay, () => { 120 | // select-all and erase first time 121 | wait(preEraseDelay + eraseDelay, () => { 122 | // assert that we're not done yet, we still have to repeat one more time! 123 | expect(vm.state).not.to.equal('complete') 124 | 125 | // type second time 126 | wait(preTypeDelay, () => { 127 | // assert that we're not done yet, we still have to erase the final text! 128 | expect(vm.state).not.to.equal('complete') 129 | 130 | // select-all and erase second time 131 | wait(preEraseDelay + eraseDelay, () => { 132 | expect(vm.state).to.equal('complete') 133 | done() 134 | }) 135 | }) 136 | }) 137 | }) 138 | }) 139 | }) 140 | 141 | describe('Caret', function() { 142 | it('should have the correct animation class', function() { 143 | let vm = mount({ text: 'abc', caretAnimation: 'solid' }) 144 | let caret = vm.$el.querySelector('.caret') 145 | expect(caret.classList.contains('vue-typer-caret-solid')).to.be.true 146 | 147 | vm = mount({ text: 'abc', caretAnimation: 'blink' }) 148 | caret = vm.$el.querySelector('.caret') 149 | expect(caret.classList.contains('vue-typer-caret-blink')).to.be.true 150 | 151 | vm = mount({ text: 'abc', caretAnimation: 'smooth' }) 152 | caret = vm.$el.querySelector('.caret') 153 | expect(caret.classList.contains('vue-typer-caret-smooth')).to.be.true 154 | 155 | vm = mount({ text: 'abc', caretAnimation: 'phase' }) 156 | caret = vm.$el.querySelector('.caret') 157 | expect(caret.classList.contains('vue-typer-caret-phase')).to.be.true 158 | 159 | vm = mount({ text: 'abc', caretAnimation: 'expand' }) 160 | caret = vm.$el.querySelector('.caret') 161 | expect(caret.classList.contains('vue-typer-caret-expand')).to.be.true 162 | }) 163 | it('should be positionable to the beginning', function() { 164 | const vm = mount({ text: 'abc' }) 165 | vm.moveCaretToStart() 166 | expect(vm.currentTextIndex).to.equal(0) 167 | }) 168 | it('should be positionable to the end', function() { 169 | const vm = mount({ text: 'abc' }) 170 | vm.moveCaretToEnd() 171 | expect(vm.currentTextIndex).to.equal(3) 172 | }) 173 | it('should be shiftable', function() { 174 | const vm = mount({ text: 'abc' }) 175 | vm.moveCaretToStart() 176 | vm.shiftCaret(2) 177 | expect(vm.currentTextIndex).to.equal(2) 178 | }) 179 | }) 180 | 181 | describe('Events', function() { 182 | const text = 'abc' 183 | let vm 184 | beforeEach(function() { 185 | vm = mount({ text }) 186 | }) 187 | 188 | it('should emit \'typed-char\' event for each char in a typed word', function(done) { 189 | let numTyped = 0 190 | vm.$on('typed-char', (char, index) => { 191 | expect(text.charAt(numTyped)).to.equal(char) 192 | expect(numTyped).to.equal(index) 193 | 194 | numTyped++ 195 | if (numTyped === text.length) { 196 | done() 197 | } 198 | }) 199 | 200 | let numChars = text.length 201 | while (numChars--) { 202 | vm.typeStep() 203 | } 204 | }) 205 | 206 | it('should emit \'typed\' event after a word is typed', function(done) { 207 | vm.$on('typed', (word) => { 208 | expect(word).to.equal(text) 209 | done() 210 | }) 211 | vm.onTyped() 212 | }) 213 | it('should emit \'erased\' event after a word is erased', function(done) { 214 | vm.$on('erased', (word) => { 215 | expect(word).to.equal(text) 216 | done() 217 | }) 218 | vm.onErased() 219 | }) 220 | it('should emit \'completed\' event after all words are typed/erased', function(done) { 221 | vm.$on('completed', (word) => { 222 | done() 223 | }) 224 | vm.onComplete() 225 | }) 226 | }) 227 | 228 | describe('Typing and Erasing', function() { 229 | function expectText(vm, side, expectedText, expectedCharClass) { 230 | const container = vm.$el.querySelector('.' + side) 231 | expect(container.classList.contains(side)).to.be.true 232 | expect(container.childElementCount).to.equal(expectedText.length) 233 | 234 | let child 235 | for (let i = 0; i < expectedText.length; i++) { 236 | child = container.children[i] 237 | if (expectedCharClass) { 238 | expect(child.classList.contains(expectedCharClass)) 239 | } 240 | expect(child.textContent).to.equal(expectedText.charAt(i)) 241 | } 242 | } 243 | function expectLeftText(vm, expectedText) { 244 | expectText(vm, 'left', expectedText, 'typed') 245 | } 246 | function expectRightText(vm, expectedText, expectedCharClass) { 247 | expectText(vm, 'right', expectedText, expectedCharClass) 248 | } 249 | 250 | describe('Initial Action', function() { 251 | it('should initialize to typing state', function(done) { 252 | const vm = mount({ 253 | text: 'abc', 254 | initialAction: 'typing' 255 | }) 256 | 257 | waitUntilRendered(() => { 258 | expectLeftText(vm, '') 259 | expectRightText(vm, 'abc') 260 | done() 261 | }) 262 | }) 263 | 264 | it('should initialize to erasing state', function(done) { 265 | const vm = mount({ 266 | text: 'abc', 267 | initialAction: 'erasing' 268 | }) 269 | 270 | waitUntilRendered(() => { 271 | expectLeftText(vm, 'abc') 272 | expectRightText(vm, '') 273 | done() 274 | }) 275 | }) 276 | }) 277 | 278 | describe('Typing Delay', function() { 279 | const preTypeDelay = 100 280 | const typeDelay = 50 281 | let vm 282 | beforeEach(function() { 283 | vm = mount({ 284 | text: 'abc', 285 | typeDelay, 286 | preTypeDelay 287 | }) 288 | }) 289 | 290 | it('should wait \'preTypeDelay\' before typing the first character', function(done) { 291 | wait(preTypeDelay, () => { 292 | expectLeftText(vm, 'a') 293 | expectRightText(vm, 'bc') 294 | done() 295 | }) 296 | }) 297 | 298 | it('should wait \'typeDelay\' before typing the second character', function(done) { 299 | wait(preTypeDelay + typeDelay, () => { 300 | expectLeftText(vm, 'ab') 301 | expectRightText(vm, 'c') 302 | done() 303 | }) 304 | }) 305 | }) 306 | 307 | describe('Erasing Delay', function() { 308 | const preEraseDelay = 100 309 | const eraseDelay = 50 310 | let vm 311 | 312 | function createBeforeEach(eraseStyle) { 313 | return function() { 314 | vm = mount({ 315 | text: 'abc', 316 | initialAction: 'erasing', 317 | eraseStyle, 318 | eraseDelay, 319 | preEraseDelay 320 | }) 321 | } 322 | } 323 | 324 | describe('backspace', function() { 325 | beforeEach(createBeforeEach('backspace')) 326 | it('should wait \'preEraseDelay\' before erasing the first character', function(done) { 327 | wait(preEraseDelay, () => { 328 | expectLeftText(vm, 'ab') 329 | expectRightText(vm, 'c', 'erased') 330 | done() 331 | }) 332 | }) 333 | it('should wait \'eraseDelay\' before erasing the second character', function(done) { 334 | wait(preEraseDelay + eraseDelay, () => { 335 | expectLeftText(vm, 'a') 336 | expectRightText(vm, 'bc', 'erased') 337 | done() 338 | }) 339 | }) 340 | }) 341 | 342 | describe('select-back', function() { 343 | beforeEach(createBeforeEach('select-back')) 344 | it('should wait \'preEraseDelay\' before selecting the first character', function(done) { 345 | wait(preEraseDelay, () => { 346 | expectLeftText(vm, 'ab') 347 | expectRightText(vm, 'c', 'erased') 348 | done() 349 | }) 350 | }) 351 | it('should wait \'eraseDelay\' before selecting the second character', function(done) { 352 | wait(preEraseDelay + eraseDelay, () => { 353 | expectLeftText(vm, 'a') 354 | expectRightText(vm, 'bc', 'selected') 355 | done() 356 | }) 357 | }) 358 | }) 359 | 360 | describe('select-all', function() { 361 | beforeEach(createBeforeEach('select-all')) 362 | it('should wait \'preEraseDelay\' before selecting all characters', function(done) { 363 | wait(preEraseDelay, () => { 364 | expectLeftText(vm, '') 365 | expectRightText(vm, 'abc', 'selected') 366 | done() 367 | }) 368 | }) 369 | it('should wait \'eraseDelay\' before erasing the entire selection', function(done) { 370 | wait(preEraseDelay + eraseDelay, () => { 371 | expectLeftText(vm, '') 372 | expectRightText(vm, 'abc', 'erased') 373 | done() 374 | }) 375 | }) 376 | }) 377 | 378 | describe('clear', function() { 379 | beforeEach(createBeforeEach('clear')) 380 | it('should wait \'preEraseDelay\' before clearing all characters', function(done) { 381 | wait(preEraseDelay, () => { 382 | expectLeftText(vm, '') 383 | expectRightText(vm, 'abc', 'erased') 384 | done() 385 | }) 386 | }) 387 | }) 388 | }) 389 | }) 390 | }) 391 | -------------------------------------------------------------------------------- /test/unit/utils/mount.js: -------------------------------------------------------------------------------- 1 | import Vue from 'vue' 2 | 3 | export default function mount(componentOptions, propsData) { 4 | const Ctor = Vue.extend(componentOptions) 5 | const vm = new Ctor({ propsData }) 6 | return vm.$mount() 7 | } 8 | -------------------------------------------------------------------------------- /test/unit/utils/wait.js: -------------------------------------------------------------------------------- 1 | import Vue from 'vue' 2 | 3 | export function waitUntilRendered(next) { 4 | Vue.nextTick(next) 5 | } 6 | 7 | export function wait(clock, ms, next) { 8 | clock.tick(ms) 9 | waitUntilRendered(next) 10 | } 11 | --------------------------------------------------------------------------------