├── .babelrc ├── .editorconfig ├── .gitignore ├── CHANGELOG.md ├── LICENSE ├── README.md ├── build └── rollup.config.js ├── dist ├── vue-countable.esm.js ├── vue-countable.min.js └── vue-countable.umd.js ├── docs ├── build.js ├── build.js.map └── index.html ├── example ├── App.vue ├── index.html └── main.js ├── package-lock.json ├── package.json ├── src ├── index.js └── vue-countable.vue ├── test └── VueCountable.spec.js └── webpack.config.js /.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": [ 3 | ["env", { "modules": false }], 4 | "stage-3" 5 | ], 6 | "env": { 7 | "test": { 8 | "presets": [ 9 | ["env", { "targets": { "node": "current" }}] 10 | ] 11 | } 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | # editorconfig.org 2 | 3 | root = true 4 | 5 | [*] 6 | indent_style = space 7 | indent_size = 4 8 | end_of_line = lf 9 | charset = utf-8 10 | trim_trailing_whitespace = true 11 | insert_final_newline = true 12 | 13 | [*.md] 14 | trim_trailing_whitespace = false -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # CHANGELOG.md 2 | 3 | ## 1.0.8 (2019-02-19) 4 | 5 | - Updating iife build 6 | 7 | ## 1.0.7 (2019-01-01) 8 | 9 | - Removed need to create actual div to hold the text - due to 3.0.1 `countable.js` update. 10 | 11 | ## 1.0.5 (2018-11-03) 12 | 13 | Features: 14 | 15 | - Renamed prop `id` to `elementId` 16 | - Added tests -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 Promosis, Inc. 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 | # vue-countable 2 | 3 | Vue binding for [countable.js](https://sacha.me/Countable/). Provides real-time paragraph, sentence, word, and character counting. 4 | 5 |

6 | NPM Version 7 | NPM Downloads 8 | License 9 | 10 | Tweet 11 |

12 | 13 | ### Demo 14 | 15 | [View Demo](https://johndatserakis.github.io/vue-countable/) | [View on NPM](https://www.npmjs.com/package/vue-countable) | [View on GitHub](https://github.com/johndatserakis/vue-countable) 16 | 17 | ### Install 18 | 19 | ``` 20 | # npm 21 | npm i vue-countable 22 | 23 | # yarn 24 | yarn add vue-countable 25 | ``` 26 | 27 | Or you can include it through the browser at the bottom of your page: 28 | 29 | `` 30 | 31 | ### About 32 | 33 | Simple way to count characters, words, sentences, and paragraphs in your Vue apps. 34 | 35 | Pass your `text` as a prop to the provided component, along with a unique `elementId`, and register for the `change` event to get real-time count updates. 36 | 37 | ### Usage Example 38 | 39 | ```html 40 | import VueCountable from 'vue-countable' 41 | Vue.component('vue-countable', VueCountable) 42 | ``` 43 | 44 | ```html 45 | 46 | 51 | 52 | 53 | change (event) { 54 | console.log(event) 55 | // event.words to get word count, etc. 56 | } 57 | ``` 58 | 59 | Now, anytime (and on component initialization) your `myText` variable changes, `vue-countable` will emit an event containing the details of your text. Register for this event as shown above to get your real-time counts. 60 | 61 | ### Props 62 | 63 | | prop | type | description | 64 | |---------|-------|--------------------------------| 65 | | text | String | Text you want to track. | 66 | | elementId | String | A unique id for your tracking instance. This allows you to have multiple different counts on the same page.| 67 | | hardReturns | Boolean (Optional, defaults to false) | Require two returns to count paragraphs | 68 | | stripTags | Boolean (Optional, defaults to false) | Remove HTML before counting | 69 | | ignore | Array of Strings (Optional, defaults to empty) | Characters to be ignored. | 70 | 71 | ### Events 72 | 73 | | event | value | description | 74 | |---------|-------|--------------------------------| 75 | | change | Object | Provides `character`, `word`, `sentence`, `paragraph`, and `all` values. | 76 | 77 | ### Development 78 | 79 | ``` bash 80 | # install dependencies 81 | npm install 82 | 83 | # serve with hot reload 84 | npm run watch 85 | 86 | # run the tests 87 | npm run test 88 | 89 | # build demo page 90 | npm run build:example 91 | 92 | # build library 93 | npm run build:library 94 | 95 | # build everything and run tests 96 | npm run build 97 | ``` 98 | 99 | ### Other 100 | 101 | Go ahead and fork the project! Submit an issue if needed. Have fun! 102 | 103 | ### Thank You 104 | 105 | [Sacha Schmid](https://sacha.me/) for [countable.js](https://sacha.me/Countable/). 106 | 107 | ### License 108 | 109 | [MIT](http://opensource.org/licenses/MIT) 110 | 111 | Packaged with a mixture of [vue-lib-template](https://github.com/biigpongsatorn/vue-lib-template) and [vue-sfc-rollup](https://github.com/team-innovation/vue-sfc-rollup). Using [webpack 4](https://webpack.js.org/). -------------------------------------------------------------------------------- /build/rollup.config.js: -------------------------------------------------------------------------------- 1 | import vue from 'rollup-plugin-vue'; 2 | import buble from 'rollup-plugin-buble'; 3 | import commonjs from 'rollup-plugin-commonjs'; 4 | import resolve from 'rollup-plugin-node-resolve'; 5 | import { terser } from 'rollup-plugin-terser'; 6 | import minimist from 'minimist'; 7 | 8 | const argv = minimist(process.argv.slice(2)); 9 | 10 | const config = { 11 | input: 'src/index.js', 12 | output: { 13 | name: 'VueCountable', 14 | exports: 'named', 15 | globals: { 16 | 'vue': 'Vue', 17 | 'countable': 'countable' 18 | } 19 | }, 20 | plugins: [ 21 | commonjs(), 22 | resolve({ 23 | jsnext: true, 24 | main: true, 25 | browser: true, 26 | }), 27 | vue({ 28 | css: true, 29 | compileTemplate: true, 30 | }), 31 | buble(), 32 | ], 33 | external: ['vue', 'countable'] 34 | }; 35 | 36 | // Only minify browser (iife) version 37 | if (argv.format === 'iife') { 38 | config.plugins.push(terser()); 39 | 40 | // Here we remove our `external` dependency that we have in this project 41 | // Be careful with the index here - it has to match any dependency that 42 | // you want to be built into to the iife output 43 | config.external.splice(1) 44 | } 45 | 46 | export default config; 47 | -------------------------------------------------------------------------------- /dist/vue-countable.esm.js: -------------------------------------------------------------------------------- 1 | import countable from 'countable'; 2 | 3 | // 4 | 5 | var script = { 6 | name: 'VueCountable', 7 | props: { 8 | text: { 9 | type: String, 10 | required: true 11 | }, 12 | elementId: { 13 | type: String, 14 | required: true 15 | }, 16 | hardReturns: { 17 | type: Boolean, 18 | required: false, 19 | default: false 20 | }, 21 | stripTags: { 22 | type: Boolean, 23 | required: false, 24 | default: false 25 | }, 26 | ignore: { 27 | type: Array, 28 | required: false, 29 | default: function () { return []; } 30 | } 31 | }, 32 | data: function data () { 33 | return { 34 | countable: null 35 | } 36 | }, 37 | computed: { 38 | options: function options () { 39 | return { 40 | hardReturns: this.hardReturns, 41 | stripTags: this.stripTags, 42 | ignore: this.ignore 43 | } 44 | } 45 | }, 46 | watch: { 47 | text: { 48 | handler: function (value) { 49 | // Everytime this.text changes we'll update our live count 50 | this.init(); 51 | } 52 | } 53 | }, 54 | methods: { 55 | init: function init () { 56 | var this$1 = this; 57 | 58 | // Set our countable instance 59 | this.countable = countable; 60 | 61 | this.$nextTick(function () { 62 | this$1.countable.count(this$1.text, function (counter) { 63 | this$1.$emit('change', counter); 64 | }, this$1.options); 65 | }); 66 | } 67 | }, 68 | mounted: function mounted () { 69 | // The init function is important because we want to provide counts not 70 | // only during changes, but also on initialization. 71 | this.init(); 72 | } 73 | } 74 | 75 | function normalizeComponent(template, style, script, scopeId, isFunctionalTemplate, moduleIdentifier 76 | /* server only */ 77 | , shadowMode, createInjector, createInjectorSSR, createInjectorShadow) { 78 | if (typeof shadowMode !== 'boolean') { 79 | createInjectorSSR = createInjector; 80 | createInjector = shadowMode; 81 | shadowMode = false; 82 | } // Vue.extend constructor export interop. 83 | 84 | 85 | var options = typeof script === 'function' ? script.options : script; // render functions 86 | 87 | if (template && template.render) { 88 | options.render = template.render; 89 | options.staticRenderFns = template.staticRenderFns; 90 | options._compiled = true; // functional template 91 | 92 | if (isFunctionalTemplate) { 93 | options.functional = true; 94 | } 95 | } // scopedId 96 | 97 | 98 | if (scopeId) { 99 | options._scopeId = scopeId; 100 | } 101 | 102 | var hook; 103 | 104 | if (moduleIdentifier) { 105 | // server build 106 | hook = function hook(context) { 107 | // 2.3 injection 108 | context = context || // cached call 109 | this.$vnode && this.$vnode.ssrContext || // stateful 110 | this.parent && this.parent.$vnode && this.parent.$vnode.ssrContext; // functional 111 | // 2.2 with runInNewContext: true 112 | 113 | if (!context && typeof __VUE_SSR_CONTEXT__ !== 'undefined') { 114 | context = __VUE_SSR_CONTEXT__; 115 | } // inject component styles 116 | 117 | 118 | if (style) { 119 | style.call(this, createInjectorSSR(context)); 120 | } // register component module identifier for async chunk inference 121 | 122 | 123 | if (context && context._registeredComponents) { 124 | context._registeredComponents.add(moduleIdentifier); 125 | } 126 | }; // used by ssr in case component is cached and beforeCreate 127 | // never gets called 128 | 129 | 130 | options._ssrRegister = hook; 131 | } else if (style) { 132 | hook = shadowMode ? function () { 133 | style.call(this, createInjectorShadow(this.$root.$options.shadowRoot)); 134 | } : function (context) { 135 | style.call(this, createInjector(context)); 136 | }; 137 | } 138 | 139 | if (hook) { 140 | if (options.functional) { 141 | // register for functional component in vue file 142 | var originalRender = options.render; 143 | 144 | options.render = function renderWithStyleInjection(h, context) { 145 | hook.call(context); 146 | return originalRender(h, context); 147 | }; 148 | } else { 149 | // inject component registration as beforeCreate hook 150 | var existing = options.beforeCreate; 151 | options.beforeCreate = existing ? [].concat(existing, hook) : [hook]; 152 | } 153 | } 154 | 155 | return script; 156 | } 157 | 158 | var normalizeComponent_1 = normalizeComponent; 159 | 160 | var isOldIE = typeof navigator !== 'undefined' && /msie [6-9]\\b/.test(navigator.userAgent.toLowerCase()); 161 | function createInjector(context) { 162 | return function (id, style) { 163 | return addStyle(id, style); 164 | }; 165 | } 166 | var HEAD = document.head || document.getElementsByTagName('head')[0]; 167 | var styles = {}; 168 | 169 | function addStyle(id, css) { 170 | var group = isOldIE ? css.media || 'default' : id; 171 | var style = styles[group] || (styles[group] = { 172 | ids: new Set(), 173 | styles: [] 174 | }); 175 | 176 | if (!style.ids.has(id)) { 177 | style.ids.add(id); 178 | var code = css.source; 179 | 180 | if (css.map) { 181 | // https://developer.chrome.com/devtools/docs/javascript-debugging 182 | // this makes source maps inside style tags work properly in Chrome 183 | code += '\n/*# sourceURL=' + css.map.sources[0] + ' */'; // http://stackoverflow.com/a/26603875 184 | 185 | code += '\n/*# sourceMappingURL=data:application/json;base64,' + btoa(unescape(encodeURIComponent(JSON.stringify(css.map)))) + ' */'; 186 | } 187 | 188 | if (!style.element) { 189 | style.element = document.createElement('style'); 190 | style.element.type = 'text/css'; 191 | if (css.media) { style.element.setAttribute('media', css.media); } 192 | HEAD.appendChild(style.element); 193 | } 194 | 195 | if ('styleSheet' in style.element) { 196 | style.styles.push(code); 197 | style.element.styleSheet.cssText = style.styles.filter(Boolean).join('\n'); 198 | } else { 199 | var index = style.ids.size - 1; 200 | var textNode = document.createTextNode(code); 201 | var nodes = style.element.childNodes; 202 | if (nodes[index]) { style.element.removeChild(nodes[index]); } 203 | if (nodes.length) { style.element.insertBefore(textNode, nodes[index]); }else { style.element.appendChild(textNode); } 204 | } 205 | } 206 | } 207 | 208 | var browser = createInjector; 209 | 210 | /* script */ 211 | var __vue_script__ = script; 212 | 213 | /* template */ 214 | var __vue_render__ = function() { 215 | var _vm = this; 216 | var _h = _vm.$createElement; 217 | var _c = _vm._self._c || _h; 218 | return _c("div") 219 | }; 220 | var __vue_staticRenderFns__ = []; 221 | __vue_render__._withStripped = true; 222 | 223 | /* style */ 224 | var __vue_inject_styles__ = function (inject) { 225 | if (!inject) { return } 226 | inject("data-v-12d8198d_0", { source: "\n/*# sourceMappingURL=vue-countable.vue.map */", map: {"version":3,"sources":["vue-countable.vue"],"names":[],"mappings":";AACA,4CAA4C","file":"vue-countable.vue"}, media: undefined }); 227 | 228 | }; 229 | /* scoped */ 230 | var __vue_scope_id__ = "data-v-12d8198d"; 231 | /* module identifier */ 232 | var __vue_module_identifier__ = undefined; 233 | /* functional template */ 234 | var __vue_is_functional_template__ = false; 235 | /* style inject SSR */ 236 | 237 | 238 | 239 | var component = normalizeComponent_1( 240 | { render: __vue_render__, staticRenderFns: __vue_staticRenderFns__ }, 241 | __vue_inject_styles__, 242 | __vue_script__, 243 | __vue_scope_id__, 244 | __vue_is_functional_template__, 245 | __vue_module_identifier__, 246 | browser, 247 | undefined 248 | ) 249 | 250 | // Import vue component 251 | 252 | // install function executed by Vue.use() 253 | function install(Vue) { 254 | if (install.installed) { return; } 255 | install.installed = true; 256 | Vue.component('VueCountable', component); 257 | } 258 | 259 | // Create module definition for Vue.use() 260 | var plugin = { 261 | install: install, 262 | }; 263 | 264 | // To auto-install when vue is found 265 | var GlobalVue = null; 266 | if (typeof window !== 'undefined') { 267 | GlobalVue = window.Vue; 268 | } else if (typeof global !== 'undefined') { 269 | GlobalVue = global.Vue; 270 | } 271 | if (GlobalVue) { 272 | GlobalVue.use(plugin); 273 | } 274 | 275 | // It's possible to expose named exports when writing components that can 276 | // also be used as directives, etc. - eg. import { RollupDemoDirective } from 'rollup-demo'; 277 | // export const RollupDemoDirective = component; 278 | 279 | export default component; 280 | export { install }; 281 | -------------------------------------------------------------------------------- /dist/vue-countable.min.js: -------------------------------------------------------------------------------- 1 | var VueCountable = (function (exports) { 2 | 'use strict'; 3 | 4 | var commonjsGlobal = typeof window !== 'undefined' ? window : typeof global !== 'undefined' ? global : typeof self !== 'undefined' ? self : {}; 5 | 6 | function createCommonjsModule(fn, module) { 7 | return module = { exports: {} }, fn(module, module.exports), module.exports; 8 | } 9 | 10 | var Countable = createCommonjsModule(function (module, exports) { 11 | (function (global) { 12 | 13 | /** 14 | * @private 15 | * 16 | * `liveElements` holds all elements that have the live-counting 17 | * functionality bound to them. 18 | */ 19 | 20 | var liveElements = []; 21 | var each = Array.prototype.forEach; 22 | 23 | /** 24 | * `ucs2decode` function from the punycode.js library. 25 | * 26 | * Creates an array containing the decimal code points of each Unicode 27 | * character in the string. While JavaScript uses UCS-2 internally, this 28 | * function will convert a pair of surrogate halves (each of which UCS-2 29 | * exposes as separate characters) into a single code point, matching 30 | * UTF-16. 31 | * 32 | * @see 33 | * @see 34 | * 35 | * @param {String} string The Unicode input string (UCS-2). 36 | * 37 | * @return {Array} The new array of code points. 38 | */ 39 | 40 | function decode (string) { 41 | var output = []; 42 | var counter = 0; 43 | var length = string.length; 44 | 45 | while (counter < length) { 46 | var value = string.charCodeAt(counter++); 47 | 48 | if (value >= 0xD800 && value <= 0xDBFF && counter < length) { 49 | 50 | // It's a high surrogate, and there is a next character. 51 | 52 | var extra = string.charCodeAt(counter++); 53 | 54 | if ((extra & 0xFC00) == 0xDC00) { // Low surrogate. 55 | output.push(((value & 0x3FF) << 10) + (extra & 0x3FF) + 0x10000); 56 | } else { 57 | 58 | // It's an unmatched surrogate; only append this code unit, in case the 59 | // next code unit is the high surrogate of a surrogate pair. 60 | 61 | output.push(value); 62 | counter--; 63 | } 64 | } else { 65 | output.push(value); 66 | } 67 | } 68 | 69 | return output 70 | } 71 | 72 | /** 73 | * `validateArguments` validates the arguments given to each function call. 74 | * Errors are logged to the console as warnings, but Countable fails 75 | * silently. 76 | * 77 | * @private 78 | * 79 | * @param {Nodes|String} targets A (collection of) element(s) or a single 80 | * string to validate. 81 | * 82 | * @param {Function} callback The callback function to validate. 83 | * 84 | * @return {Boolean} Returns whether all arguments are vaild. 85 | */ 86 | 87 | function validateArguments (targets, callback) { 88 | var nodes = Object.prototype.toString.call(targets); 89 | var targetsValid = typeof targets === 'string' || ((nodes === '[object NodeList]' || nodes === '[object HTMLCollection]') || targets.nodeType === 1); 90 | var callbackValid = typeof callback === 'function'; 91 | 92 | if (!targetsValid) { console.error('Countable: Not a valid target'); } 93 | if (!callbackValid) { console.error('Countable: Not a valid callback function'); } 94 | 95 | return targetsValid && callbackValid 96 | } 97 | 98 | /** 99 | * `count` trims an element's value, optionally strips HTML tags and counts 100 | * paragraphs, sentences, words, characters and characters plus spaces. 101 | * 102 | * @private 103 | * 104 | * @param {Node|String} target The target for the count. 105 | * 106 | * @param {Object} options The options to use for the counting. 107 | * 108 | * @return {Object} The object containing the number of paragraphs, 109 | * sentences, words, characters and characters 110 | * plus spaces. 111 | */ 112 | 113 | function count (target, options) { 114 | var original = '' + (typeof target === 'string' ? target : ('value' in target ? target.value : target.textContent)); 115 | options = options || {}; 116 | 117 | /** 118 | * The initial implementation to allow for HTML tags stripping was created 119 | * @craniumslows while the current one was created by @Rob--W. 120 | * 121 | * @see 122 | * @see 123 | */ 124 | 125 | if (options.stripTags) { original = original.replace(/<\/?[a-z][^>]*>/gi, ''); } 126 | 127 | if (options.ignore) { 128 | each.call(options.ignore, function (i) { 129 | original = original.replace(i, ''); 130 | }); 131 | } 132 | 133 | var trimmed = original.trim(); 134 | 135 | /** 136 | * Most of the performance improvements are based on the works of @epmatsw. 137 | * 138 | * @see 139 | */ 140 | 141 | return { 142 | paragraphs: trimmed ? (trimmed.match(options.hardReturns ? /\n{2,}/g : /\n+/g) || []).length + 1 : 0, 143 | sentences: trimmed ? (trimmed.match(/[.?!…]+./g) || []).length + 1 : 0, 144 | words: trimmed ? (trimmed.replace(/['";:,.?¿\-!¡]+/g, '').match(/\S+/g) || []).length : 0, 145 | characters: trimmed ? decode(trimmed.replace(/\s/g, '')).length : 0, 146 | all: decode(original).length 147 | } 148 | } 149 | 150 | /** 151 | * This is the main object that will later be exposed to other scripts. It 152 | * holds all the public methods that can be used to enable the Countable 153 | * functionality. 154 | * 155 | * Some methods accept an optional options parameter. This includes the 156 | * following options. 157 | * 158 | * {Boolean} hardReturns Use two returns to seperate a paragraph 159 | * instead of one. (default: false) 160 | * {Boolean} stripTags Strip HTML tags before counting the values. 161 | * (default: false) 162 | * {Array} ignore A list of characters that should be removed 163 | * ignored when calculating the counters. 164 | * (default: ) 165 | */ 166 | 167 | var Countable = { 168 | 169 | /** 170 | * The `on` method binds the counting handler to all given elements. The 171 | * event is either `oninput` or `onkeydown`, based on the capabilities of 172 | * the browser. 173 | * 174 | * @param {Nodes} elements All elements that should receive the 175 | * Countable functionality. 176 | * 177 | * @param {Function} callback The callback to fire whenever the 178 | * element's value changes. The callback is 179 | * called with the relevant element bound 180 | * to `this` and the counted values as the 181 | * single parameter. 182 | * 183 | * @param {Object} [options] An object to modify Countable's 184 | * behaviour. 185 | * 186 | * @return {Object} Returns the Countable object to allow for chaining. 187 | */ 188 | 189 | on: function (elements, callback, options) { 190 | if (!validateArguments(elements, callback)) { return } 191 | 192 | if (!Array.isArray(elements)) { 193 | elements = [ elements ]; 194 | } 195 | 196 | each.call(elements, function (e) { 197 | var handler = function () { 198 | callback.call(e, count(e, options)); 199 | }; 200 | 201 | liveElements.push({ element: e, handler: handler }); 202 | 203 | handler(); 204 | 205 | e.addEventListener('input', handler); 206 | }); 207 | 208 | return this 209 | }, 210 | 211 | /** 212 | * The `off` method removes the Countable functionality from all given 213 | * elements. 214 | * 215 | * @param {Nodes} elements All elements whose Countable functionality 216 | * should be unbound. 217 | * 218 | * @return {Object} Returns the Countable object to allow for chaining. 219 | */ 220 | 221 | off: function (elements) { 222 | if (!validateArguments(elements, function () {})) { return } 223 | 224 | if (!Array.isArray(elements)) { 225 | elements = [ elements ]; 226 | } 227 | 228 | liveElements.filter(function (e) { 229 | return elements.indexOf(e.element) !== -1 230 | }).forEach(function (e) { 231 | e.element.removeEventListener('input', e.handler); 232 | }); 233 | 234 | liveElements = liveElements.filter(function (e) { 235 | return elements.indexOf(e.element) === -1 236 | }); 237 | 238 | return this 239 | }, 240 | 241 | /** 242 | * The `count` method works mostly like the `live` method, but no events are 243 | * bound, the functionality is only executed once. 244 | * 245 | * @param {Nodes|String} targets All elements that should be counted. 246 | * 247 | * @param {Function} callback The callback to fire whenever the 248 | * element's value changes. The callback 249 | * is called with the relevant element 250 | * bound to `this` and the counted values 251 | * as the single parameter. 252 | * 253 | * @param {Object} [options] An object to modify Countable's 254 | * behaviour. 255 | * 256 | * @return {Object} Returns the Countable object to allow for chaining. 257 | */ 258 | 259 | count: function (targets, callback, options) { 260 | if (!validateArguments(targets, callback)) { return } 261 | 262 | if (!Array.isArray(targets)) { 263 | targets = [ targets ]; 264 | } 265 | 266 | each.call(targets, function (e) { 267 | callback.call(e, count(e, options)); 268 | }); 269 | 270 | return this 271 | }, 272 | 273 | /** 274 | * The `enabled` method checks if the live-counting functionality is bound 275 | * to an element. 276 | * 277 | * @param {Node} element All elements that should be checked for the 278 | * Countable functionality. 279 | * 280 | * @return {Boolean} A boolean value representing whether Countable 281 | * functionality is bound to all given elements. 282 | */ 283 | 284 | enabled: function (elements) { 285 | if (elements.length === undefined) { 286 | elements = [ elements ]; 287 | } 288 | 289 | return liveElements.filter(function (e) { 290 | return elements.indexOf(e.element) !== -1 291 | }).length === elements.length 292 | } 293 | 294 | }; 295 | 296 | /** 297 | * Expose Countable depending on the module system used across the 298 | * application. (Node / CommonJS, AMD, global) 299 | */ 300 | 301 | { 302 | module.exports = Countable; 303 | } 304 | }(commonjsGlobal)); 305 | }); 306 | 307 | // 308 | 309 | var script = { 310 | name: 'VueCountable', 311 | props: { 312 | text: { 313 | type: String, 314 | required: true 315 | }, 316 | elementId: { 317 | type: String, 318 | required: true 319 | }, 320 | hardReturns: { 321 | type: Boolean, 322 | required: false, 323 | default: false 324 | }, 325 | stripTags: { 326 | type: Boolean, 327 | required: false, 328 | default: false 329 | }, 330 | ignore: { 331 | type: Array, 332 | required: false, 333 | default: function () { return []; } 334 | } 335 | }, 336 | data: function data () { 337 | return { 338 | countable: null 339 | } 340 | }, 341 | computed: { 342 | options: function options () { 343 | return { 344 | hardReturns: this.hardReturns, 345 | stripTags: this.stripTags, 346 | ignore: this.ignore 347 | } 348 | } 349 | }, 350 | watch: { 351 | text: { 352 | handler: function (value) { 353 | // Everytime this.text changes we'll update our live count 354 | this.init(); 355 | } 356 | } 357 | }, 358 | methods: { 359 | init: function init () { 360 | var this$1 = this; 361 | 362 | // Set our countable instance 363 | this.countable = Countable; 364 | 365 | this.$nextTick(function () { 366 | this$1.countable.count(this$1.text, function (counter) { 367 | this$1.$emit('change', counter); 368 | }, this$1.options); 369 | }); 370 | } 371 | }, 372 | mounted: function mounted () { 373 | // The init function is important because we want to provide counts not 374 | // only during changes, but also on initialization. 375 | this.init(); 376 | } 377 | } 378 | 379 | function normalizeComponent(template, style, script, scopeId, isFunctionalTemplate, moduleIdentifier 380 | /* server only */ 381 | , shadowMode, createInjector, createInjectorSSR, createInjectorShadow) { 382 | if (typeof shadowMode !== 'boolean') { 383 | createInjectorSSR = createInjector; 384 | createInjector = shadowMode; 385 | shadowMode = false; 386 | } // Vue.extend constructor export interop. 387 | 388 | 389 | var options = typeof script === 'function' ? script.options : script; // render functions 390 | 391 | if (template && template.render) { 392 | options.render = template.render; 393 | options.staticRenderFns = template.staticRenderFns; 394 | options._compiled = true; // functional template 395 | 396 | if (isFunctionalTemplate) { 397 | options.functional = true; 398 | } 399 | } // scopedId 400 | 401 | 402 | if (scopeId) { 403 | options._scopeId = scopeId; 404 | } 405 | 406 | var hook; 407 | 408 | if (moduleIdentifier) { 409 | // server build 410 | hook = function hook(context) { 411 | // 2.3 injection 412 | context = context || // cached call 413 | this.$vnode && this.$vnode.ssrContext || // stateful 414 | this.parent && this.parent.$vnode && this.parent.$vnode.ssrContext; // functional 415 | // 2.2 with runInNewContext: true 416 | 417 | if (!context && typeof __VUE_SSR_CONTEXT__ !== 'undefined') { 418 | context = __VUE_SSR_CONTEXT__; 419 | } // inject component styles 420 | 421 | 422 | if (style) { 423 | style.call(this, createInjectorSSR(context)); 424 | } // register component module identifier for async chunk inference 425 | 426 | 427 | if (context && context._registeredComponents) { 428 | context._registeredComponents.add(moduleIdentifier); 429 | } 430 | }; // used by ssr in case component is cached and beforeCreate 431 | // never gets called 432 | 433 | 434 | options._ssrRegister = hook; 435 | } else if (style) { 436 | hook = shadowMode ? function () { 437 | style.call(this, createInjectorShadow(this.$root.$options.shadowRoot)); 438 | } : function (context) { 439 | style.call(this, createInjector(context)); 440 | }; 441 | } 442 | 443 | if (hook) { 444 | if (options.functional) { 445 | // register for functional component in vue file 446 | var originalRender = options.render; 447 | 448 | options.render = function renderWithStyleInjection(h, context) { 449 | hook.call(context); 450 | return originalRender(h, context); 451 | }; 452 | } else { 453 | // inject component registration as beforeCreate hook 454 | var existing = options.beforeCreate; 455 | options.beforeCreate = existing ? [].concat(existing, hook) : [hook]; 456 | } 457 | } 458 | 459 | return script; 460 | } 461 | 462 | var normalizeComponent_1 = normalizeComponent; 463 | 464 | var isOldIE = typeof navigator !== 'undefined' && /msie [6-9]\\b/.test(navigator.userAgent.toLowerCase()); 465 | function createInjector(context) { 466 | return function (id, style) { 467 | return addStyle(id, style); 468 | }; 469 | } 470 | var HEAD = document.head || document.getElementsByTagName('head')[0]; 471 | var styles = {}; 472 | 473 | function addStyle(id, css) { 474 | var group = isOldIE ? css.media || 'default' : id; 475 | var style = styles[group] || (styles[group] = { 476 | ids: new Set(), 477 | styles: [] 478 | }); 479 | 480 | if (!style.ids.has(id)) { 481 | style.ids.add(id); 482 | var code = css.source; 483 | 484 | if (css.map) { 485 | // https://developer.chrome.com/devtools/docs/javascript-debugging 486 | // this makes source maps inside style tags work properly in Chrome 487 | code += '\n/*# sourceURL=' + css.map.sources[0] + ' */'; // http://stackoverflow.com/a/26603875 488 | 489 | code += '\n/*# sourceMappingURL=data:application/json;base64,' + btoa(unescape(encodeURIComponent(JSON.stringify(css.map)))) + ' */'; 490 | } 491 | 492 | if (!style.element) { 493 | style.element = document.createElement('style'); 494 | style.element.type = 'text/css'; 495 | if (css.media) { style.element.setAttribute('media', css.media); } 496 | HEAD.appendChild(style.element); 497 | } 498 | 499 | if ('styleSheet' in style.element) { 500 | style.styles.push(code); 501 | style.element.styleSheet.cssText = style.styles.filter(Boolean).join('\n'); 502 | } else { 503 | var index = style.ids.size - 1; 504 | var textNode = document.createTextNode(code); 505 | var nodes = style.element.childNodes; 506 | if (nodes[index]) { style.element.removeChild(nodes[index]); } 507 | if (nodes.length) { style.element.insertBefore(textNode, nodes[index]); }else { style.element.appendChild(textNode); } 508 | } 509 | } 510 | } 511 | 512 | var browser = createInjector; 513 | 514 | /* script */ 515 | var __vue_script__ = script; 516 | 517 | /* template */ 518 | var __vue_render__ = function() { 519 | var _vm = this; 520 | var _h = _vm.$createElement; 521 | var _c = _vm._self._c || _h; 522 | return _c("div") 523 | }; 524 | var __vue_staticRenderFns__ = []; 525 | __vue_render__._withStripped = true; 526 | 527 | /* style */ 528 | var __vue_inject_styles__ = function (inject) { 529 | if (!inject) { return } 530 | inject("data-v-12d8198d_0", { source: "\n/*# sourceMappingURL=vue-countable.vue.map */", map: {"version":3,"sources":["vue-countable.vue"],"names":[],"mappings":";AACA,4CAA4C","file":"vue-countable.vue"}, media: undefined }); 531 | 532 | }; 533 | /* scoped */ 534 | var __vue_scope_id__ = "data-v-12d8198d"; 535 | /* module identifier */ 536 | var __vue_module_identifier__ = undefined; 537 | /* functional template */ 538 | var __vue_is_functional_template__ = false; 539 | /* style inject SSR */ 540 | 541 | 542 | 543 | var component = normalizeComponent_1( 544 | { render: __vue_render__, staticRenderFns: __vue_staticRenderFns__ }, 545 | __vue_inject_styles__, 546 | __vue_script__, 547 | __vue_scope_id__, 548 | __vue_is_functional_template__, 549 | __vue_module_identifier__, 550 | browser, 551 | undefined 552 | ) 553 | 554 | // Import vue component 555 | 556 | // install function executed by Vue.use() 557 | function install(Vue) { 558 | if (install.installed) { return; } 559 | install.installed = true; 560 | Vue.component('VueCountable', component); 561 | } 562 | 563 | // Create module definition for Vue.use() 564 | var plugin = { 565 | install: install, 566 | }; 567 | 568 | // To auto-install when vue is found 569 | var GlobalVue = null; 570 | if (typeof window !== 'undefined') { 571 | GlobalVue = window.Vue; 572 | } else if (typeof global !== 'undefined') { 573 | GlobalVue = global.Vue; 574 | } 575 | if (GlobalVue) { 576 | GlobalVue.use(plugin); 577 | } 578 | 579 | // It's possible to expose named exports when writing components that can 580 | // also be used as directives, etc. - eg. import { RollupDemoDirective } from 'rollup-demo'; 581 | // export const RollupDemoDirective = component; 582 | 583 | exports.install = install; 584 | exports.default = component; 585 | 586 | return exports; 587 | 588 | }({})); 589 | -------------------------------------------------------------------------------- /dist/vue-countable.umd.js: -------------------------------------------------------------------------------- 1 | (function (global, factory) { 2 | typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports, require('countable')) : 3 | typeof define === 'function' && define.amd ? define(['exports', 'countable'], factory) : 4 | (factory((global.VueCountable = {}),global.countable)); 5 | }(this, (function (exports,countable) { 'use strict'; 6 | 7 | countable = countable && countable.hasOwnProperty('default') ? countable['default'] : countable; 8 | 9 | // 10 | 11 | var script = { 12 | name: 'VueCountable', 13 | props: { 14 | text: { 15 | type: String, 16 | required: true 17 | }, 18 | elementId: { 19 | type: String, 20 | required: true 21 | }, 22 | hardReturns: { 23 | type: Boolean, 24 | required: false, 25 | default: false 26 | }, 27 | stripTags: { 28 | type: Boolean, 29 | required: false, 30 | default: false 31 | }, 32 | ignore: { 33 | type: Array, 34 | required: false, 35 | default: function () { return []; } 36 | } 37 | }, 38 | data: function data () { 39 | return { 40 | countable: null 41 | } 42 | }, 43 | computed: { 44 | options: function options () { 45 | return { 46 | hardReturns: this.hardReturns, 47 | stripTags: this.stripTags, 48 | ignore: this.ignore 49 | } 50 | } 51 | }, 52 | watch: { 53 | text: { 54 | handler: function (value) { 55 | // Everytime this.text changes we'll update our live count 56 | this.init(); 57 | } 58 | } 59 | }, 60 | methods: { 61 | init: function init () { 62 | var this$1 = this; 63 | 64 | // Set our countable instance 65 | this.countable = countable; 66 | 67 | this.$nextTick(function () { 68 | this$1.countable.count(this$1.text, function (counter) { 69 | this$1.$emit('change', counter); 70 | }, this$1.options); 71 | }); 72 | } 73 | }, 74 | mounted: function mounted () { 75 | // The init function is important because we want to provide counts not 76 | // only during changes, but also on initialization. 77 | this.init(); 78 | } 79 | } 80 | 81 | function normalizeComponent(template, style, script, scopeId, isFunctionalTemplate, moduleIdentifier 82 | /* server only */ 83 | , shadowMode, createInjector, createInjectorSSR, createInjectorShadow) { 84 | if (typeof shadowMode !== 'boolean') { 85 | createInjectorSSR = createInjector; 86 | createInjector = shadowMode; 87 | shadowMode = false; 88 | } // Vue.extend constructor export interop. 89 | 90 | 91 | var options = typeof script === 'function' ? script.options : script; // render functions 92 | 93 | if (template && template.render) { 94 | options.render = template.render; 95 | options.staticRenderFns = template.staticRenderFns; 96 | options._compiled = true; // functional template 97 | 98 | if (isFunctionalTemplate) { 99 | options.functional = true; 100 | } 101 | } // scopedId 102 | 103 | 104 | if (scopeId) { 105 | options._scopeId = scopeId; 106 | } 107 | 108 | var hook; 109 | 110 | if (moduleIdentifier) { 111 | // server build 112 | hook = function hook(context) { 113 | // 2.3 injection 114 | context = context || // cached call 115 | this.$vnode && this.$vnode.ssrContext || // stateful 116 | this.parent && this.parent.$vnode && this.parent.$vnode.ssrContext; // functional 117 | // 2.2 with runInNewContext: true 118 | 119 | if (!context && typeof __VUE_SSR_CONTEXT__ !== 'undefined') { 120 | context = __VUE_SSR_CONTEXT__; 121 | } // inject component styles 122 | 123 | 124 | if (style) { 125 | style.call(this, createInjectorSSR(context)); 126 | } // register component module identifier for async chunk inference 127 | 128 | 129 | if (context && context._registeredComponents) { 130 | context._registeredComponents.add(moduleIdentifier); 131 | } 132 | }; // used by ssr in case component is cached and beforeCreate 133 | // never gets called 134 | 135 | 136 | options._ssrRegister = hook; 137 | } else if (style) { 138 | hook = shadowMode ? function () { 139 | style.call(this, createInjectorShadow(this.$root.$options.shadowRoot)); 140 | } : function (context) { 141 | style.call(this, createInjector(context)); 142 | }; 143 | } 144 | 145 | if (hook) { 146 | if (options.functional) { 147 | // register for functional component in vue file 148 | var originalRender = options.render; 149 | 150 | options.render = function renderWithStyleInjection(h, context) { 151 | hook.call(context); 152 | return originalRender(h, context); 153 | }; 154 | } else { 155 | // inject component registration as beforeCreate hook 156 | var existing = options.beforeCreate; 157 | options.beforeCreate = existing ? [].concat(existing, hook) : [hook]; 158 | } 159 | } 160 | 161 | return script; 162 | } 163 | 164 | var normalizeComponent_1 = normalizeComponent; 165 | 166 | var isOldIE = typeof navigator !== 'undefined' && /msie [6-9]\\b/.test(navigator.userAgent.toLowerCase()); 167 | function createInjector(context) { 168 | return function (id, style) { 169 | return addStyle(id, style); 170 | }; 171 | } 172 | var HEAD = document.head || document.getElementsByTagName('head')[0]; 173 | var styles = {}; 174 | 175 | function addStyle(id, css) { 176 | var group = isOldIE ? css.media || 'default' : id; 177 | var style = styles[group] || (styles[group] = { 178 | ids: new Set(), 179 | styles: [] 180 | }); 181 | 182 | if (!style.ids.has(id)) { 183 | style.ids.add(id); 184 | var code = css.source; 185 | 186 | if (css.map) { 187 | // https://developer.chrome.com/devtools/docs/javascript-debugging 188 | // this makes source maps inside style tags work properly in Chrome 189 | code += '\n/*# sourceURL=' + css.map.sources[0] + ' */'; // http://stackoverflow.com/a/26603875 190 | 191 | code += '\n/*# sourceMappingURL=data:application/json;base64,' + btoa(unescape(encodeURIComponent(JSON.stringify(css.map)))) + ' */'; 192 | } 193 | 194 | if (!style.element) { 195 | style.element = document.createElement('style'); 196 | style.element.type = 'text/css'; 197 | if (css.media) { style.element.setAttribute('media', css.media); } 198 | HEAD.appendChild(style.element); 199 | } 200 | 201 | if ('styleSheet' in style.element) { 202 | style.styles.push(code); 203 | style.element.styleSheet.cssText = style.styles.filter(Boolean).join('\n'); 204 | } else { 205 | var index = style.ids.size - 1; 206 | var textNode = document.createTextNode(code); 207 | var nodes = style.element.childNodes; 208 | if (nodes[index]) { style.element.removeChild(nodes[index]); } 209 | if (nodes.length) { style.element.insertBefore(textNode, nodes[index]); }else { style.element.appendChild(textNode); } 210 | } 211 | } 212 | } 213 | 214 | var browser = createInjector; 215 | 216 | /* script */ 217 | var __vue_script__ = script; 218 | 219 | /* template */ 220 | var __vue_render__ = function() { 221 | var _vm = this; 222 | var _h = _vm.$createElement; 223 | var _c = _vm._self._c || _h; 224 | return _c("div") 225 | }; 226 | var __vue_staticRenderFns__ = []; 227 | __vue_render__._withStripped = true; 228 | 229 | /* style */ 230 | var __vue_inject_styles__ = function (inject) { 231 | if (!inject) { return } 232 | inject("data-v-12d8198d_0", { source: "\n/*# sourceMappingURL=vue-countable.vue.map */", map: {"version":3,"sources":["vue-countable.vue"],"names":[],"mappings":";AACA,4CAA4C","file":"vue-countable.vue"}, media: undefined }); 233 | 234 | }; 235 | /* scoped */ 236 | var __vue_scope_id__ = "data-v-12d8198d"; 237 | /* module identifier */ 238 | var __vue_module_identifier__ = undefined; 239 | /* functional template */ 240 | var __vue_is_functional_template__ = false; 241 | /* style inject SSR */ 242 | 243 | 244 | 245 | var component = normalizeComponent_1( 246 | { render: __vue_render__, staticRenderFns: __vue_staticRenderFns__ }, 247 | __vue_inject_styles__, 248 | __vue_script__, 249 | __vue_scope_id__, 250 | __vue_is_functional_template__, 251 | __vue_module_identifier__, 252 | browser, 253 | undefined 254 | ) 255 | 256 | // Import vue component 257 | 258 | // install function executed by Vue.use() 259 | function install(Vue) { 260 | if (install.installed) { return; } 261 | install.installed = true; 262 | Vue.component('VueCountable', component); 263 | } 264 | 265 | // Create module definition for Vue.use() 266 | var plugin = { 267 | install: install, 268 | }; 269 | 270 | // To auto-install when vue is found 271 | var GlobalVue = null; 272 | if (typeof window !== 'undefined') { 273 | GlobalVue = window.Vue; 274 | } else if (typeof global !== 'undefined') { 275 | GlobalVue = global.Vue; 276 | } 277 | if (GlobalVue) { 278 | GlobalVue.use(plugin); 279 | } 280 | 281 | // It's possible to expose named exports when writing components that can 282 | // also be used as directives, etc. - eg. import { RollupDemoDirective } from 'rollup-demo'; 283 | // export const RollupDemoDirective = component; 284 | 285 | exports.install = install; 286 | exports.default = component; 287 | 288 | Object.defineProperty(exports, '__esModule', { value: true }); 289 | 290 | }))); 291 | -------------------------------------------------------------------------------- /docs/build.js: -------------------------------------------------------------------------------- 1 | !function(e){var t={};function n(r){if(t[r])return t[r].exports;var i=t[r]={i:r,l:!1,exports:{}};return e[r].call(i.exports,i,i.exports,n),i.l=!0,i.exports}n.m=e,n.c=t,n.d=function(e,t,r){n.o(e,t)||Object.defineProperty(e,t,{configurable:!1,enumerable:!0,get:r})},n.n=function(e){var t=e&&e.__esModule?function(){return e.default}:function(){return e};return n.d(t,"a",t),t},n.o=function(e,t){return Object.prototype.hasOwnProperty.call(e,t)},n.p="/vue-countable/",n(n.s=6)}([function(e,t){var n;n=function(){return this}();try{n=n||Function("return this")()||(0,eval)("this")}catch(e){"object"==typeof window&&(n=window)}e.exports=n},function(e,t){e.exports=function(e){var t=[];return t.toString=function(){return this.map(function(t){var n=function(e,t){var n=e[1]||"",r=e[3];if(!r)return n;if(t&&"function"==typeof btoa){var i=(a=r,"/*# sourceMappingURL=data:application/json;charset=utf-8;base64,"+btoa(unescape(encodeURIComponent(JSON.stringify(a))))+" */"),o=r.sources.map(function(e){return"/*# sourceURL="+r.sourceRoot+e+" */"});return[n].concat(o).concat([i]).join("\n")}var a;return[n].join("\n")}(t,e);return t[2]?"@media "+t[2]+"{"+n+"}":n}).join("")},t.i=function(e,n){"string"==typeof e&&(e=[[null,e,""]]);for(var r={},i=0;in.parts.length&&(r.parts.length=n.parts.length)}else{var a=[];for(i=0;i=0&&Math.floor(t)===t&&isFinite(e)}function p(e){return o(e)&&"function"==typeof e.then&&"function"==typeof e.catch}function v(e){return null==e?"":Array.isArray(e)||l(e)&&e.toString===u?JSON.stringify(e,null,2):String(e)}function h(e){var t=parseFloat(e);return isNaN(t)?e:t}function m(e,t){for(var n=Object.create(null),r=e.split(","),i=0;i-1)return e.splice(n,1)}}var b=Object.prototype.hasOwnProperty;function C(e,t){return b.call(e,t)}function w(e){var t=Object.create(null);return function(n){return t[n]||(t[n]=e(n))}}var x=/-(\w)/g,$=w(function(e){return e.replace(x,function(e,t){return t?t.toUpperCase():""})}),A=w(function(e){return e.charAt(0).toUpperCase()+e.slice(1)}),k=/\B([A-Z])/g,T=w(function(e){return e.replace(k,"-$1").toLowerCase()});var O=Function.prototype.bind?function(e,t){return e.bind(t)}:function(e,t){function n(n){var r=arguments.length;return r?r>1?e.apply(t,arguments):e.call(t,n):e.call(t)}return n._length=e.length,n};function S(e,t){t=t||0;for(var n=e.length-t,r=new Array(n);n--;)r[n]=e[n+t];return r}function E(e,t){for(var n in t)e[n]=t[n];return e}function j(e){for(var t={},n=0;n0,ee=X&&X.indexOf("edge/")>0,te=(X&&X.indexOf("android"),X&&/iphone|ipad|ipod|ios/.test(X)||"ios"===Z),ne=(X&&/chrome\/\d+/.test(X),X&&/phantomjs/.test(X),X&&X.match(/firefox\/(\d+)/)),re={}.watch,ie=!1;if(W)try{var oe={};Object.defineProperty(oe,"passive",{get:function(){ie=!0}}),window.addEventListener("test-passive",null,oe)}catch(e){}var ae=function(){return void 0===K&&(K=!W&&!G&&void 0!==e&&(e.process&&"server"===e.process.env.VUE_ENV)),K},se=W&&window.__VUE_DEVTOOLS_GLOBAL_HOOK__;function ce(e){return"function"==typeof e&&/native code/.test(e.toString())}var ue,le="undefined"!=typeof Symbol&&ce(Symbol)&&"undefined"!=typeof Reflect&&ce(Reflect.ownKeys);ue="undefined"!=typeof Set&&ce(Set)?Set:function(){function e(){this.set=Object.create(null)}return e.prototype.has=function(e){return!0===this.set[e]},e.prototype.add=function(e){this.set[e]=!0},e.prototype.clear=function(){this.set=Object.create(null)},e}();var fe=I,de=0,pe=function(){this.id=de++,this.subs=[]};pe.prototype.addSub=function(e){this.subs.push(e)},pe.prototype.removeSub=function(e){_(this.subs,e)},pe.prototype.depend=function(){pe.target&&pe.target.addDep(this)},pe.prototype.notify=function(){var e=this.subs.slice();for(var t=0,n=e.length;t-1)if(o&&!C(i,"default"))a=!1;else if(""===a||a===T(e)){var c=Ve(String,i.type);(c<0||s0&&(dt((u=e(u,(n||"")+"_"+c))[0])&&dt(f)&&(r[l]=be(f.text+u[0].text),u.shift()),r.push.apply(r,u)):s(u)?dt(f)?r[l]=be(f.text+u):""!==u&&r.push(be(u)):dt(u)&&dt(f)?r[l]=be(f.text+u.text):(a(t._isVList)&&o(u.tag)&&i(u.key)&&o(n)&&(u.key="__vlist"+n+"_"+c+"__"),r.push(u)));return r}(e):void 0}function dt(e){return o(e)&&o(e.text)&&!1===e.isComment}function pt(e,t){if(e){for(var n=Object.create(null),r=le?Reflect.ownKeys(e):Object.keys(e),i=0;idocument.createEvent("Event").timeStamp&&(dn=function(){return performance.now()});var vn=0,hn=function(e,t,n,r,i){this.vm=e,i&&(e._watcher=this),e._watchers.push(this),r?(this.deep=!!r.deep,this.user=!!r.user,this.lazy=!!r.lazy,this.sync=!!r.sync,this.before=r.before):this.deep=this.user=this.lazy=this.sync=!1,this.cb=n,this.id=++vn,this.active=!0,this.dirty=this.lazy,this.deps=[],this.newDeps=[],this.depIds=new ue,this.newDepIds=new ue,this.expression="","function"==typeof t?this.getter=t:(this.getter=function(e){if(!q.test(e)){var t=e.split(".");return function(e){for(var n=0;nln&&on[n].id>e.id;)n--;on.splice(n+1,0,e)}else on.push(e);cn||(cn=!0,rt(pn))}}(this)},hn.prototype.run=function(){if(this.active){var e=this.get();if(e!==this.value||c(e)||this.deep){var t=this.value;if(this.value=e,this.user)try{this.cb.call(this.vm,e,t)}catch(e){ze(e,this.vm,'callback for watcher "'+this.expression+'"')}else this.cb.call(this.vm,e,t)}}},hn.prototype.evaluate=function(){this.value=this.get(),this.dirty=!1},hn.prototype.depend=function(){for(var e=this.deps.length;e--;)this.deps[e].depend()},hn.prototype.teardown=function(){if(this.active){this.vm._isBeingDestroyed||_(this.vm._watchers,this);for(var e=this.deps.length;e--;)this.deps[e].removeSub(this);this.active=!1}};var mn={enumerable:!0,configurable:!0,get:I,set:I};function gn(e,t,n){mn.get=function(){return this[t][n]},mn.set=function(e){this[t][n]=e},Object.defineProperty(e,n,mn)}function yn(e){e._watchers=[];var t=e.$options;t.props&&function(e,t){var n=e.$options.propsData||{},r=e._props={},i=e.$options._propKeys=[],o=!e.$parent;o||ke(!1);var a=function(o){i.push(o);var a=He(o,t,n,e);Se(r,o,a),o in e||gn(e,"_props",o)};for(var s in t)a(s);ke(!0)}(e,t.props),t.methods&&function(e,t){e.$options.props;for(var n in t)e[n]="function"!=typeof t[n]?I:O(t[n],e)}(e,t.methods),t.data?function(e){var t=e.$options.data;l(t=e._data="function"==typeof t?function(e,t){he();try{return e.call(t,t)}catch(e){return ze(e,t,"data()"),{}}finally{me()}}(t,e):t||{})||(t={});var n=Object.keys(t),r=e.$options.props,i=(e.$options.methods,n.length);for(;i--;){var o=n[i];0,r&&C(r,o)||V(o)||gn(e,"_data",o)}Oe(t,!0)}(e):Oe(e._data={},!0),t.computed&&function(e,t){var n=e._computedWatchers=Object.create(null),r=ae();for(var i in t){var o=t[i],a="function"==typeof o?o:o.get;0,r||(n[i]=new hn(e,a||I,I,_n)),i in e||bn(e,i,o)}}(e,t.computed),t.watch&&t.watch!==re&&function(e,t){for(var n in t){var r=t[n];if(Array.isArray(r))for(var i=0;i-1:"string"==typeof e?e.split(",").indexOf(t)>-1:!!f(e)&&e.test(t)}function En(e,t){var n=e.cache,r=e.keys,i=e._vnode;for(var o in n){var a=n[o];if(a){var s=On(a.componentOptions);s&&!t(s)&&jn(n,o,r,i)}}}function jn(e,t,n,r){var i=e[t];!i||r&&i.tag===r.tag||i.componentInstance.$destroy(),e[t]=null,_(n,t)}!function(e){e.prototype._init=function(e){var t=this;t._uid=$n++,t._isVue=!0,e&&e._isComponent?function(e,t){var n=e.$options=Object.create(e.constructor.options),r=t._parentVnode;n.parent=t.parent,n._parentVnode=r;var i=r.componentOptions;n.propsData=i.propsData,n._parentListeners=i.listeners,n._renderChildren=i.children,n._componentTag=i.tag,t.render&&(n.render=t.render,n.staticRenderFns=t.staticRenderFns)}(t,e):t.$options=Re(An(t.constructor),e||{},t),t._renderProxy=t,t._self=t,function(e){var t=e.$options,n=t.parent;if(n&&!t.abstract){for(;n.$options.abstract&&n.$parent;)n=n.$parent;n.$children.push(e)}e.$parent=n,e.$root=n?n.$root:e,e.$children=[],e.$refs={},e._watcher=null,e._inactive=null,e._directInactive=!1,e._isMounted=!1,e._isDestroyed=!1,e._isBeingDestroyed=!1}(t),function(e){e._events=Object.create(null),e._hasHookEvent=!1;var t=e.$options._parentListeners;t&&Yt(e,t)}(t),function(e){e._vnode=null,e._staticTrees=null;var t=e.$options,n=e.$vnode=t._parentVnode,i=n&&n.context;e.$slots=vt(t._renderChildren,i),e.$scopedSlots=r,e._c=function(t,n,r,i){return Vt(e,t,n,r,i,!1)},e.$createElement=function(t,n,r,i){return Vt(e,t,n,r,i,!0)};var o=n&&n.data;Se(e,"$attrs",o&&o.attrs||r,null,!0),Se(e,"$listeners",t._parentListeners||r,null,!0)}(t),rn(t,"beforeCreate"),function(e){var t=pt(e.$options.inject,e);t&&(ke(!1),Object.keys(t).forEach(function(n){Se(e,n,t[n])}),ke(!0))}(t),yn(t),function(e){var t=e.$options.provide;t&&(e._provided="function"==typeof t?t.call(e):t)}(t),rn(t,"created"),t.$options.el&&t.$mount(t.$options.el)}}(kn),function(e){var t={get:function(){return this._data}},n={get:function(){return this._props}};Object.defineProperty(e.prototype,"$data",t),Object.defineProperty(e.prototype,"$props",n),e.prototype.$set=Ee,e.prototype.$delete=je,e.prototype.$watch=function(e,t,n){if(l(t))return xn(this,e,t,n);(n=n||{}).user=!0;var r=new hn(this,e,t,n);if(n.immediate)try{t.call(this,r.value)}catch(e){ze(e,this,'callback for immediate watcher "'+r.expression+'"')}return function(){r.teardown()}}}(kn),function(e){var t=/^hook:/;e.prototype.$on=function(e,n){var r=this;if(Array.isArray(e))for(var i=0,o=e.length;i1?S(n):n;for(var r=S(arguments,1),i='event handler for "'+e+'"',o=0,a=n.length;oparseInt(this.max)&&jn(a,s[0],s,this._vnode)),t.data.keepAlive=!0}return t||e&&e[0]}}};!function(e){var t={get:function(){return U}};Object.defineProperty(e,"config",t),e.util={warn:fe,extend:E,mergeOptions:Re,defineReactive:Se},e.set=Ee,e.delete=je,e.nextTick=rt,e.observable=function(e){return Oe(e),e},e.options=Object.create(null),F.forEach(function(t){e.options[t+"s"]=Object.create(null)}),e.options._base=e,E(e.options.components,Ln),function(e){e.use=function(e){var t=this._installedPlugins||(this._installedPlugins=[]);if(t.indexOf(e)>-1)return this;var n=S(arguments,1);return n.unshift(this),"function"==typeof e.install?e.install.apply(e,n):"function"==typeof e&&e.apply(null,n),t.push(e),this}}(e),function(e){e.mixin=function(e){return this.options=Re(this.options,e),this}}(e),Tn(e),function(e){F.forEach(function(t){e[t]=function(e,n){return n?("component"===t&&l(n)&&(n.name=n.name||e,n=this.options._base.extend(n)),"directive"===t&&"function"==typeof n&&(n={bind:n,update:n}),this.options[t+"s"][e]=n,n):this.options[t+"s"][e]}})}(e)}(kn),Object.defineProperty(kn.prototype,"$isServer",{get:ae}),Object.defineProperty(kn.prototype,"$ssrContext",{get:function(){return this.$vnode&&this.$vnode.ssrContext}}),Object.defineProperty(kn,"FunctionalRenderContext",{value:Nt}),kn.version="2.6.6";var Nn=m("style,class"),Mn=m("input,textarea,option,select,progress"),Dn=function(e,t,n){return"value"===n&&Mn(e)&&"button"!==t||"selected"===n&&"option"===e||"checked"===n&&"input"===e||"muted"===n&&"video"===e},Pn=m("contenteditable,draggable,spellcheck"),Rn=m("events,caret,typing,plaintext-only"),Fn=function(e,t){return zn(t)||"false"===t?"false":"contenteditable"===e&&Rn(t)?t:"true"},Hn=m("allowfullscreen,async,autofocus,autoplay,checked,compact,controls,declare,default,defaultchecked,defaultmuted,defaultselected,defer,disabled,enabled,formnovalidate,hidden,indeterminate,inert,ismap,itemscope,loop,multiple,muted,nohref,noresize,noshade,novalidate,nowrap,open,pauseonexit,readonly,required,reversed,scoped,seamless,selected,sortable,translate,truespeed,typemustmatch,visible"),Un="http://www.w3.org/1999/xlink",Bn=function(e){return":"===e.charAt(5)&&"xlink"===e.slice(0,5)},Vn=function(e){return Bn(e)?e.slice(6,e.length):""},zn=function(e){return null==e||!1===e};function qn(e){for(var t=e.data,n=e,r=e;o(r.componentInstance);)(r=r.componentInstance._vnode)&&r.data&&(t=Kn(r.data,t));for(;o(n=n.parent);)n&&n.data&&(t=Kn(t,n.data));return function(e,t){if(o(e)||o(t))return Jn(e,Wn(t));return""}(t.staticClass,t.class)}function Kn(e,t){return{staticClass:Jn(e.staticClass,t.staticClass),class:o(e.class)?[e.class,t.class]:t.class}}function Jn(e,t){return e?t?e+" "+t:e:t||""}function Wn(e){return Array.isArray(e)?function(e){for(var t,n="",r=0,i=e.length;r-1?_r(e,t,n):Hn(t)?zn(n)?e.removeAttribute(t):(n="allowfullscreen"===t&&"EMBED"===e.tagName?"true":t,e.setAttribute(t,n)):Pn(t)?e.setAttribute(t,Fn(t,n)):Bn(t)?zn(n)?e.removeAttributeNS(Un,Vn(t)):e.setAttributeNS(Un,t,n):_r(e,t,n)}function _r(e,t,n){if(zn(n))e.removeAttribute(t);else{if(Y&&!Q&&"TEXTAREA"===e.tagName&&"placeholder"===t&&""!==n&&!e.__ieph){var r=function(t){t.stopImmediatePropagation(),e.removeEventListener("input",r)};e.addEventListener("input",r),e.__ieph=!0}e.setAttribute(t,n)}}var br={create:gr,update:gr};function Cr(e,t){var n=t.elm,r=t.data,a=e.data;if(!(i(r.staticClass)&&i(r.class)&&(i(a)||i(a.staticClass)&&i(a.class)))){var s=qn(t),c=n._transitionClasses;o(c)&&(s=Jn(s,Wn(c))),s!==n._prevClass&&(n.setAttribute("class",s),n._prevClass=s)}}var wr,xr,$r,Ar,kr,Tr,Or={create:Cr,update:Cr},Sr=/[\w).+\-_$\]]/;function Er(e){var t,n,r,i,o,a=!1,s=!1,c=!1,u=!1,l=0,f=0,d=0,p=0;for(r=0;r=0&&" "===(h=e.charAt(v));v--);h&&Sr.test(h)||(u=!0)}}else void 0===i?(p=r+1,i=e.slice(0,r).trim()):m();function m(){(o||(o=[])).push(e.slice(p,r).trim()),p=r+1}if(void 0===i?i=e.slice(0,r).trim():0!==p&&m(),o)for(r=0;r-1?{exp:e.slice(0,Ar),key:'"'+e.slice(Ar+1)+'"'}:{exp:e,key:null};xr=e,Ar=kr=Tr=0;for(;!Wr();)Gr($r=Jr())?Xr($r):91===$r&&Zr($r);return{exp:e.slice(0,kr),key:e.slice(kr+1,Tr)}}(e);return null===n.key?e+"="+t:"$set("+n.exp+", "+n.key+", "+t+")"}function Jr(){return xr.charCodeAt(++Ar)}function Wr(){return Ar>=wr}function Gr(e){return 34===e||39===e}function Zr(e){var t=1;for(kr=Ar;!Wr();)if(Gr(e=Jr()))Xr(e);else if(91===e&&t++,93===e&&t--,0===t){Tr=Ar;break}}function Xr(e){for(var t=e;!Wr()&&(e=Jr())!==t;);}var Yr,Qr="__r",ei="__c";function ti(e,t,n){var r=Yr;return function i(){null!==t.apply(null,arguments)&&ii(e,i,n,r)}}var ni=Ge&&!(ne&&Number(ne[1])<=53);function ri(e,t,n,r){if(ni){var i=fn,o=t;t=o._wrapper=function(e){if(e.target===e.currentTarget||e.timeStamp>=i||0===e.timeStamp||e.target.ownerDocument!==document)return o.apply(this,arguments)}}Yr.addEventListener(e,t,ie?{capture:n,passive:r}:n)}function ii(e,t,n,r){(r||Yr).removeEventListener(e,t._wrapper||t,n)}function oi(e,t){if(!i(e.data.on)||!i(t.data.on)){var n=t.data.on||{},r=e.data.on||{};Yr=t.elm,function(e){if(o(e[Qr])){var t=Y?"change":"input";e[t]=[].concat(e[Qr],e[t]||[]),delete e[Qr]}o(e[ei])&&(e.change=[].concat(e[ei],e.change||[]),delete e[ei])}(n),ct(n,r,ri,ii,ti,t.context),Yr=void 0}}var ai,si={create:oi,update:oi};function ci(e,t){if(!i(e.data.domProps)||!i(t.data.domProps)){var n,r,a=t.elm,s=e.data.domProps||{},c=t.data.domProps||{};for(n in o(c.__ob__)&&(c=t.data.domProps=E({},c)),s)i(c[n])&&(a[n]="");for(n in c){if(r=c[n],"textContent"===n||"innerHTML"===n){if(t.children&&(t.children.length=0),r===s[n])continue;1===a.childNodes.length&&a.removeChild(a.childNodes[0])}if("value"===n||r!==s[n])if("value"===n){a._value=r;var u=i(r)?"":String(r);ui(a,u)&&(a.value=u)}else if("innerHTML"===n&&Xn(a.tagName)&&i(a.innerHTML)){(ai=ai||document.createElement("div")).innerHTML=""+r+"";for(var l=ai.firstChild;a.firstChild;)a.removeChild(a.firstChild);for(;l.firstChild;)a.appendChild(l.firstChild)}else a[n]=r}}}function ui(e,t){return!e.composing&&("OPTION"===e.tagName||function(e,t){var n=!0;try{n=document.activeElement!==e}catch(e){}return n&&e.value!==t}(e,t)||function(e,t){var n=e.value,r=e._vModifiers;if(o(r)){if(r.number)return h(n)!==h(t);if(r.trim)return n.trim()!==t.trim()}return n!==t}(e,t))}var li={create:ci,update:ci},fi=w(function(e){var t={},n=/:(.+)/;return e.split(/;(?![^(]*\))/g).forEach(function(e){if(e){var r=e.split(n);r.length>1&&(t[r[0].trim()]=r[1].trim())}}),t});function di(e){var t=pi(e.style);return e.staticStyle?E(e.staticStyle,t):t}function pi(e){return Array.isArray(e)?j(e):"string"==typeof e?fi(e):e}var vi,hi=/^--/,mi=/\s*!important$/,gi=function(e,t,n){if(hi.test(t))e.style.setProperty(t,n);else if(mi.test(n))e.style.setProperty(T(t),n.replace(mi,""),"important");else{var r=_i(t);if(Array.isArray(n))for(var i=0,o=n.length;i-1?t.split(wi).forEach(function(t){return e.classList.add(t)}):e.classList.add(t);else{var n=" "+(e.getAttribute("class")||"")+" ";n.indexOf(" "+t+" ")<0&&e.setAttribute("class",(n+t).trim())}}function $i(e,t){if(t&&(t=t.trim()))if(e.classList)t.indexOf(" ")>-1?t.split(wi).forEach(function(t){return e.classList.remove(t)}):e.classList.remove(t),e.classList.length||e.removeAttribute("class");else{for(var n=" "+(e.getAttribute("class")||"")+" ",r=" "+t+" ";n.indexOf(r)>=0;)n=n.replace(r," ");(n=n.trim())?e.setAttribute("class",n):e.removeAttribute("class")}}function Ai(e){if(e){if("object"==typeof e){var t={};return!1!==e.css&&E(t,ki(e.name||"v")),E(t,e),t}return"string"==typeof e?ki(e):void 0}}var ki=w(function(e){return{enterClass:e+"-enter",enterToClass:e+"-enter-to",enterActiveClass:e+"-enter-active",leaveClass:e+"-leave",leaveToClass:e+"-leave-to",leaveActiveClass:e+"-leave-active"}}),Ti=W&&!Q,Oi="transition",Si="animation",Ei="transition",ji="transitionend",Ii="animation",Li="animationend";Ti&&(void 0===window.ontransitionend&&void 0!==window.onwebkittransitionend&&(Ei="WebkitTransition",ji="webkitTransitionEnd"),void 0===window.onanimationend&&void 0!==window.onwebkitanimationend&&(Ii="WebkitAnimation",Li="webkitAnimationEnd"));var Ni=W?window.requestAnimationFrame?window.requestAnimationFrame.bind(window):setTimeout:function(e){return e()};function Mi(e){Ni(function(){Ni(e)})}function Di(e,t){var n=e._transitionClasses||(e._transitionClasses=[]);n.indexOf(t)<0&&(n.push(t),xi(e,t))}function Pi(e,t){e._transitionClasses&&_(e._transitionClasses,t),$i(e,t)}function Ri(e,t,n){var r=Hi(e,t),i=r.type,o=r.timeout,a=r.propCount;if(!i)return n();var s=i===Oi?ji:Li,c=0,u=function(){e.removeEventListener(s,l),n()},l=function(t){t.target===e&&++c>=a&&u()};setTimeout(function(){c0&&(n=Oi,l=a,f=o.length):t===Si?u>0&&(n=Si,l=u,f=c.length):f=(n=(l=Math.max(a,u))>0?a>u?Oi:Si:null)?n===Oi?o.length:c.length:0,{type:n,timeout:l,propCount:f,hasTransform:n===Oi&&Fi.test(r[Ei+"Property"])}}function Ui(e,t){for(;e.length1}function Ji(e,t){!0!==t.data.show&&Vi(t)}var Wi=function(e){var t,n,r={},c=e.modules,u=e.nodeOps;for(t=0;tv?_(e,i(n[g+1])?null:n[g+1].elm,n,p,g,r):p>g&&C(0,t,d,v)}(d,m,g,n,l):o(g)?(o(e.text)&&u.setTextContent(d,""),_(d,null,g,0,g.length-1,n)):o(m)?C(0,m,0,m.length-1):o(e.text)&&u.setTextContent(d,""):e.text!==t.text&&u.setTextContent(d,t.text),o(v)&&o(p=v.hook)&&o(p=p.postpatch)&&p(e,t)}}}function A(e,t,n){if(a(n)&&o(e.parent))e.parent.data.pendingInsert=t;else for(var r=0;r-1,a.selected!==o&&(a.selected=o);else if(M(Qi(a),r))return void(e.selectedIndex!==s&&(e.selectedIndex=s));i||(e.selectedIndex=-1)}}function Yi(e,t){return t.every(function(t){return!M(t,e)})}function Qi(e){return"_value"in e?e._value:e.value}function eo(e){e.target.composing=!0}function to(e){e.target.composing&&(e.target.composing=!1,no(e.target,"input"))}function no(e,t){var n=document.createEvent("HTMLEvents");n.initEvent(t,!0,!0),e.dispatchEvent(n)}function ro(e){return!e.componentInstance||e.data&&e.data.transition?e:ro(e.componentInstance._vnode)}var io={model:Gi,show:{bind:function(e,t,n){var r=t.value,i=(n=ro(n)).data&&n.data.transition,o=e.__vOriginalDisplay="none"===e.style.display?"":e.style.display;r&&i?(n.data.show=!0,Vi(n,function(){e.style.display=o})):e.style.display=r?o:"none"},update:function(e,t,n){var r=t.value;!r!=!t.oldValue&&((n=ro(n)).data&&n.data.transition?(n.data.show=!0,r?Vi(n,function(){e.style.display=e.__vOriginalDisplay}):zi(n,function(){e.style.display="none"})):e.style.display=r?e.__vOriginalDisplay:"none")},unbind:function(e,t,n,r,i){i||(e.style.display=e.__vOriginalDisplay)}}},oo={name:String,appear:Boolean,css:Boolean,mode:String,type:String,enterClass:String,leaveClass:String,enterToClass:String,leaveToClass:String,enterActiveClass:String,leaveActiveClass:String,appearClass:String,appearActiveClass:String,appearToClass:String,duration:[Number,String,Object]};function ao(e){var t=e&&e.componentOptions;return t&&t.Ctor.options.abstract?ao(Wt(t.children)):e}function so(e){var t={},n=e.$options;for(var r in n.propsData)t[r]=e[r];var i=n._parentListeners;for(var o in i)t[$(o)]=i[o];return t}function co(e,t){if(/\d-keep-alive$/.test(t.tag))return e("keep-alive",{props:t.componentOptions.propsData})}var uo=function(e){return e.tag||Jt(e)},lo=function(e){return"show"===e.name},fo={name:"transition",props:oo,abstract:!0,render:function(e){var t=this,n=this.$slots.default;if(n&&(n=n.filter(uo)).length){0;var r=this.mode;0;var i=n[0];if(function(e){for(;e=e.parent;)if(e.data.transition)return!0}(this.$vnode))return i;var o=ao(i);if(!o)return i;if(this._leaving)return co(e,i);var a="__transition-"+this._uid+"-";o.key=null==o.key?o.isComment?a+"comment":a+o.tag:s(o.key)?0===String(o.key).indexOf(a)?o.key:a+o.key:o.key;var c=(o.data||(o.data={})).transition=so(this),u=this._vnode,l=ao(u);if(o.data.directives&&o.data.directives.some(lo)&&(o.data.show=!0),l&&l.data&&!function(e,t){return t.key===e.key&&t.tag===e.tag}(o,l)&&!Jt(l)&&(!l.componentInstance||!l.componentInstance._vnode.isComment)){var f=l.data.transition=E({},c);if("out-in"===r)return this._leaving=!0,ut(f,"afterLeave",function(){t._leaving=!1,t.$forceUpdate()}),co(e,i);if("in-out"===r){if(Jt(o))return u;var d,p=function(){d()};ut(c,"afterEnter",p),ut(c,"enterCancelled",p),ut(f,"delayLeave",function(e){d=e})}}return i}}},po=E({tag:String,moveClass:String},oo);function vo(e){e.elm._moveCb&&e.elm._moveCb(),e.elm._enterCb&&e.elm._enterCb()}function ho(e){e.data.newPos=e.elm.getBoundingClientRect()}function mo(e){var t=e.data.pos,n=e.data.newPos,r=t.left-n.left,i=t.top-n.top;if(r||i){e.data.moved=!0;var o=e.elm.style;o.transform=o.WebkitTransform="translate("+r+"px,"+i+"px)",o.transitionDuration="0s"}}delete po.mode;var go={Transition:fo,TransitionGroup:{props:po,beforeMount:function(){var e=this,t=this._update;this._update=function(n,r){var i=en(e);e.__patch__(e._vnode,e.kept,!1,!0),e._vnode=e.kept,i(),t.call(e,n,r)}},render:function(e){for(var t=this.tag||this.$vnode.data.tag||"span",n=Object.create(null),r=this.prevChildren=this.children,i=this.$slots.default||[],o=this.children=[],a=so(this),s=0;s-1?er[e]=t.constructor===window.HTMLUnknownElement||t.constructor===window.HTMLElement:er[e]=/HTMLUnknownElement/.test(t.toString())},E(kn.options.directives,io),E(kn.options.components,go),kn.prototype.__patch__=W?Wi:I,kn.prototype.$mount=function(e,t){return function(e,t,n){return e.$el=t,e.$options.render||(e.$options.render=_e),rn(e,"beforeMount"),new hn(e,function(){e._update(e._render(),n)},I,{before:function(){e._isMounted&&!e._isDestroyed&&rn(e,"beforeUpdate")}},!0),n=!1,null==e.$vnode&&(e._isMounted=!0,rn(e,"mounted")),e}(this,e=e&&W?nr(e):void 0,t)},W&&setTimeout(function(){U.devtools&&se&&se.emit("init",kn)},0);var yo=/\{\{((?:.|\r?\n)+?)\}\}/g,_o=/[-.*+?^${}()|[\]\/\\]/g,bo=w(function(e){var t=e[0].replace(_o,"\\$&"),n=e[1].replace(_o,"\\$&");return new RegExp(t+"((?:.|\\n)+?)"+n,"g")});function Co(e,t){var n=t?bo(t):yo;if(n.test(e)){for(var r,i,o,a=[],s=[],c=n.lastIndex=0;r=n.exec(e);){(i=r.index)>c&&(s.push(o=e.slice(c,i)),a.push(JSON.stringify(o)));var u=Er(r[1].trim());a.push("_s("+u+")"),s.push({"@binding":u}),c=i+r[0].length}return c\/=]+)(?:\s*(=)\s*(?:"([^"]*)"+|'([^']*)'+|([^\s"'=<>`]+)))?/,Eo=/^\s*((?:v-[\w-]+:|@|:|#)\[[^=]+\][^\s"'<>\/=]*)(?:\s*(=)\s*(?:"([^"]*)"+|'([^']*)'+|([^\s"'=<>`]+)))?/,jo="[a-zA-Z_][\\-\\.0-9_a-zA-Z"+B+"]*",Io="((?:"+jo+"\\:)?"+jo+")",Lo=new RegExp("^<"+Io),No=/^\s*(\/?)>/,Mo=new RegExp("^<\\/"+Io+"[^>]*>"),Do=/^]+>/i,Po=/^",""":'"',"&":"&"," ":"\n"," ":"\t","'":"'"},Bo=/&(?:lt|gt|quot|amp|#39);/g,Vo=/&(?:lt|gt|quot|amp|#39|#10|#9);/g,zo=m("pre,textarea",!0),qo=function(e,t){return e&&zo(e)&&"\n"===t[0]};function Ko(e,t){var n=t?Vo:Bo;return e.replace(n,function(e){return Uo[e]})}var Jo,Wo,Go,Zo,Xo,Yo,Qo,ea,ta=/^@|^v-on:/,na=/^v-|^@|^:/,ra=/([\s\S]*?)\s+(?:in|of)\s+([\s\S]*)/,ia=/,([^,\}\]]*)(?:,([^,\}\]]*))?$/,oa=/^\(|\)$/g,aa=/^\[.*\]$/,sa=/:(.*)$/,ca=/^:|^\.|^v-bind:/,ua=/\.[^.]+/g,la=/^v-slot(:|$)|^#/,fa=/[\r\n]/,da=/\s+/g,pa=w(Ao),va="_empty_";function ha(e,t,n){return{type:1,tag:e,attrsList:t,attrsMap:function(e){for(var t={},n=0,r=e.length;n]*>)","i")),d=e.replace(f,function(e,n,r){return u=r.length,Fo(l)||"noscript"===l||(n=n.replace(//g,"$1").replace(//g,"$1")),qo(l,n)&&(n=n.slice(1)),t.chars&&t.chars(n),""});c+=e.length-d.length,e=d,k(l,c-u,c)}else{var p=e.indexOf("<");if(0===p){if(Po.test(e)){var v=e.indexOf("--\x3e");if(v>=0){t.shouldKeepComment&&t.comment(e.substring(4,v),c,c+v+3),x(v+3);continue}}if(Ro.test(e)){var h=e.indexOf("]>");if(h>=0){x(h+2);continue}}var m=e.match(Do);if(m){x(m[0].length);continue}var g=e.match(Mo);if(g){var y=c;x(g[0].length),k(g[1],y,c);continue}var _=$();if(_){A(_),qo(_.tagName,e)&&x(1);continue}}var b=void 0,C=void 0,w=void 0;if(p>=0){for(C=e.slice(p);!(Mo.test(C)||Lo.test(C)||Po.test(C)||Ro.test(C)||(w=C.indexOf("<",1))<0);)p+=w,C=e.slice(p);b=e.substring(0,p)}p<0&&(b=e),b&&x(b.length),t.chars&&b&&t.chars(b,c-b.length,c)}if(e===n){t.chars&&t.chars(e);break}}function x(t){c+=t,e=e.substring(t)}function $(){var t=e.match(Lo);if(t){var n,r,i={tagName:t[1],attrs:[],start:c};for(x(t[0].length);!(n=e.match(No))&&(r=e.match(Eo)||e.match(So));)r.start=c,x(r[0].length),r.end=c,i.attrs.push(r);if(n)return i.unarySlash=n[1],x(n[0].length),i.end=c,i}}function A(e){var n=e.tagName,c=e.unarySlash;o&&("p"===r&&Oo(n)&&k(r),s(n)&&r===n&&k(n));for(var u=a(n)||!!c,l=e.attrs.length,f=new Array(l),d=0;d=0&&i[a].lowerCasedTag!==s;a--);else a=0;if(a>=0){for(var u=i.length-1;u>=a;u--)t.end&&t.end(i[u].tag,n,o);i.length=a,r=a&&i[a-1].tag}else"br"===s?t.start&&t.start(e,[],!0,n,o):"p"===s&&(t.start&&t.start(e,[],!1,n,o),t.end&&t.end(e,n,o))}k()}(e,{warn:Jo,expectHTML:t.expectHTML,isUnaryTag:t.isUnaryTag,canBeLeftOpenTag:t.canBeLeftOpenTag,shouldDecodeNewlines:t.shouldDecodeNewlines,shouldDecodeNewlinesForHref:t.shouldDecodeNewlinesForHref,shouldKeepComment:t.comments,outputSourceRange:t.outputSourceRange,start:function(e,n,a,s){var f=i&&i.ns||ea(e);Y&&"svg"===f&&(n=function(e){for(var t=[],n=0;n-1"+("true"===o?":("+t+")":":_q("+t+","+o+")")),Fr(e,"change","var $$a="+t+",$$el=$event.target,$$c=$$el.checked?("+o+"):("+a+");if(Array.isArray($$a)){var $$v="+(r?"_n("+i+")":i)+",$$i=_i($$a,$$v);if($$el.checked){$$i<0&&("+Kr(t,"$$a.concat([$$v])")+")}else{$$i>-1&&("+Kr(t,"$$a.slice(0,$$i).concat($$a.slice($$i+1))")+")}}else{"+Kr(t,"$$c")+"}",null,!0)}(e,r,i);else if("input"===o&&"radio"===a)!function(e,t,n){var r=n&&n.number,i=Ur(e,"value")||"null";Nr(e,"checked","_q("+t+","+(i=r?"_n("+i+")":i)+")"),Fr(e,"change",Kr(t,i),null,!0)}(e,r,i);else if("input"===o||"textarea"===o)!function(e,t,n){var r=e.attrsMap.type,i=n||{},o=i.lazy,a=i.number,s=i.trim,c=!o&&"range"!==r,u=o?"change":"range"===r?Qr:"input",l="$event.target.value";s&&(l="$event.target.value.trim()"),a&&(l="_n("+l+")");var f=Kr(t,l);c&&(f="if($event.target.composing)return;"+f),Nr(e,"value","("+t+")"),Fr(e,u,f,null,!0),(s||a)&&Fr(e,"blur","$forceUpdate()")}(e,r,i);else if(!U.isReservedTag(o))return qr(e,r,i),!1;return!0},text:function(e,t){t.value&&Nr(e,"textContent","_s("+t.value+")",t)},html:function(e,t){t.value&&Nr(e,"innerHTML","_s("+t.value+")",t)}},isPreTag:function(e){return"pre"===e},isUnaryTag:ko,mustUseProp:Dn,canBeLeftOpenTag:To,isReservedTag:Yn,getTagNamespace:Qn,staticKeys:function(e){return e.reduce(function(e,t){return e.concat(t.staticKeys||[])},[]).join(",")}(Aa)},Sa=w(function(e){return m("type,tag,attrsList,attrsMap,plain,parent,children,attrs,start,end,rawAttrsMap"+(e?","+e:""))});function Ea(e,t){e&&(ka=Sa(t.staticKeys||""),Ta=t.isReservedTag||L,function e(t){t.static=function(e){if(2===e.type)return!1;if(3===e.type)return!0;return!(!e.pre&&(e.hasBindings||e.if||e.for||g(e.tag)||!Ta(e.tag)||function(e){for(;e.parent;){if("template"!==(e=e.parent).tag)return!1;if(e.for)return!0}return!1}(e)||!Object.keys(e).every(ka)))}(t);if(1===t.type){if(!Ta(t.tag)&&"slot"!==t.tag&&null==t.attrsMap["inline-template"])return;for(var n=0,r=t.children.length;n|^function\s*\(/,Ia=/\([^)]*?\);*$/,La=/^[A-Za-z_$][\w$]*(?:\.[A-Za-z_$][\w$]*|\['[^']*?']|\["[^"]*?"]|\[\d+]|\[[A-Za-z_$][\w$]*])*$/,Na={esc:27,tab:9,enter:13,space:32,up:38,left:37,right:39,down:40,delete:[8,46]},Ma={esc:["Esc","Escape"],tab:"Tab",enter:"Enter",space:[" ","Spacebar"],up:["Up","ArrowUp"],left:["Left","ArrowLeft"],right:["Right","ArrowRight"],down:["Down","ArrowDown"],delete:["Backspace","Delete","Del"]},Da=function(e){return"if("+e+")return null;"},Pa={stop:"$event.stopPropagation();",prevent:"$event.preventDefault();",self:Da("$event.target !== $event.currentTarget"),ctrl:Da("!$event.ctrlKey"),shift:Da("!$event.shiftKey"),alt:Da("!$event.altKey"),meta:Da("!$event.metaKey"),left:Da("'button' in $event && $event.button !== 0"),middle:Da("'button' in $event && $event.button !== 1"),right:Da("'button' in $event && $event.button !== 2")};function Ra(e,t){var n=t?"nativeOn:":"on:",r="",i="";for(var o in e){var a=Fa(e[o]);e[o]&&e[o].dynamic?i+=o+","+a+",":r+='"'+o+'":'+a+","}return r="{"+r.slice(0,-1)+"}",i?n+"_d("+r+",["+i.slice(0,-1)+"])":n+r}function Fa(e){if(!e)return"function(){}";if(Array.isArray(e))return"["+e.map(function(e){return Fa(e)}).join(",")+"]";var t=La.test(e.value),n=ja.test(e.value),r=La.test(e.value.replace(Ia,""));if(e.modifiers){var i="",o="",a=[];for(var s in e.modifiers)if(Pa[s])o+=Pa[s],Na[s]&&a.push(s);else if("exact"===s){var c=e.modifiers;o+=Da(["ctrl","shift","alt","meta"].filter(function(e){return!c[e]}).map(function(e){return"$event."+e+"Key"}).join("||"))}else a.push(s);return a.length&&(i+=function(e){return"if(!$event.type.indexOf('key')&&"+e.map(Ha).join("&&")+")return null;"}(a)),o&&(i+=o),"function($event){"+i+(t?"return "+e.value+"($event)":n?"return ("+e.value+")($event)":r?"return "+e.value:e.value)+"}"}return t||n?e.value:"function($event){"+(r?"return "+e.value:e.value)+"}"}function Ha(e){var t=parseInt(e,10);if(t)return"$event.keyCode!=="+t;var n=Na[e],r=Ma[e];return"_k($event.keyCode,"+JSON.stringify(e)+","+JSON.stringify(n)+",$event.key,"+JSON.stringify(r)+")"}var Ua={on:function(e,t){e.wrapListeners=function(e){return"_g("+e+","+t.value+")"}},bind:function(e,t){e.wrapData=function(n){return"_b("+n+",'"+e.tag+"',"+t.value+","+(t.modifiers&&t.modifiers.prop?"true":"false")+(t.modifiers&&t.modifiers.sync?",true":"")+")"}},cloak:I},Ba=function(e){this.options=e,this.warn=e.warn||Ir,this.transforms=Lr(e.modules,"transformCode"),this.dataGenFns=Lr(e.modules,"genData"),this.directives=E(E({},Ua),e.directives);var t=e.isReservedTag||L;this.maybeComponent=function(e){return!!e.component||!t(e.tag)},this.onceId=0,this.staticRenderFns=[],this.pre=!1};function Va(e,t){var n=new Ba(t);return{render:"with(this){return "+(e?za(e,n):'_c("div")')+"}",staticRenderFns:n.staticRenderFns}}function za(e,t){if(e.parent&&(e.pre=e.pre||e.parent.pre),e.staticRoot&&!e.staticProcessed)return qa(e,t);if(e.once&&!e.onceProcessed)return Ka(e,t);if(e.for&&!e.forProcessed)return Wa(e,t);if(e.if&&!e.ifProcessed)return Ja(e,t);if("template"!==e.tag||e.slotTarget||t.pre){if("slot"===e.tag)return function(e,t){var n=e.slotName||'"default"',r=Ya(e,t),i="_t("+n+(r?","+r:""),o=e.attrs||e.dynamicAttrs?ts((e.attrs||[]).concat(e.dynamicAttrs||[]).map(function(e){return{name:$(e.name),value:e.value,dynamic:e.dynamic}})):null,a=e.attrsMap["v-bind"];!o&&!a||r||(i+=",null");o&&(i+=","+o);a&&(i+=(o?"":",null")+","+a);return i+")"}(e,t);var n;if(e.component)n=function(e,t,n){var r=t.inlineTemplate?null:Ya(t,n,!0);return"_c("+e+","+Ga(t,n)+(r?","+r:"")+")"}(e.component,e,t);else{var r;(!e.plain||e.pre&&t.maybeComponent(e))&&(r=Ga(e,t));var i=e.inlineTemplate?null:Ya(e,t,!0);n="_c('"+e.tag+"'"+(r?","+r:"")+(i?","+i:"")+")"}for(var o=0;o':'
',as.innerHTML.indexOf(" ")>0}var ls=!!W&&us(!1),fs=!!W&&us(!0),ds=w(function(e){var t=nr(e);return t&&t.innerHTML}),ps=kn.prototype.$mount;kn.prototype.$mount=function(e,t){if((e=e&&nr(e))===document.body||e===document.documentElement)return this;var n=this.$options;if(!n.render){var r=n.template;if(r)if("string"==typeof r)"#"===r.charAt(0)&&(r=ds(r));else{if(!r.nodeType)return this;r=r.innerHTML}else e&&(r=function(e){if(e.outerHTML)return e.outerHTML;var t=document.createElement("div");return t.appendChild(e.cloneNode(!0)),t.innerHTML}(e));if(r){0;var i=cs(r,{outputSourceRange:!1,shouldDecodeNewlines:ls,shouldDecodeNewlinesForHref:fs,delimiters:n.delimiters,comments:n.comments},this),o=i.render,a=i.staticRenderFns;n.render=o,n.staticRenderFns=a}}return ps.call(this,e,t)},kn.compile=cs,t.a=kn}).call(t,n(0),n(8).setImmediate)},function(e,t,n){(function(e){var r=void 0!==e&&e||"undefined"!=typeof self&&self||window,i=Function.prototype.apply;function o(e,t){this._id=e,this._clearFn=t}t.setTimeout=function(){return new o(i.call(setTimeout,r,arguments),clearTimeout)},t.setInterval=function(){return new o(i.call(setInterval,r,arguments),clearInterval)},t.clearTimeout=t.clearInterval=function(e){e&&e.close()},o.prototype.unref=o.prototype.ref=function(){},o.prototype.close=function(){this._clearFn.call(r,this._id)},t.enroll=function(e,t){clearTimeout(e._idleTimeoutId),e._idleTimeout=t},t.unenroll=function(e){clearTimeout(e._idleTimeoutId),e._idleTimeout=-1},t._unrefActive=t.active=function(e){clearTimeout(e._idleTimeoutId);var t=e._idleTimeout;t>=0&&(e._idleTimeoutId=setTimeout(function(){e._onTimeout&&e._onTimeout()},t))},n(9),t.setImmediate="undefined"!=typeof self&&self.setImmediate||void 0!==e&&e.setImmediate||this&&this.setImmediate,t.clearImmediate="undefined"!=typeof self&&self.clearImmediate||void 0!==e&&e.clearImmediate||this&&this.clearImmediate}).call(t,n(0))},function(e,t,n){(function(e,t){!function(e,n){"use strict";if(!e.setImmediate){var r,i,o,a,s,c=1,u={},l=!1,f=e.document,d=Object.getPrototypeOf&&Object.getPrototypeOf(e);d=d&&d.setTimeout?d:e,"[object process]"==={}.toString.call(e.process)?r=function(e){t.nextTick(function(){v(e)})}:!function(){if(e.postMessage&&!e.importScripts){var t=!0,n=e.onmessage;return e.onmessage=function(){t=!1},e.postMessage("","*"),e.onmessage=n,t}}()?e.MessageChannel?((o=new MessageChannel).port1.onmessage=function(e){v(e.data)},r=function(e){o.port2.postMessage(e)}):f&&"onreadystatechange"in f.createElement("script")?(i=f.documentElement,r=function(e){var t=f.createElement("script");t.onreadystatechange=function(){v(e),t.onreadystatechange=null,i.removeChild(t),t=null},i.appendChild(t)}):r=function(e){setTimeout(v,0,e)}:(a="setImmediate$"+Math.random()+"$",s=function(t){t.source===e&&"string"==typeof t.data&&0===t.data.indexOf(a)&&v(+t.data.slice(a.length))},e.addEventListener?e.addEventListener("message",s,!1):e.attachEvent("onmessage",s),r=function(t){e.postMessage(a+t,"*")}),d.setImmediate=function(e){"function"!=typeof e&&(e=new Function(""+e));for(var t=new Array(arguments.length-1),n=0;n1)for(var n=1;n=55296&&i<=56319&&n]*>/gi,"")),t.ignore&&r.call(t.ignore,function(e){n=n.replace(e,"")});const o=n.trim();return{paragraphs:o?(o.match(t.hardReturns?/\n{2,}/g:/\n+/g)||[]).length+1:0,sentences:o?(o.match(/[.?!…]+./g)||[]).length+1:0,words:o?(o.replace(/['";:,.?¿\-!¡]+/g,"").match(/\S+/g)||[]).length:0,characters:o?i(o.replace(/\s/g,"")).length:0,all:i(n).length}}const s={on:function(e,t,i){if(o(e,t))return Array.isArray(e)||(e=[e]),r.call(e,function(e){const r=function(){t.call(e,a(e,i))};n.push({element:e,handler:r}),r(),e.addEventListener("input",r)}),this},off:function(e){if(o(e,function(){}))return Array.isArray(e)||(e=[e]),n.filter(function(t){return-1!==e.indexOf(t.element)}).forEach(function(e){e.element.removeEventListener("input",e.handler)}),n=n.filter(function(t){return-1===e.indexOf(t.element)}),this},count:function(e,t,n){if(o(e,t))return Array.isArray(e)||(e=[e]),r.call(e,function(e){t.call(e,a(e,n))}),this},enabled:function(e){return void 0===e.length&&(e=[e]),n.filter(function(t){return-1!==e.indexOf(t.element)}).length===e.length}};e.exports=s}()},function(e,t,n){"use strict";var r={render:function(){var e=this.$createElement;return(this._self._c||e)("div")},staticRenderFns:[]};t.a=r}]); 8 | //# sourceMappingURL=build.js.map -------------------------------------------------------------------------------- /docs/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | vue-countable 10 | 11 | 12 | 13 |
14 | 15 | -------------------------------------------------------------------------------- /example/App.vue: -------------------------------------------------------------------------------- 1 | 96 | 97 | 120 | 121 | -------------------------------------------------------------------------------- /example/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | vue-countable 10 | 11 | 12 | 13 |
14 | 15 | -------------------------------------------------------------------------------- /example/main.js: -------------------------------------------------------------------------------- 1 | import Vue from 'vue' 2 | import App from './App.vue' 3 | 4 | import VueCountable from '../src/index.js' 5 | Vue.component('vue-countable', VueCountable) 6 | 7 | new Vue({ 8 | el: '#app', 9 | render: h => h(App) 10 | }) 11 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "vue-countable", 3 | "version": "1.0.9", 4 | "description": "Vue binding for countable.js. Provides real-time paragraph, sentence, word, and character counting.", 5 | "author": "John Datserakis ", 6 | "license": "MIT", 7 | "repository": { 8 | "type": "git", 9 | "url": "git+https://github.com/johndatserakis/vue-countable.git" 10 | }, 11 | "bugs": { 12 | "url": "https://github.com/johndatserakis/vue-countable/issues" 13 | }, 14 | "keywords": [ 15 | "vue", 16 | "counter", 17 | "countable", 18 | "count", 19 | "characters", 20 | "words", 21 | "paragraphs", 22 | "sentences" 23 | ], 24 | "main": "dist/vue-countable.umd.js", 25 | "module": "dist/vue-countable.esm.js", 26 | "unpkg": "dist/vue-countable.min.js", 27 | "browser": { 28 | "./sfc": "src/vue-countable.vue" 29 | }, 30 | "scripts": { 31 | "watch": "cross-env NODE_ENV=development webpack-dev-server --open --hot", 32 | "build": "npm run test && npm run build:example && npm run build:library", 33 | "build:umd": "rollup --config build/rollup.config.js --format umd --file dist/vue-countable.umd.js", 34 | "build:es": "rollup --config build/rollup.config.js --format es --file dist/vue-countable.esm.js", 35 | "build:unpkg": "rollup --config build/rollup.config.js --format iife --file dist/vue-countable.min.js", 36 | "build:library": "rm -rf ./dist npm run build:unpkg & npm run build:es & npm run build:umd & npm run build:unpkg", 37 | "build:example": "rm -rf ./docs && cross-env NODE_ENV=production webpack --progress --hide-modules", 38 | "test": "jest" 39 | }, 40 | "dependencies": { 41 | "countable": "^3.0.1" 42 | }, 43 | "devDependencies": { 44 | "@vue/test-utils": "^1.0.0-beta.25", 45 | "babel-core": "^6.26.0", 46 | "babel-loader": "^7.1.2", 47 | "babel-preset-env": "^1.6.0", 48 | "babel-preset-stage-3": "^6.24.1", 49 | "cross-env": "^5.1.6", 50 | "css-loader": "^0.28.7", 51 | "file-loader": "^1.1.4", 52 | "html-webpack-plugin": "^3.2.0", 53 | "jest": "^23.6.0", 54 | "jest-serializer-vue": "^2.0.2", 55 | "minimist": "^1.2.0", 56 | "node-sass": "^4.5.3", 57 | "rollup": "^0.57.1", 58 | "rollup-plugin-buble": "^0.19.2", 59 | "rollup-plugin-commonjs": "^9.2.0", 60 | "rollup-plugin-css-only": "^0.4.0", 61 | "rollup-plugin-node-resolve": "^4.0.0", 62 | "rollup-plugin-terser": "^4.0.3", 63 | "rollup-plugin-vue": "^4.3.0", 64 | "sass-loader": "^7.0.1", 65 | "uglifyjs-webpack-plugin": "^1.3.0", 66 | "vue": "^2.5.16", 67 | "vue-jest": "^2.6.0", 68 | "vue-loader": "^13.0.5", 69 | "vue-template-compiler": "^2.5.16", 70 | "webpack": "^3.6.0", 71 | "webpack-dev-server": "^2.9.1" 72 | }, 73 | "jest": { 74 | "moduleFileExtensions": [ 75 | "js", 76 | "vue" 77 | ], 78 | "moduleNameMapper": { 79 | "^@/(.*)$": "/src/$1" 80 | }, 81 | "transform": { 82 | "^.+\\.js$": "/node_modules/babel-jest", 83 | ".*\\.(vue)$": "/node_modules/vue-jest" 84 | }, 85 | "snapshotSerializers": [ 86 | "/node_modules/jest-serializer-vue" 87 | ] 88 | } 89 | } 90 | -------------------------------------------------------------------------------- /src/index.js: -------------------------------------------------------------------------------- 1 | // Import vue component 2 | import component from './vue-countable.vue'; 3 | 4 | // install function executed by Vue.use() 5 | export function install(Vue) { 6 | if (install.installed) return; 7 | install.installed = true; 8 | Vue.component('VueCountable', component); 9 | } 10 | 11 | // Create module definition for Vue.use() 12 | const plugin = { 13 | install, 14 | }; 15 | 16 | // To auto-install when vue is found 17 | let GlobalVue = null; 18 | if (typeof window !== 'undefined') { 19 | GlobalVue = window.Vue; 20 | } else if (typeof global !== 'undefined') { 21 | GlobalVue = global.Vue; 22 | } 23 | if (GlobalVue) { 24 | GlobalVue.use(plugin); 25 | } 26 | 27 | // To allow use as module (npm/webpack/etc.) export component 28 | export default component; 29 | 30 | // It's possible to expose named exports when writing components that can 31 | // also be used as directives, etc. - eg. import { RollupDemoDirective } from 'rollup-demo'; 32 | // export const RollupDemoDirective = component; -------------------------------------------------------------------------------- /src/vue-countable.vue: -------------------------------------------------------------------------------- 1 | 3 | 4 | 75 | 76 | -------------------------------------------------------------------------------- /test/VueCountable.spec.js: -------------------------------------------------------------------------------- 1 | import { shallowMount } from '@vue/test-utils' 2 | import VueCountable from '@/vue-countable.vue' 3 | 4 | describe('VueCountable.vue', () => { 5 | it('Sets props correctly', async () => { 6 | let initialPropsData = { 7 | elementId: 'first-div', 8 | text: 'We\'re like \'Friends\'. I am Chandler and Joey and, uh, Pam is Rachel. And Dwight is Kramer.' 9 | } 10 | 11 | const wrapper = shallowMount(VueCountable, { 12 | propsData: { 13 | elementId: initialPropsData.elementId, 14 | text: initialPropsData.text 15 | } 16 | }) 17 | 18 | expect(wrapper.vm.elementId).toBe(initialPropsData.elementId) 19 | expect(wrapper.vm.text).toBe(initialPropsData.text) 20 | }) 21 | 22 | it('Reads sentence character count correctly', async () => { 23 | let initialPropsData = { 24 | elementId: 'first-div', 25 | text: 'We\'re like \'Friends\'. I am Chandler and Joey and, uh, Pam is Rachel. And Dwight is Kramer.' 26 | } 27 | 28 | const wrapper = shallowMount(VueCountable, { 29 | propsData: { 30 | elementId: initialPropsData.elementId, 31 | text: initialPropsData.text 32 | } 33 | }) 34 | 35 | expect(wrapper.vm.elementId).toBe(initialPropsData.elementId) 36 | expect(wrapper.vm.text).toBe(initialPropsData.text) 37 | 38 | wrapper.vm.$nextTick(() => { 39 | expect(wrapper.emitted('change')).toBeTruthy() 40 | expect(wrapper.emitted('change')[0][0].paragraphs).toBe(1) 41 | expect(wrapper.emitted('change')[0][0].sentences).toBe(3) 42 | expect(wrapper.emitted('change')[0][0].words).toBe(17) 43 | expect(wrapper.emitted('change')[0][0].characters).toBe(74) 44 | expect(wrapper.emitted('change')[0][0].all).toBe(90) 45 | }) 46 | }) 47 | 48 | it('Reads long text character count correctly', async () => { 49 | let initialPropsData = { 50 | elementId: 'first-div', 51 | text: 'Yes, I was the first one out. And, yes, I\'ve heard women and children first. But, we do not employ children. We are not a sweat shop. Thankfully. And, women are equal in the workplace by law. So, I let them out first, I have a lawsuit on my hands.' 52 | } 53 | 54 | const wrapper = shallowMount(VueCountable, { 55 | propsData: { 56 | elementId: initialPropsData.elementId, 57 | text: initialPropsData.text 58 | } 59 | }) 60 | 61 | expect(wrapper.vm.elementId).toBe(initialPropsData.elementId) 62 | expect(wrapper.vm.text).toBe(initialPropsData.text) 63 | 64 | wrapper.vm.$nextTick(() => { 65 | expect(wrapper.emitted('change')).toBeTruthy() 66 | expect(wrapper.emitted('change')[0][0].paragraphs).toBe(1) 67 | expect(wrapper.emitted('change')[0][0].sentences).toBe(7) 68 | expect(wrapper.emitted('change')[0][0].words).toBe(50) 69 | expect(wrapper.emitted('change')[0][0].characters).toBe(198) 70 | expect(wrapper.emitted('change')[0][0].all).toBe(247) 71 | }) 72 | }) 73 | }) -------------------------------------------------------------------------------- /webpack.config.js: -------------------------------------------------------------------------------- 1 | const path = require('path') 2 | const webpack = require('webpack') 3 | const HtmlWebpackPlugin = require('html-webpack-plugin') 4 | const UglifyJsPlugin = require("uglifyjs-webpack-plugin") 5 | 6 | module.exports = { 7 | entry: './example/main.js', 8 | output: { 9 | path: path.resolve(__dirname, './docs'), 10 | publicPath: (process.env.NODE_ENV === 'development') ? '/' : '/vue-countable/', 11 | filename: 'build.js' 12 | }, 13 | module: { 14 | rules: [ 15 | { 16 | test: /\.css$/, 17 | use: [ 18 | 'vue-style-loader', 19 | 'css-loader' 20 | ], 21 | }, 22 | { 23 | test: /\.scss$/, 24 | use: [ 25 | 'vue-style-loader', 26 | 'css-loader', 27 | 'sass-loader' 28 | ], 29 | }, 30 | { 31 | test: /\.sass$/, 32 | use: [ 33 | 'vue-style-loader', 34 | 'css-loader', 35 | 'sass-loader?indentedSyntax' 36 | ], 37 | }, 38 | { 39 | test: /\.vue$/, 40 | loader: 'vue-loader', 41 | options: { 42 | loaders: { 43 | // Since sass-loader (weirdly) has SCSS as its default parse mode, we map 44 | // the "scss" and "sass" values for the lang attribute to the right configs here. 45 | // other preprocessors should work out of the box, no loader config like this necessary. 46 | 'scss': [ 47 | 'vue-style-loader', 48 | 'css-loader', 49 | 'sass-loader' 50 | ], 51 | 'sass': [ 52 | 'vue-style-loader', 53 | 'css-loader', 54 | 'sass-loader?indentedSyntax' 55 | ] 56 | } 57 | // other vue-loader options go here 58 | } 59 | }, 60 | { 61 | test: /\.js$/, 62 | loader: 'babel-loader', 63 | exclude: /node_modules/ 64 | }, 65 | { 66 | test: /\.(png|jpg|gif|svg)$/, 67 | loader: 'file-loader', 68 | options: { 69 | name: '[name].[ext]?[hash]' 70 | } 71 | } 72 | ] 73 | }, 74 | resolve: { 75 | alias: { 76 | 'vue$': 'vue/dist/vue.esm.js' 77 | }, 78 | extensions: ['*', '.js', '.vue', '.json'] 79 | }, 80 | devServer: { 81 | historyApiFallback: true, 82 | noInfo: true, 83 | overlay: true 84 | }, 85 | performance: { 86 | hints: false 87 | }, 88 | devtool: '#eval-source-map', 89 | plugins: [ 90 | new HtmlWebpackPlugin({ 91 | title: 'vue-countable', 92 | template: './example/index.html' 93 | }) 94 | ] 95 | } 96 | 97 | if (process.env.NODE_ENV === 'production') { 98 | module.exports.devtool = '#source-map' 99 | // http://vue-loader.vuejs.org/en/workflow/production.html 100 | module.exports.plugins = (module.exports.plugins || []).concat([ 101 | new webpack.DefinePlugin({ 102 | 'process.env': { 103 | NODE_ENV: '"production"' 104 | } 105 | }), 106 | // This is done a little different then the rest of my 107 | // components due to something in countable.js 108 | // See https://github.com/webpack/webpack/issues/2972 109 | new UglifyJsPlugin({ 110 | test: /\.js($|\?)/i, 111 | sourceMap: true, 112 | uglifyOptions: { 113 | compress: true 114 | } 115 | }), 116 | new webpack.LoaderOptionsPlugin({ 117 | minimize: true 118 | }) 119 | ]) 120 | } --------------------------------------------------------------------------------