blob: 569147228942cee8b5f1d3da67c32a0b92c4467f [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
/**
* @fileOverview
* Everything about component event which includes event object, event listener,
* event emitter and lifecycle hooks.
*/
/**
* Event object definition. An event object has `type`, `timestamp` and
* `detail` from which a component emit. The event object could be dispatched to
* parents or broadcasted to children except `this.stop()` is called.
* @param {string} type
* @param {any} detail
*/
function Evt (type, detail) {
if (detail instanceof Evt) {
return detail
}
this.timestamp = Date.now()
this.detail = detail
this.type = type
let shouldStop = false
/**
* stop dispatch and broadcast
*/
this.stop = function () {
shouldStop = true
}
/**
* check if it can't be dispatched or broadcasted
*/
this.hasStopped = function () {
return shouldStop
}
}
/**
* Emit an event but not broadcast down or dispatch up.
* @param {string} type
* @param {any} detail
*/
export function $emit (type, detail) {
const events = this._vmEvents
const handlerList = events[type]
if (handlerList) {
const evt = new Evt(type, detail)
handlerList.forEach((handler) => {
handler.call(this, evt)
})
}
}
/**
* Emit an event and dispatch it up.
* @param {string} type
* @param {any} detail
*/
export function $dispatch (type, detail) {
const evt = new Evt(type, detail)
this.$emit(type, evt)
if (!evt.hasStopped() && this._parent && this._parent.$dispatch) {
this._parent.$dispatch(type, evt)
}
}
/**
* Emit an event and broadcast it down.
* @param {string} type
* @param {any} detail
*/
export function $broadcast (type, detail) {
const evt = new Evt(type, detail)
this.$emit(type, evt)
if (!evt.hasStopped() && this._childrenVms) {
this._childrenVms.forEach((subVm) => {
subVm.$broadcast(type, evt)
})
}
}
/**
* Add event listener.
* @param {string} type
* @param {function} handler
*/
export function $on (type, handler) {
if (!type || typeof handler !== 'function') {
return
}
const events = this._vmEvents
const handlerList = events[type] || []
handlerList.push(handler)
events[type] = handlerList
// fixed old version lifecycle design
/* istanbul ignore if */
if (type === 'hook:ready' && this._ready) {
this.$emit('hook:ready')
}
}
/**
* Remove event listener.
* @param {string} type
* @param {function} handler
*/
export function $off (type, handler) {
if (!type) {
return
}
const events = this._vmEvents
if (!handler) {
delete events[type]
return
}
const handlerList = events[type]
if (!handlerList) {
return
}
handlerList.$remove(handler)
}
const LIFE_CYCLE_TYPES = ['init', 'created', 'ready', 'destroyed']
/**
* Init events:
* 1. listen `events` in component options & `externalEvents`.
* 2. bind lifecycle hooks.
* @param {Vm} vm
* @param {object} externalEvents
*/
export function initEvents (vm, externalEvents) {
const options = vm._options || {}
const events = options.events || {}
for (const type1 in events) {
vm.$on(type1, events[type1])
}
for (const type2 in externalEvents) {
vm.$on(type2, externalEvents[type2])
}
LIFE_CYCLE_TYPES.forEach((type) => {
vm.$on(`hook:${type}`, options[type])
})
}
/**
* Bind event related methods to ViewModel instance.
* @param {Vm} vm
*/
export function mixinEvents (vm) {
vm.$emit = $emit
vm.$dispatch = $dispatch
vm.$broadcast = $broadcast
vm.$on = $on
vm.$off = $off
}