blob: 4cef0672d1085181dbc2e9d31d6b99d8f13a361c [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.
*/
import { isArray } from '../utils'
/**
* remove text nodes in the nodes array.
* @param {Array} nodes
* @return {Array} nodes without text nodes.
*/
export function trimTextVNodes (vnodes) {
if (isArray(vnodes)) {
return vnodes.filter(vnode => !!vnode.tag)
}
return vnodes
}
/**
* get listeners from on config and v-on binding.
* v-on binding has a priority over on config.
* @param {vnode} vnode
* @param {String} evt: event name.
*/
function getListeners (vnode, evt) {
const handlers = []
while (vnode) {
if (vnode.data && vnode.data.on) {
const handler = vnode.data.on[evt]
handler && handlers.push(handler)
}
if (vnode.componentOptions && vnode.componentOptions.listeners) {
const handler = vnode.componentOptions.listeners[evt]
handler && handlers.push(handler)
}
vnode = vnode.parent
}
return handlers
}
/**
* Instead of vue's invoker, this function should check if the binding function
* has a _weex_hook flag. If there is one, the handler should not be triggered.
* @param {Array | Function} fns
*/
export function applyFns (fns, ...args) {
if (Array.isArray(fns)) {
const cloned = fns.slice()
const len = cloned.length
for (let i = 0; i < len; i++) {
const fn = cloned[i]
if (fn._weex_hook) {
continue
}
fn.apply(null, args)
}
}
else {
if (!fns._weex_hook) {
fns.apply(null, args)
}
}
}
/**
* emit native events to enable v-on.
* @param {VComponent} context: which one to emit a event on.
* @param {array | object} events: extra events. You can pass in multiple arguments here.
*/
export function createEventMap (context, ...events) {
const eventMap = {}
/**
* Bind some original type event to your specified type event handler.
* e.g. bind 'tap' event to 'click' event listener: bindFunc('tap')('click').
* Or bind certian event with your specified handler: bindFunc('click', someFunction)
*/
const bindFunc = (originalType) => {
return listenTo => {
let handler
const evtName = originalType || listenTo
if (typeof listenTo === 'function') {
handler = listenTo
}
else if (typeof listenTo === 'string') {
handler = function (e) {
/**
* use '_triggered' to control actural bubbling (allow original bubbling).
*/
if (e._triggered) {
return
}
/**
* trigger the closest parent which has bound event handlers.
*/
let vm = context
while (vm) {
const ons = getListeners(vm._vnode || vm.$vnode, listenTo)
const len = ons.length
if (len > 0) {
let idx = 0
while (idx < len) {
const on = ons[idx]
applyFns(on.fns, e)
idx++
}
// once a parent node (or self node) has triggered the handler, then
// it stops bubbling immediately, and a '_triggered' object is set.
e._triggered = {
el: vm.$el
}
return
}
vm = vm.$parent
}
}
// flag to distinguish from user-binding listeners.
handler._weex_hook = true
}
if (!eventMap[evtName]) {
eventMap[evtName] = []
}
eventMap[evtName].push(handler)
}
}
/**
* component's extra event bindings. This is mostly for the needs of component's
* own special behaviours. These handlers will be processed after the user's
* corresponding event handlers.
*/
if (events) {
const len = events.length
for (let i = 0; i < len; i++) {
const extra = events[i]
if (isArray(extra)) {
extra.forEach(bindFunc())
}
else if (typeof extra === 'object') {
for (const key in extra) {
bindFunc(key)(extra[key])
}
}
}
}
return eventMap
}