@@ -23,6 +23,88 @@ const VALID_MODIFIERS = new Set([
23
23
const VERB_MODIFIERS = new Set ( [
24
24
'stop' , 'prevent'
25
25
] )
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
+ }
26
108
27
109
// ------------------------------------------------------------------------------
28
110
// Rule Definition
@@ -43,7 +125,7 @@ module.exports = {
43
125
return utils . defineTemplateBodyVisitor ( context , {
44
126
"VAttribute[directive=true][key.name='on']" ( node ) {
45
127
for ( const modifier of node . key . modifiers ) {
46
- if ( ! VALID_MODIFIERS . has ( modifier ) && ! Number . isInteger ( parseInt ( modifier , 10 ) ) ) {
128
+ if ( ! isValidModifier ( modifier ) ) {
47
129
context . report ( {
48
130
node,
49
131
loc : node . loc ,
0 commit comments