|
| 1 | +/* @flow */ |
| 2 | + |
| 3 | +const fnExpRE = /^\s*([\w$_]+|\([^)]*?\))\s*=>|^function\s*\(/ |
| 4 | +const simplePathRE = /^\s*[A-Za-z_$][\w$]*(?:\.[A-Za-z_$][\w$]*|\['.*?']|\[".*?"]|\[\d+]|\[[A-Za-z_$][\w$]*])*\s*$/ |
| 5 | + |
| 6 | +// keyCode aliases |
| 7 | +const keyCodes: { [key: string]: number | Array<number> } = { |
| 8 | + esc: 27, |
| 9 | + tab: 9, |
| 10 | + enter: 13, |
| 11 | + space: 32, |
| 12 | + up: 38, |
| 13 | + left: 37, |
| 14 | + right: 39, |
| 15 | + down: 40, |
| 16 | + 'delete': [8, 46] |
| 17 | +} |
| 18 | + |
| 19 | +// #4868: modifiers that prevent the execution of the listener |
| 20 | +// need to explicitly return null so that we can determine whether to remove |
| 21 | +// the listener for .once |
| 22 | +const genGuard = condition => `if(${condition})return null;` |
| 23 | + |
| 24 | +const modifierCode: { [key: string]: string } = { |
| 25 | + stop: '$event.stopPropagation();', |
| 26 | + prevent: '$event.preventDefault();', |
| 27 | + self: genGuard(`$event.target !== $event.currentTarget`), |
| 28 | + ctrl: genGuard(`!$event.ctrlKey`), |
| 29 | + shift: genGuard(`!$event.shiftKey`), |
| 30 | + alt: genGuard(`!$event.altKey`), |
| 31 | + meta: genGuard(`!$event.metaKey`), |
| 32 | + left: genGuard(`'button' in $event && $event.button !== 0`), |
| 33 | + middle: genGuard(`'button' in $event && $event.button !== 1`), |
| 34 | + right: genGuard(`'button' in $event && $event.button !== 2`) |
| 35 | +} |
| 36 | + |
| 37 | +export function genHandlers ( |
| 38 | + events: ASTElementHandlers, |
| 39 | + native: boolean, |
| 40 | + warn: Function |
| 41 | +): string { |
| 42 | + let res = native ? 'nativeOn:{' : 'on:{' |
| 43 | + for (const name in events) { |
| 44 | + const handler = events[name] |
| 45 | + // #5330: warn click.right, since right clicks do not actually fire click events. |
| 46 | + if (process.env.NODE_ENV !== 'production' && |
| 47 | + name === 'click' && |
| 48 | + handler && handler.modifiers && handler.modifiers.right |
| 49 | + ) { |
| 50 | + warn( |
| 51 | + `Use "contextmenu" instead of "click.right" since right clicks ` + |
| 52 | + `do not actually fire "click" events.` |
| 53 | + ) |
| 54 | + } |
| 55 | + res += `"${name}":${genHandler(name, handler)},` |
| 56 | + } |
| 57 | + return res.slice(0, -1) + '}' |
| 58 | +} |
| 59 | +
|
| 60 | +function genHandler ( |
| 61 | + name: string, |
| 62 | + handler: ASTElementHandler | Array<ASTElementHandler> |
| 63 | +): string { |
| 64 | + if (!handler) { |
| 65 | + return 'function(){}' |
| 66 | + } |
| 67 | +
|
| 68 | + if (Array.isArray(handler)) { |
| 69 | + return `[${handler.map(handler => genHandler(name, handler)).join(',')}]` |
| 70 | + } |
| 71 | +
|
| 72 | + const isMethodPath = simplePathRE.test(handler.value) |
| 73 | + const isFunctionExpression = fnExpRE.test(handler.value) |
| 74 | +
|
| 75 | + if (!handler.modifiers) { |
| 76 | + return isMethodPath || isFunctionExpression |
| 77 | + ? handler.value |
| 78 | + : `function($event){${handler.value}}` // inline statement |
| 79 | + } else { |
| 80 | + let code = '' |
| 81 | + let genModifierCode = '' |
| 82 | + const keys = [] |
| 83 | + for (const key in handler.modifiers) { |
| 84 | + if (modifierCode[key]) { |
| 85 | + genModifierCode += modifierCode[key] |
| 86 | + // left/right |
| 87 | + if (keyCodes[key]) { |
| 88 | + keys.push(key) |
| 89 | + } |
| 90 | + } else { |
| 91 | + keys.push(key) |
| 92 | + } |
| 93 | + } |
| 94 | + if (keys.length) { |
| 95 | + code += genKeyFilter(keys) |
| 96 | + } |
| 97 | + // Make sure modifiers like prevent and stop get executed after key filtering |
| 98 | + if (genModifierCode) { |
| 99 | + code += genModifierCode |
| 100 | + } |
| 101 | + const handlerCode = isMethodPath |
| 102 | + ? handler.value + '($event)' |
| 103 | + : isFunctionExpression |
| 104 | + ? `(${handler.value})($event)` |
| 105 | + : handler.value |
| 106 | + return `function($event){${code}${handlerCode}}` |
| 107 | + } |
| 108 | +} |
| 109 | +
|
| 110 | +function genKeyFilter (keys: Array<string>): string { |
| 111 | + return `if(!('button' in $event)&&${keys.map(genFilterCode).join('&&')})return null;` |
| 112 | +} |
| 113 | +
|
| 114 | +function genFilterCode (key: string): string { |
| 115 | + const keyVal = parseInt(key, 10) |
| 116 | + if (keyVal) { |
| 117 | + return `$event.keyCode!==${keyVal}` |
| 118 | + } |
| 119 | + const alias = keyCodes[key] |
| 120 | + return `_k($event.keyCode,${JSON.stringify(key)}${alias ? ',' + JSON.stringify(alias) : ''})` |
| 121 | +} |
0 commit comments