UNPKG

@dolphinweex/dof-weex-vue-precompiler

Version:

a precompiler for weex-vue-render.

229 lines (216 loc) 5.78 kB
const { extend } = require('../util') const transEvtsMap = { appear: 'appear', offsetAppear: 'offset-appear', disappear: 'disappear', offsetDisappear: 'offset-disappear' } const fnExpRE = /^([\w$_]+|\([^)]*?\))\s*=>|^function(?:\s+[\w$]+)?\s*\(/ const fnInvokeRE = /\([^)]*?\);*$/ const simplePathRE = /^[A-Za-z_$][\w$]*(?:\.[A-Za-z_$][\w$]*|\['[^']*?']|\["[^"]*?"]|\[\d+]|\[[A-Za-z_$][\w$]*])*$/ /** * check whether has a parent that meet the check-condition. * @param {Array<String>} flags a flag is to put on the parent node to * opertimize the next travelsal's efficiency. * @param {Array<Function>} checks condition funtions to return true or false. * @returns {Array<Boolean>} returns a array with boolean values. */ function checkParents (el, flags, checks) { let marks const len = checks.length const results = [] let cnts = 0 // count for results that already checked out. if (!Array.isArray(flags)) { flags = [flags] } if (!Array.isArray(checks)) { checks = [checks] } marks = flags.map((flag) => `_${flag}`) let parent = el.parent while (parent) { if (cnts === len) { return results } checks.forEach((check, idx) => { if (typeof results[idx] === 'boolean') { return } const mark = marks[idx] let res if (check(parent) || parent[mark] === true) { res = true } else if (parent[mark] === false) { res = false } else { return } results[idx] = res el[mark] = res cnts++ }) parent = parent.parent } for (let i = 0; i < len; i++) { if (typeof results[i] !== 'boolean') { results[i] = false el[marks[i]] = false cnts++ if (cnts === len) { return results } } } return results } function checkBubble (el) { return checkParents( el, ['hasBubbleParent'], [function (el) { return !!el.attrsMap.bubble || !!el._bubble }] )[0] } /** * bind events and nativeEvents: * - with appear events: weex-appear, data-evt-xxx * - with all weex events: stop propagation by default. * - with click and scroll events: weex$tap and weex$scroll. */ function bindEvents(evts, el, attrs, weexEvents, appearAttached) { const evtKeys = Object.keys(evts) for (let i = 0, l = evtKeys.length; i < l; i++) { const key = evtKeys[i] const transKey = transEvtsMap[key] const evtName = transKey || key if (transKey && !appearAttached.value) { appearAttached.value = true attrs.push({ name: `weex-appear`, value: '""' }) } attrs.push({ name: `data-evt-${evtName}`, value: '""' }) } const hasBubbleParent = checkBubble(el) /** * stop propagation by default unless attr 'bubble' is set to true. * only for weex events, user defined events should not be stopped * by default. */ // add by wuhl55 如果是同层渲染组件,则不参与事件冒泡等,不添加stop修饰符 if (!hasBubbleParent && !el._sameLayerComponent && !el._userRegistered) { for (const k in evts) { if (weexEvents.indexOf(k) > -1) { const evt = evts[k] const modifiers = evt.modifiers || (evt.modifiers = {}) modifiers.stop = true } } } /** * map event handlers. * - click -> weex$tap * - scroll -> weex$scroll */ if (evts.click && !el._sameLayerComponent && !el._userRegistered) { evts['weex$tap'] = extend({}, evts.click) delete evts.click if (!hasBubbleParent) { evts.click = { value: '$stopOuterA' } } } if (evts.scroll && !el._sameLayerComponent && !el._userRegistered) { evts['weex$scroll'] = evts.scroll delete evts.scroll } } module.exports = function eventsHook ( el, attrsMap, attrsList, attrs, staticClass ) { // bind _bubble attr for every node. const bubble = attrsMap.bubble if (bubble === 'true' || bubble === true) { el._bubble = true } const { weexEvents } = this.config let evts = el.events let nativeEvts = el.nativeEvents if (nativeEvts && el._builtIn) { /** * for div, image, text, cell, a, ... * user should bind all events without .native. */ nativeEvts = null delete el.nativeEvents } if (el._weexRegistered) { /** * for slider, list, ... * user should bind events without .native. * in our events handling, all events should transfer to * .native binding. */ delete el.nativeEvents nativeEvts = null if (evts) { nativeEvts = el.nativeEvents = evts } evts = null delete el.events } const appearAttached = { value: false } if (evts) { bindEvents(evts, el, attrs, weexEvents, appearAttached) } if (nativeEvts) { bindEvents(nativeEvts, el, attrs, weexEvents, appearAttached) } else { delete el.nativeEvents } /** * binding a weex$tap to <a> element to stop propagation if there * is no bubbles=true flag showing on parents. */ if (el.tag === 'a') { if (!evts) { evts = el.events = {} } if (!checkBubble(el)) { const evt = evts['weex$tap'] const val = evt && evt.value const inject = '$stopPropagation' if (!val) { evts['weex$tap'] = { value: inject } } else if (Array.isArray(val)) { evt.value.unshift(inject) } else if (typeof val === 'string') { const handlerCode = simplePathRE.test(val) // is method path ? `${val}($event)` : fnExpRE.test(val) // is function expression ? `(${val})($event)` : val evt.value = `function($event){${inject}(); return ${handlerCode}}` } } } }