@@ -23,6 +23,88 @@ const VALID_MODIFIERS = new Set([
2323const VERB_MODIFIERS = new Set ( [
2424 'stop' , 'prevent'
2525] )
26+ // https://www.w3.org/TR/uievents-key/
27+ const KEY_ALIASES = new Set ( [
28+ 'unidentified' , 'alt' , 'alt-graph' , 'caps-lock' , 'control' , 'fn' , 'fn-lock' ,
29+ 'meta' , 'num-lock' , 'scroll-lock' , 'shift' , 'symbol' , 'symbol-lock' , 'hyper' ,
30+ 'super' , 'enter' , 'tab' , 'arrow-down' , 'arrow-left' , 'arrow-right' ,
31+ 'arrow-up' , 'end' , 'home' , 'page-down' , 'page-up' , 'backspace' , 'clear' ,
32+ 'copy' , 'cr-sel' , 'cut' , 'delete' , 'erase-eof' , 'ex-sel' , 'insert' , 'paste' ,
33+ 'redo' , 'undo' , 'accept' , 'again' , 'attn' , 'cancel' , 'context-menu' , 'escape' ,
34+ 'execute' , 'find' , 'help' , 'pause' , 'select' , 'zoom-in' , 'zoom-out' ,
35+ 'brightness-down' , 'brightness-up' , 'eject' , 'log-off' , 'power' ,
36+ 'print-screen' , 'hibernate' , 'standby' , 'wake-up' , 'all-candidates' ,
37+ 'alphanumeric' , 'code-input' , 'compose' , 'convert' , 'dead' , 'final-mode' ,
38+ 'group-first' , 'group-last' , 'group-next' , 'group-previous' , 'mode-change' ,
39+ 'next-candidate' , 'non-convert' , 'previous-candidate' , 'process' ,
40+ 'single-candidate' , 'hangul-mode' , 'hanja-mode' , 'junja-mode' , 'eisu' ,
41+ 'hankaku' , 'hiragana' , 'hiragana-katakana' , 'kana-mode' , 'kanji-mode' ,
42+ 'katakana' , 'romaji' , 'zenkaku' , 'zenkaku-hankaku' , 'f1' , 'f2' , 'f3' , 'f4' ,
43+ 'f5' , 'f6' , 'f7' , 'f8' , 'f9' , 'f10' , 'f11' , 'f12' , 'soft1' , 'soft2' , 'soft3' ,
44+ 'soft4' , 'channel-down' , 'channel-up' , 'close' , 'mail-forward' , 'mail-reply' ,
45+ 'mail-send' , 'media-close' , 'media-fast-forward' , 'media-pause' ,
46+ 'media-play-pause' , 'media-record' , 'media-rewind' , 'media-stop' ,
47+ 'media-track-next' , 'media-track-previous' , 'new' , 'open' , 'print' , 'save' ,
48+ 'spell-check' , 'key11' , 'key12' , 'audio-balance-left' , 'audio-balance-right' ,
49+ 'audio-bass-boost-down' , 'audio-bass-boost-toggle' , 'audio-bass-boost-up' ,
50+ 'audio-fader-front' , 'audio-fader-rear' , 'audio-surround-mode-next' ,
51+ 'audio-treble-down' , 'audio-treble-up' , 'audio-volume-down' ,
52+ 'audio-volume-up' , 'audio-volume-mute' , 'microphone-toggle' ,
53+ 'microphone-volume-down' , 'microphone-volume-up' , 'microphone-volume-mute' ,
54+ 'speech-correction-list' , 'speech-input-toggle' , 'launch-application1' ,
55+ 'launch-application2' , 'launch-calendar' , 'launch-contacts' , 'launch-mail' ,
56+ 'launch-media-player' , 'launch-music-player' , 'launch-phone' ,
57+ 'launch-screen-saver' , 'launch-spreadsheet' , 'launch-web-browser' ,
58+ 'launch-web-cam' , 'launch-word-processor' , 'browser-back' ,
59+ 'browser-favorites' , 'browser-forward' , 'browser-home' , 'browser-refresh' ,
60+ 'browser-search' , 'browser-stop' , 'app-switch' , 'call' , 'camera' ,
61+ 'camera-focus' , 'end-call' , 'go-back' , 'go-home' , 'headset-hook' ,
62+ 'last-number-redial' , 'notification' , 'manner-mode' , 'voice-dial' , 't-v' ,
63+ 't-v3-d-mode' , 't-v-antenna-cable' , 't-v-audio-description' ,
64+ 't-v-audio-description-mix-down' , 't-v-audio-description-mix-up' ,
65+ 't-v-contents-menu' , 't-v-data-service' , 't-v-input' , 't-v-input-component1' ,
66+ 't-v-input-component2' , 't-v-input-composite1' , 't-v-input-composite2' ,
67+ 't-v-input-h-d-m-i1' , 't-v-input-h-d-m-i2' , 't-v-input-h-d-m-i3' ,
68+ 't-v-input-h-d-m-i4' , 't-v-input-v-g-a1' , 't-v-media-context' , 't-v-network' ,
69+ 't-v-number-entry' , 't-v-power' , 't-v-radio-service' , 't-v-satellite' ,
70+ 't-v-satellite-b-s' , 't-v-satellite-c-s' , 't-v-satellite-toggle' ,
71+ 't-v-terrestrial-analog' , 't-v-terrestrial-digital' , 't-v-timer' ,
72+ 'a-v-r-input' , 'a-v-r-power' , 'color-f0-red' , 'color-f1-green' ,
73+ 'color-f2-yellow' , 'color-f3-blue' , 'color-f4-grey' , 'color-f5-brown' ,
74+ 'closed-caption-toggle' , 'dimmer' , 'display-swap' , 'd-v-r' , 'exit' ,
75+ 'favorite-clear0' , 'favorite-clear1' , 'favorite-clear2' , 'favorite-clear3' ,
76+ 'favorite-recall0' , 'favorite-recall1' , 'favorite-recall2' ,
77+ 'favorite-recall3' , 'favorite-store0' , 'favorite-store1' , 'favorite-store2' ,
78+ 'favorite-store3' , 'guide' , 'guide-next-day' , 'guide-previous-day' , 'info' ,
79+ 'instant-replay' , 'link' , 'list-program' , 'live-content' , 'lock' ,
80+ 'media-apps' , 'media-last' , 'media-skip-backward' , 'media-skip-forward' ,
81+ 'media-step-backward' , 'media-step-forward' , 'media-top-menu' , 'navigate-in' ,
82+ 'navigate-next' , 'navigate-out' , 'navigate-previous' , 'next-favorite-channel' ,
83+ 'next-user-profile' , 'on-demand' , 'pairing' , 'pin-p-down' , 'pin-p-move' ,
84+ 'pin-p-toggle' , 'pin-p-up' , 'play-speed-down' , 'play-speed-reset' ,
85+ 'play-speed-up' , 'random-toggle' , 'rc-low-battery' , 'record-speed-next' ,
86+ 'rf-bypass' , 'scan-channels-toggle' , 'screen-mode-next' , 'settings' ,
87+ 'split-screen-toggle' , 's-t-b-input' , 's-t-b-power' , 'subtitle' , 'teletext' ,
88+ 'video-mode-next' , 'wink' , 'zoom-toggle' , 'audio-volume-down' ,
89+ 'audio-volume-up' , 'audio-volume-mute' , 'browser-back' , 'browser-forward' ,
90+ 'channel-down' , 'channel-up' , 'context-menu' , 'eject' , 'end' , 'enter' , 'home' ,
91+ 'media-fast-forward' , 'media-play' , 'media-play-pause' , 'media-record' ,
92+ 'media-rewind' , 'media-stop' , 'media-next-track' , 'media-pause' ,
93+ 'media-previous-track' , 'power' , 'unidentified'
94+ ] )
95+
96+ function isValidModifier ( modifier ) {
97+ return (
98+ // built-in aliases
99+ VALID_MODIFIERS . has ( modifier ) ||
100+ // keyCode
101+ Number . isInteger ( parseInt ( modifier , 10 ) ) ||
102+ // keyAlias (an Unicode character)
103+ Array . from ( modifier ) . length === 1 ||
104+ // keyAlias (special keys)
105+ KEY_ALIASES . has ( modifier )
106+ )
107+ }
26108
27109// ------------------------------------------------------------------------------
28110// Rule Definition
@@ -43,7 +125,7 @@ module.exports = {
43125 return utils . defineTemplateBodyVisitor ( context , {
44126 "VAttribute[directive=true][key.name='on']" ( node ) {
45127 for ( const modifier of node . key . modifiers ) {
46- if ( ! VALID_MODIFIERS . has ( modifier ) && ! Number . isInteger ( parseInt ( modifier , 10 ) ) ) {
128+ if ( ! isValidModifier ( modifier ) ) {
47129 context . report ( {
48130 node,
49131 loc : node . loc ,
0 commit comments