├── .gitignore
├── .babelrc
├── .prettierrc.js
├── .eslintrc.js
├── index.js
├── utils.js
├── LICENSE
├── rollup.config.js
├── package.json
├── test
├── index.spec.js
├── mixins.isSwitchTab.spec.js
└── mixins.spec.js
├── README.md
└── mixin.js
/.gitignore:
--------------------------------------------------------------------------------
1 | .DS_Store
2 | *.log
3 | node_modules
4 | dist
5 | build
6 | .idea
7 | report
8 | npm-debug*
9 | .vscode
10 | index.min.js
--------------------------------------------------------------------------------
/.babelrc:
--------------------------------------------------------------------------------
1 | {
2 | "presets": [
3 | ["@babel/preset-env", {
4 | "targets": { "node": "6" } // change this to your node version
5 | }]
6 | ]
7 | }
8 |
--------------------------------------------------------------------------------
/.prettierrc.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | semi: false,
3 | parser: 'babylon',
4 | useTabs: false,
5 | printWidth: 80,
6 | singleQuote: true,
7 | bracketSpacing: true
8 | }
9 |
--------------------------------------------------------------------------------
/.eslintrc.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | extends: 'standard',
3 |
4 | env: {
5 | node: true,
6 | browser: true
7 | },
8 |
9 | rules: {
10 | 'space-before-function-paren': 0
11 | },
12 | }
13 |
--------------------------------------------------------------------------------
/index.js:
--------------------------------------------------------------------------------
1 | import VueTimers from './mixin'
2 | import { assign } from './utils'
3 |
4 | export default function install(Vue) {
5 | Vue.config.optionMergeStrategies.timers = Vue.config.optionMergeStrategies.methods
6 | Vue.mixin(VueTimers)
7 | }
8 |
9 | if (typeof window !== 'undefined' && window.Vue) {
10 | install(window.Vue)
11 | }
12 |
13 | export function timer(name, time, options) {
14 | return assign({ name: name, time: time }, options)
15 | }
16 |
17 | export const mixin = VueTimers
18 |
--------------------------------------------------------------------------------
/utils.js:
--------------------------------------------------------------------------------
1 | export const set = function(key, value, obj) {
2 | const clone = assign({}, obj)
3 | clone[key] = value
4 | return clone
5 | }
6 |
7 | /**
8 | * Polyfill for Object.assign for IE11 support
9 | */
10 | export const assign =
11 | Object.assign ||
12 | function assign(to) {
13 | for (var s = 1; s < arguments.length; s += 1) {
14 | var from = arguments[s]
15 | for (var key in from) {
16 | to[key] = from[key]
17 | }
18 | }
19 | return to
20 | }
21 |
22 | export const isArray =
23 | Array.isArray ||
24 | function(arg) {
25 | return Object.prototype.toString.call(arg) === '[object Array]'
26 | }
27 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2017 Anton Kosykh
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 |
--------------------------------------------------------------------------------
/rollup.config.js:
--------------------------------------------------------------------------------
1 | import resolve from 'rollup-plugin-node-resolve'
2 | import babel from 'rollup-plugin-babel'
3 | import replace from 'rollup-plugin-replace'
4 | const terser = require('rollup-plugin-terser').terser
5 |
6 | export default [
7 | {
8 | input: 'index.js',
9 | output: {
10 | file: 'dist/vue-timers.esm.js',
11 | format: 'es'
12 | },
13 | plugins: [
14 | resolve(),
15 | // use own babel config to compile
16 | babel({
17 | exclude: 'node_modules/**',
18 | presets: [
19 | [
20 | '@babel/preset-env',
21 | {
22 | modules: false
23 | }
24 | ]
25 | ],
26 | plugins: ['@babel/plugin-external-helpers'],
27 | babelrc: false
28 | }),
29 | terser()
30 | ]
31 | },
32 | {
33 | input: 'index.js',
34 | output: {
35 | file: 'dist/vue-timers.umd.js',
36 | format: 'umd',
37 | name: 'vueTimers'
38 | },
39 | plugins: [
40 | resolve(),
41 | // use own babel config to compile
42 | babel({
43 | exclude: 'node_modules/**',
44 | presets: [
45 | [
46 | '@babel/preset-env',
47 | {
48 | modules: false
49 | }
50 | ]
51 | ],
52 | plugins: ['@babel/plugin-external-helpers'],
53 | babelrc: false
54 | }),
55 | replace({
56 | 'process.env.NODE_ENV': JSON.stringify('production')
57 | }),
58 | terser()
59 | ]
60 | }
61 | ]
62 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "vue-timers",
3 | "version": "2.0.0",
4 | "license": "MIT",
5 | "description": "Mixin to manage your intervals in Vue.js",
6 | "main": "dist/vue-timers.umd.js",
7 | "module": "dist/vue-timers.esm.js",
8 | "jsdelivr": "dist/vue-timers.umd.js",
9 | "repository": "https://github.com/kelin2025/vue-timers",
10 | "author": {
11 | "email": "kelin2025@yandex.ru",
12 | "name": "Anton Kosykh"
13 | },
14 | "scripts": {
15 | "lint": "eslint --fix index.js mixin.js utils.js",
16 | "size": "size-limit",
17 | "test": "jest",
18 | "build": "rollup -c rollup.config.js"
19 | },
20 | "size-limit": [
21 | {
22 | "path": "./index.min.js",
23 | "limit": "600 B"
24 | }
25 | ],
26 | "devDependencies": {
27 | "@babel/core": "^7.4.3",
28 | "@babel/plugin-external-helpers": "^7.2.0",
29 | "@babel/preset-env": "^7.4.3",
30 | "@vue/test-utils": "^1.0.0-beta.29",
31 | "babel-jest": "^24.7.1",
32 | "eslint": "^4.12.1",
33 | "eslint-config-standard": "^10.2.1",
34 | "eslint-plugin-import": "^2.8.0",
35 | "eslint-plugin-node": "^5.2.1",
36 | "eslint-plugin-promise": "^3.6.0",
37 | "eslint-plugin-standard": "^3.0.1",
38 | "jest": "^24.7.1",
39 | "rollup": "^0.67.4",
40 | "rollup-plugin-babel": "^4.3.2",
41 | "rollup-plugin-node-resolve": "^4.0.0",
42 | "rollup-plugin-replace": "^2.2.0",
43 | "rollup-plugin-terser": "^3.0.0",
44 | "size-limit": "^1.0.1",
45 | "vue": "^2.5.17",
46 | "vue-template-compiler": "^2.5.17"
47 | },
48 | "dependencies": {}
49 | }
50 |
--------------------------------------------------------------------------------
/test/index.spec.js:
--------------------------------------------------------------------------------
1 | import VueTimers from '../index'
2 | import { mount, createLocalVue } from '@vue/test-utils'
3 |
4 | const localVue = createLocalVue()
5 | localVue.use(VueTimers)
6 |
7 | const component = {
8 | template: '
',
9 | data() {
10 | return {
11 | count: 0
12 | }
13 | },
14 | methods: {
15 | log() {
16 | this.count++
17 | }
18 | }
19 | }
20 |
21 | describe('global import', () => {
22 | beforeAll(() => {
23 | jest.useFakeTimers()
24 | })
25 |
26 | afterEach(() => {
27 | jest.clearAllTimers()
28 | })
29 |
30 | afterAll(() => {
31 | jest.useRealTimers()
32 | })
33 |
34 | it('test setTimeout', () => {
35 | const wrapper = mount(component, {
36 | localVue,
37 | timers: {
38 | log: { time: 1000 }
39 | }
40 | })
41 |
42 | wrapper.vm.$timer.start('log')
43 | expect(wrapper.vm.timers.log.isRunning).toBe(true)
44 | expect(setTimeout).toHaveBeenCalledTimes(1)
45 | expect(setTimeout).toHaveBeenLastCalledWith(expect.any(Function), 1000)
46 |
47 | expect(wrapper.vm.count).toBe(0)
48 | jest.advanceTimersByTime(1000)
49 | expect(wrapper.vm.count).toBe(1)
50 |
51 | wrapper.vm.$timer.stop('log')
52 | expect(wrapper.vm.timers.log.isRunning).toBe(false)
53 | })
54 |
55 | it('test setInterval', () => {
56 | const wrapper = mount(component, {
57 | localVue,
58 | timers: {
59 | log: { time: 1000, repeat: true }
60 | }
61 | })
62 |
63 | wrapper.vm.$timer.start('log')
64 | expect(wrapper.vm.timers.log.isRunning).toBe(true)
65 |
66 | expect(wrapper.vm.count).toBe(0)
67 | jest.advanceTimersByTime(1000)
68 | expect(wrapper.vm.count).toBe(1)
69 | jest.advanceTimersByTime(1000)
70 | expect(wrapper.vm.count).toBe(2)
71 |
72 | wrapper.vm.$timer.stop('log')
73 | expect(wrapper.vm.timers.log.isRunning).toBe(false)
74 | })
75 | })
76 |
--------------------------------------------------------------------------------
/test/mixins.isSwitchTab.spec.js:
--------------------------------------------------------------------------------
1 | import { mount } from '@vue/test-utils'
2 | import {mixin as VueTimers} from '../index'
3 |
4 | const component1 = {
5 | template: '',
6 | name: 'component1',
7 | mixins: [VueTimers],
8 | timers: {
9 | log: { time: 1000, autostart: true, isSwitchTab: true }
10 | },
11 | data() {
12 | return {
13 | count: 0
14 | }
15 | },
16 | methods: {
17 | log() {
18 | this.count++
19 | }
20 | }
21 | }
22 |
23 | const component2 = {
24 | template: '',
25 | name: 'component2'
26 | }
27 |
28 | const view = {
29 | template: `
30 |
31 | `,
32 | components: { component1, component2 },
33 | data() {
34 | return {
35 | view: 'component1'
36 | }
37 | }
38 | }
39 |
40 | describe('isSwitchTab: true', () => {
41 | beforeAll(() => {
42 | jest.useFakeTimers()
43 | })
44 |
45 | afterEach(() => {
46 | jest.clearAllTimers()
47 | })
48 |
49 | afterAll(() => {
50 | jest.useRealTimers()
51 | })
52 |
53 | it('test switch tab', async () => {
54 | const wrapper = mount(view)
55 | const child = wrapper.vm.$children[0]
56 |
57 | expect(child.timers.log.isRunning).toBe(true)
58 | expect(child.count).toBe(0)
59 | jest.advanceTimersByTime(1000)
60 | expect(child.count).toBe(1)
61 |
62 | // swtich tab to component2
63 | wrapper.setData({ view: 'component2' })
64 | await wrapper.vm.$nextTick()
65 | expect(child.timers.log.isRunning).toBe(false)
66 | // timer will not execute when deactivated
67 | jest.advanceTimersByTime(1000)
68 | expect(child.count).toBe(1)
69 |
70 | // swtich tab back to component1
71 | wrapper.setData({ view: 'component1' })
72 | await wrapper.vm.$nextTick()
73 | expect(child.timers.log.isRunning).toBe(true)
74 | // timer will execute when activated
75 | jest.advanceTimersByTime(1000)
76 | expect(child.count).toBe(2)
77 | })
78 |
79 | it('test switch tab with immediate', async () => {
80 | // change timers`s config
81 | component1.timers = {
82 | log: { time: 1000, autostart: true, isSwitchTab: true, immediate: true }
83 | }
84 | const wrapper = mount(view)
85 | const child = wrapper.vm.$children[0]
86 |
87 | expect(child.timers.log.isRunning).toBe(true)
88 | expect(child.count).toBe(1)
89 | jest.advanceTimersByTime(1000)
90 | expect(child.count).toBe(2)
91 |
92 | // swtich tab to component2
93 | wrapper.setData({ view: 'component2' })
94 | await wrapper.vm.$nextTick()
95 | expect(child.timers.log.isRunning).toBe(false)
96 | // timer will not execute when deactivated
97 | jest.advanceTimersByTime(1000)
98 | expect(child.count).toBe(2)
99 |
100 | // swtich tab back to component1
101 | wrapper.setData({ view: 'component1' })
102 | await wrapper.vm.$nextTick()
103 | expect(child.timers.log.isRunning).toBe(true)
104 | // timer will execute when activated
105 | expect(child.count).toBe(3)
106 | jest.advanceTimersByTime(1000)
107 | expect(child.count).toBe(4)
108 | })
109 | })
110 |
--------------------------------------------------------------------------------
/test/mixins.spec.js:
--------------------------------------------------------------------------------
1 | import { mount } from '@vue/test-utils'
2 | import {mixin as VueTimers} from '../index'
3 |
4 | const component = {
5 | template: '',
6 | mixins: [VueTimers],
7 | timers: {
8 | log: { time: 1000 }
9 | },
10 | data() {
11 | return {
12 | count: 0
13 | }
14 | },
15 | methods: {
16 | log() {
17 | this.count++
18 | }
19 | }
20 | }
21 |
22 | describe('default options', () => {
23 | // Now mount the component and you have the wrapper
24 | const wrapper = mount(component)
25 |
26 | beforeAll(() => {
27 | jest.useFakeTimers()
28 | })
29 |
30 | afterEach(() => {
31 | jest.clearAllTimers()
32 | })
33 |
34 | afterAll(() => {
35 | jest.useRealTimers()
36 | })
37 |
38 | it('start the timer which not exist', () => {
39 | try {
40 | wrapper.vm.$timer.start('notExist')
41 | } catch (e) {
42 | expect(e).toEqual(
43 | new ReferenceError('[vue-timers.start] Cannot find timer notExist')
44 | )
45 | }
46 | })
47 |
48 | it('stop the timer which not exist', () => {
49 | try {
50 | wrapper.vm.$timer.stop('notExist')
51 | } catch (e) {
52 | expect(e).toEqual(
53 | new ReferenceError('[vue-timers.stop] Cannot find timer notExist')
54 | )
55 | }
56 | })
57 |
58 | it('should be has default options', () => {
59 | expect(wrapper.vm.$options.timers.log).toEqual({
60 | name: 'log',
61 | time: 1000,
62 | repeat: false,
63 | immediate: false,
64 | autostart: false,
65 | isSwitchTab: false,
66 | callback: wrapper.vm.log
67 | })
68 | })
69 |
70 | it('test start and stop', () => {
71 | wrapper.vm.$timer.start('log')
72 | expect(wrapper.vm.timers.log.isRunning).toBe(true)
73 | expect(wrapper.emitted()['timer-start:log']).toBeTruthy()
74 | expect(wrapper.vm.count).toBe(0)
75 | jest.advanceTimersByTime(1000)
76 | expect(wrapper.vm.count).toBe(1)
77 |
78 | wrapper.vm.$timer.stop('log')
79 | expect(wrapper.vm.timers.log.isRunning).toBe(false)
80 | expect(wrapper.emitted()['timer-stop:log']).toBeTruthy()
81 | })
82 |
83 | it('test restart', () => {
84 | wrapper.vm.$timer.restart('log')
85 | expect(wrapper.vm.timers.log.isRunning).toBe(true)
86 | expect(wrapper.emitted()['timer-restart:log']).toBeTruthy()
87 | })
88 | })
89 |
90 | describe('execute start or stop 2 times', () => {
91 | // Now mount the component and you have the wrapper
92 | const wrapper = mount(component)
93 |
94 | it('test execute start or stop 2 times', () => {
95 | wrapper.vm.$timer.start('log')
96 | wrapper.vm.$timer.start('log')
97 | expect(wrapper.emitted()['timer-start:log'].length).toBe(1)
98 |
99 | wrapper.vm.$timer.stop('log')
100 | wrapper.vm.$timer.stop('log')
101 | expect(wrapper.emitted()['timer-stop:log'].length).toBe(1)
102 | })
103 | })
104 |
105 | describe('autoStart: true', () => {
106 | // Now mount the component and you have the wrapper
107 | const wrapper = mount(component, {
108 | timers: {
109 | log: { time: 1000, autostart: true }
110 | }
111 | })
112 |
113 | it('test start and stop', () => {
114 | expect(wrapper.vm.timers.log.isRunning).toBe(true)
115 | expect(wrapper.emitted()['timer-start:log']).toBeTruthy()
116 | })
117 | })
118 |
119 | describe('immediate: true', () => {
120 | // Now mount the component and you have the wrapper
121 | const wrapper = mount(component, {
122 | timers: {
123 | log: { time: 1000, immediate: true }
124 | }
125 | })
126 |
127 | beforeAll(() => {
128 | jest.useFakeTimers()
129 | })
130 |
131 | afterAll(() => {
132 | jest.clearAllTimers()
133 | jest.useRealTimers()
134 | })
135 |
136 | it('test immediate: true', () => {
137 | wrapper.vm.$timer.start('log')
138 | expect(wrapper.vm.count).toBe(1)
139 | jest.advanceTimersByTime(1000)
140 | expect(wrapper.vm.count).toBe(2)
141 | })
142 | })
143 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | ## vue-timers
2 | Simple mixin to manage timers or intervals for Vue.js
3 |
4 | ## Installation
5 |
6 | #### 1.1 Use CDN
7 | ```
8 |
9 | ```
10 |
11 | #### 1.2 Install from package manager
12 | ```
13 | npm install vue-timers
14 | yarn add vue-timers
15 | ```
16 |
17 | #### 2.1. Global import
18 | ```javascript
19 | import Vue from 'vue'
20 | import VueTimers from 'vue-timers'
21 |
22 | Vue.use(VueTimers)
23 | ```
24 |
25 | #### 2.2. Or use mixin for the specific component
26 | ```javascript
27 | import {mixin as VueTimers} from 'vue-timers'
28 |
29 | export default {
30 | mixins: [VueTimers]
31 | }
32 | ```
33 |
34 | #### 2.3. Nuxt plugins
35 | `nuxt.config.js`:
36 | ```javascript
37 | export default {
38 | plugins: [
39 | { src: '~/plugins/vue-timers', mode: 'client' }
40 | ]
41 | }
42 | ```
43 | `plugins/vue-timers.js`:
44 | ```javascript
45 | import Vue from 'vue'
46 | import VueTimers from 'vue-timers'
47 |
48 | Vue.use(VueTimers)
49 | ```
50 |
51 | ## What it does?
52 | It creates timer instances in components and slightly reduces boilerplate code with their handling.
53 | See the following code
54 | ```javascript
55 | export default {
56 | methods: {
57 | log () {
58 | console.log('Hello world')
59 | }
60 | },
61 | created () {
62 | // It looks OK for the first look
63 | // But imagine that you have more than one timer
64 | this.$options.interval = setInterval(this.log, 1000)
65 | // Ok? What about check if timer works?
66 | // And it's not reactive so you should create data option
67 | console.log(this.$options.interval !== null)
68 | // Still ok? So what about reusable timers?
69 | // New method for that? Rly?
70 | },
71 | // Did you forget that it should be destroyed?
72 | beforeDestroy () {
73 | clearInterval(this.$options.interval)
74 | }
75 | }
76 | ```
77 | It's ugly, isn't it? So let's try to fix this :)
78 |
79 | Same code with `vue-timers`:
80 | ```javascript
81 | export default {
82 | timers: {
83 | log: { time: 1000, autostart: true }
84 | },
85 | methods: {
86 | log () {
87 | console.log('Hello world')
88 | }
89 | }
90 | }
91 | ```
92 |
93 | ## Configuration
94 |
95 | ### Timer object
96 | ```javascript
97 | {
98 | // Name of timer
99 | // Default: timer key (with object notation)
100 | name: String,
101 |
102 | // Tick callback or method name from component
103 | // Note: callback is binded to component instance
104 | // Default: name
105 | callback: Function/String,
106 |
107 | // Autostart timer from created hook
108 | // Default: false
109 | autostart: Boolean,
110 |
111 | // Set true to repeat (with setInterval) or false (setTimeout)
112 | // Default: false
113 | repeat: Boolean,
114 |
115 | // Set true to call first tick immediate
116 | // Note: repeat must be true too
117 | // Default: false
118 | immediate: Boolean,
119 |
120 | // Time between ticks
121 | // Default: 1000
122 | time: Number
123 |
124 | // Switch timer`s status between activated and deactivated
125 | // Default: false
126 | isSwitchTab: Boolean
127 | }
128 | ```
129 |
130 | ### Changing timer duration
131 | ```javascript
132 | this.timers.log.time = 2000
133 | ```
134 | > **NOTE:** you should restart timer to apply changes
135 |
136 | ### Component methods
137 | ```javascript
138 | // Starts `log` timer
139 | this.$timer.start('log')
140 | // Stops `log` timer
141 | this.$timer.stop('log')
142 | ```
143 |
144 | ### isRunning property
145 | ```javascript
146 | this.timers.log.isRunning
147 | ```
148 |
149 | ### Events
150 |
151 | #### TimerComponent.vue
152 | ```javascript
153 |
154 | import { timer } from 'vue-timers'
155 |
156 | export default {
157 | timers: [
158 | timer('log', 1000)
159 | ],
160 | methods: {
161 | log () {
162 | console.log('It works!')
163 | }
164 | }
165 | }
166 | ```
167 |
168 | #### App.vue
169 | ```vue
170 |
171 |
176 |
177 | ```
178 |
179 | ### 3 ways of timers declaration
180 |
181 | #### Object notation
182 | ```javascript
183 | export default {
184 | timers: {
185 | log: { time: 1000, ...options }
186 | }
187 | }
188 | ```
189 |
190 | #### Array notation
191 | ```javascript
192 | export default {
193 | timers: [
194 | { name: 'log', time: 1000, ...options }
195 | ]
196 | }
197 | ```
198 |
199 | #### Helper function
200 | ```javascript
201 | import { timer } from 'vue-timers'
202 |
203 | export default {
204 | timers: [
205 | timer('log', 1000, { ...options })
206 | ]
207 | }
208 | ```
209 |
210 | ## Author
211 | [Anton Kosykh](https://github.com/kelin2025)
212 |
213 | ## License
214 | MIT
215 |
--------------------------------------------------------------------------------
/mixin.js:
--------------------------------------------------------------------------------
1 | import { set, isArray } from './utils'
2 |
3 | function generateData(timers) {
4 | return Object.keys(timers).reduce(function(acc, cur) {
5 | return set(
6 | cur,
7 | {
8 | isRunning: false,
9 | time: timers[cur].time || 0,
10 | instance: null
11 | },
12 | acc
13 | )
14 | }, {})
15 | }
16 |
17 | function setTimer(repeat) {
18 | return repeat ? setInterval : setTimeout
19 | }
20 |
21 | function clearTimer(repeat) {
22 | return repeat ? clearInterval : clearTimeout
23 | }
24 |
25 | function generateTimer(options, vm) {
26 | return setTimer(options.repeat)(function() {
27 | vm.$emit('timer-tick:' + options.name)
28 | options.callback()
29 | }, options.time)
30 | }
31 |
32 | function normalizeConfig(config, vm) {
33 | if (process.env.NODE_ENV !== 'production') {
34 | if (!config.name) {
35 | throw new Error('[vue-timers.create] name is required')
36 | }
37 | if (!config.callback && typeof vm[config.name] !== 'function') {
38 | throw new ReferenceError(
39 | '[vue-timers.create] Cannot find method ' + config.name
40 | )
41 | }
42 | if (config.callback && typeof config.callback !== 'function') {
43 | throw new TypeError(
44 | '[vue-timers.create] Timer callback should be a function, ' +
45 | typeof config.callback +
46 | ' given'
47 | )
48 | }
49 | }
50 | return {
51 | name: config.name,
52 | time: config.time || 0,
53 | repeat: 'repeat' in config ? config.repeat : false,
54 | immediate: 'immediate' in config ? config.immediate : false,
55 | autostart: 'autostart' in config ? config.autostart : false,
56 | isSwitchTab: 'isSwitchTab' in config ? config.isSwitchTab : false,
57 | callback: (config.callback && config.callback.bind(vm)) || vm[config.name]
58 | }
59 | }
60 |
61 | function normalizeOptions(options, vm) {
62 | return isArray(options)
63 | ? options.reduce(function(res, config) {
64 | return set(config.name, normalizeConfig(config, vm), res)
65 | }, {})
66 | : Object.keys(options).reduce(function(res, key) {
67 | return set(
68 | key,
69 | normalizeConfig(set('name', key, options[key]), vm),
70 | res
71 | )
72 | }, {})
73 | }
74 |
75 | export default {
76 | data: function() {
77 | if (!this.$options.timers) return {}
78 | this.$options.timers = normalizeOptions(this.$options.timers, this)
79 | return {
80 | timers: generateData(this.$options.timers)
81 | }
82 | },
83 |
84 | created: function() {
85 | if (!this.$options.timers) return
86 | var vm = this
87 | var data = vm.timers
88 | var options = vm.$options.timers
89 | vm.$timer = {
90 | start: function(name) {
91 | if (process.env.NODE_ENV !== 'production' && !(name in options)) {
92 | throw new ReferenceError(
93 | '[vue-timers.start] Cannot find timer ' + name
94 | )
95 | }
96 | if (data[name].isRunning) return
97 | data[name].isRunning = true
98 | data[name].instance = generateTimer(
99 | set('time', data[name].time, options[name]),
100 | vm
101 | )
102 | if (options[name].immediate) {
103 | vm.$emit('timer-tick:' + name)
104 | options[name].callback()
105 | }
106 | vm.$emit('timer-start:' + name)
107 | },
108 |
109 | stop: function(name) {
110 | if (process.env.NODE_ENV !== 'production' && !(name in options)) {
111 | throw new ReferenceError(
112 | '[vue-timers.stop] Cannot find timer ' + name
113 | )
114 | }
115 | if (!data[name].isRunning) return
116 | clearTimer(options[name].repeat)(data[name].instance)
117 | data[name].isRunning = false
118 | vm.$emit('timer-stop:' + name)
119 | },
120 |
121 | restart: function(name) {
122 | if (process.env.NODE_ENV !== 'production' && !(name in options)) {
123 | throw new ReferenceError(
124 | '[vue-timers.restart] Cannot find timer ' + name
125 | )
126 | }
127 | this.stop(name)
128 | this.start(name)
129 | vm.$emit('timer-restart:' + name)
130 | }
131 | }
132 | },
133 |
134 | mounted: function() {
135 | if (!this.$options.timers) return
136 | var vm = this
137 | var options = vm.$options.timers
138 | Object.keys(options).forEach(function(key) {
139 | if (options[key].autostart) {
140 | vm.$timer.start(key)
141 | }
142 | })
143 | },
144 |
145 | activated: function() {
146 | if (!this.$options.timers) return
147 | var vm = this
148 | var data = vm.timers
149 | var options = vm.$options.timers
150 | Object.keys(options).forEach(function(key) {
151 | if (options[key].isSwitchTab && data[key].instance) {
152 | vm.$timer.start(key)
153 | }
154 | })
155 | },
156 |
157 | deactivated: function() {
158 | if (!this.$options.timers) return
159 | var vm = this
160 | var data = vm.timers
161 | var options = vm.$options.timers
162 | Object.keys(options).forEach(function(key) {
163 | if (options[key].isSwitchTab && data[key].instance) {
164 | vm.$timer.stop(key)
165 | }
166 | })
167 | },
168 |
169 | beforeDestroy: function() {
170 | if (!this.$options.timers) return
171 | var vm = this
172 | Object.keys(vm.$options.timers).forEach(function(key) {
173 | vm.$timer.stop(key)
174 | })
175 | }
176 | }
177 |
--------------------------------------------------------------------------------