@peter.naydenov/shortcuts 2.2.0 → 3.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,74 @@
1
+ 'use strict'
2
+
3
+ import _findTarget from "./_findTarget"
4
+ import _listenDOM from "./_listenDOM"
5
+ import _normalizeShortcutName from "./_normalizeShortcutName"
6
+ import _readClickEvent from "./_readClickEvent"
7
+ import _registerShortcutEvents from "./_registerShortcutEvents"
8
+
9
+
10
+
11
+
12
+ function pluginClick ( dependencies, state, options ) {
13
+ let
14
+ { currentContext, shortcuts } = state
15
+ , { inAPI } = dependencies
16
+ , deps = {
17
+ ev: dependencies.ev
18
+ , _findTarget
19
+ , _readClickEvent
20
+ , mainDependencies : dependencies
21
+ , regex : /CLICK\s*\:/i
22
+ }
23
+ , pluginState = {
24
+ currentContext
25
+ , shortcuts
26
+ , listenOptions : {
27
+ mouseWait : options.mouseWait ? options.mouseWait : 320 // 320 ms
28
+ , maxClicks : 1 // How many clicks can be pressed in a sequence. Controlled automatically by '_registerShortcutEvents' function.
29
+ , clickTarget : options.clickTarget ? options.clickTarget : 'click' // Data-attribute name for click target ( data-click )
30
+ }
31
+ } // pluginState
32
+ ;
33
+
34
+ // Read shortcuts names from all context entities and normalize entries related to the plugin
35
+ inAPI._normalizeWithPlugins ( _normalizeShortcutName )
36
+
37
+ let
38
+ mouseListener = _listenDOM ( deps, pluginState )
39
+ , countShortcuts = _registerShortcutEvents ( deps, pluginState )
40
+ ;
41
+
42
+ if ( countShortcuts > 0 ) mouseListener.start ()
43
+
44
+ const pluginAPI = {
45
+ getPrefix : () => 'click'
46
+ , shortcutName : key => { // Format a key string according plugin needs
47
+ return _normalizeShortcutName ( key )
48
+ }
49
+ , contextChange : () => {
50
+ countShortcuts = _registerShortcutEvents ( deps, pluginState )
51
+ if ( countShortcuts < 1 ) { // Remove DOM listener if there are no shortcuts in the current context
52
+ mouseListener.stop ()
53
+ }
54
+ if ( countShortcuts > 0 ) { // Add DOM listener if there are shortcuts in the current context
55
+ mouseListener.start ()
56
+ }
57
+ }
58
+ , mute : () => mouseListener.stop ()
59
+ , unmute : () => mouseListener.start ()
60
+ , destroy : () => {
61
+ mouseListener.stop ()
62
+ pluginState = null
63
+ pluginAPI = null
64
+ }
65
+ }; // pluginAPI
66
+ Object.freeze ( pluginAPI )
67
+ return pluginAPI
68
+ } // pluginClick func.
69
+
70
+
71
+
72
+ export default pluginClick
73
+
74
+
@@ -0,0 +1,138 @@
1
+ 'use strict'
2
+
3
+
4
+
5
+ function _listenDOM ( dependencies, state ) {
6
+ // Listen for input signals and generate event titles
7
+ const {
8
+ ev
9
+ , _specialChars
10
+ , _readKeyEvent
11
+ , mainDependencies
12
+ } = dependencies
13
+ , {
14
+ currentContext
15
+ , streamKeys
16
+ , listenOptions
17
+ } = state
18
+ , {
19
+ keyWait
20
+ } = listenOptions
21
+ ;
22
+
23
+ let
24
+ r = []
25
+ , keyTimer = null // Timer for key sequence or null
26
+ , sequence = true
27
+ , ignore = false // Use to trigger a single callback without adding the key to the sequence.
28
+ ;
29
+
30
+
31
+
32
+ const
33
+ waitKeys = () => sequence = false
34
+ , endKeys = () => sequence = true
35
+ , ignoreKeys = () => ignore = true
36
+ , waitingKeys = () => sequence === false
37
+ ;
38
+
39
+
40
+
41
+ function keySequenceEnd () { // Execute when key sequence ends
42
+ let res = r.map ( x => ([x.join('+')]) )
43
+ const data = {
44
+ wait: waitKeys
45
+ , end:endKeys
46
+ , ignore:ignoreKeys
47
+ , isWaiting:waitingKeys
48
+ , note: currentContext.note
49
+ , context: currentContext.name
50
+ , dependencies : mainDependencies.extra
51
+ , type : 'key'
52
+ };
53
+ if ( !sequence ) {
54
+ let signal = res.at(-1);
55
+ ev.emit ( signal, data )
56
+ if ( ignore ) {
57
+ res = res.slice ( 0, -1 )
58
+ ignore = false
59
+ }
60
+ }
61
+
62
+ if ( sequence ) {
63
+ const signal = `KEY:${res.join(',')}`
64
+ ev.emit ( signal, data )
65
+ // Reset:
66
+ r = []
67
+ keyTimer = null
68
+ }
69
+ } // keySequeceEnd func.
70
+
71
+
72
+
73
+ function listenForSpecialKeys ( event ) { // Listen for special keyboard keys
74
+ clearTimeout ( keyTimer )
75
+ if ( _specialChars.hasOwnProperty(event.code) ) r.push ( _readKeyEvent ( event, _specialChars ))
76
+ else return
77
+ if ( streamKeys ) streamKeys ({ key:event.key, context:currentContext.name, note:currentContext.note, dependencies:dependencies.extra })
78
+ if ( listenOptions.keyIgnore ) {
79
+ clearTimeout ( listenOptions.keyIgnore )
80
+ listenOptions.keyIgnore = setTimeout ( () => listenOptions.keyIgnore=null, keyWait )
81
+ return
82
+ }
83
+ if ( sequence && r.length === listenOptions.maxSequence ) {
84
+ keySequenceEnd ()
85
+ listenOptions.keyIgnore = setTimeout ( () => listenOptions.keyIgnore=null, keyWait )
86
+ return
87
+ }
88
+ if ( sequence ) keyTimer = setTimeout ( keySequenceEnd, keyWait )
89
+ else keySequenceEnd ()
90
+ } // listenForSpecialKeys func.
91
+
92
+
93
+
94
+ function listenForRegularKeys ( event ) { // Listen for regular keyboard keys
95
+ if ( _specialChars.hasOwnProperty(event.code) ) return
96
+ clearTimeout ( keyTimer )
97
+ if ( streamKeys ) streamKeys ({ key:event.key, context:currentContext.name, note:currentContext.note, dependencies:dependencies.extra })
98
+ if ( listenOptions.keyIgnore ) {
99
+ clearTimeout ( listenOptions.keyIgnore )
100
+ listenOptions.keyIgnore = setTimeout ( () => listenOptions.keyIgnore=null, keyWait )
101
+ return
102
+ }
103
+ r.push ( _readKeyEvent ( event, _specialChars ))
104
+ if ( sequence && r.length === listenOptions.maxSequence ) {
105
+ keySequenceEnd ()
106
+ listenOptions.keyIgnore = setTimeout ( () => listenOptions.keyIgnore=null, keyWait )
107
+ return
108
+ }
109
+ if ( sequence ) keyTimer = setTimeout ( keySequenceEnd, keyWait )
110
+ else keySequenceEnd ()
111
+ } // listenForRegularKeys func.
112
+
113
+
114
+
115
+ function start () {
116
+ if ( state.active ) return
117
+ document.addEventListener ( 'keydown' , listenForSpecialKeys )
118
+ document.addEventListener ( 'keypress', listenForRegularKeys )
119
+ state.active = true
120
+ }
121
+
122
+
123
+ function stop () {
124
+ if ( !state.active ) return
125
+ document.removeEventListener ( 'keydown' , listenForSpecialKeys )
126
+ document.removeEventListener ( 'keypress', listenForRegularKeys )
127
+ state.active = false
128
+ }
129
+
130
+ return { start, stop }
131
+
132
+ } // _listenDOM func.
133
+
134
+
135
+
136
+ export default _listenDOM
137
+
138
+
@@ -0,0 +1,31 @@
1
+ 'use strict'
2
+
3
+ function _normalizeShortcutName ( name ) {
4
+ const
5
+ upperCase = name.toUpperCase ()
6
+ , regex = /KEY\s*\:/i
7
+ , isKeyboardShortcut = regex.test ( upperCase )
8
+ , sliceIndex = upperCase.indexOf ( ':' )
9
+ ;
10
+
11
+ if ( !isKeyboardShortcut ) return name
12
+ let shortcut = upperCase
13
+ .slice(sliceIndex+1)
14
+ .split(',')
15
+ .map ( key => key.trim() )
16
+ .map ( key => {
17
+ return key
18
+ .split ( '+' )
19
+ .map ( key => key.trim() )
20
+ .sort()
21
+ .join ( '+' )
22
+ })
23
+ .join(',');
24
+ return `KEY:${shortcut}`
25
+ } // _normalizeShortcutName func.
26
+
27
+
28
+
29
+ export default _normalizeShortcutName
30
+
31
+
@@ -1,7 +1,6 @@
1
1
  'use strict'
2
2
 
3
- function _readKeyEvent () {
4
- return function _readKeyEvent ( event, _specialChars ) {
3
+ function _readKeyEvent ( event, _specialChars ) {
5
4
  let
6
5
  { shiftKey, altKey, ctrlKey } = event
7
6
  , falseKeys = [ 'ControlLeft','ControlRight', 'ShiftLeft', 'ShiftRight', 'AltLeft', 'AltRight', 'Meta' ]
@@ -18,7 +17,7 @@ return function _readKeyEvent ( event, _specialChars ) {
18
17
  if ( _specialChars.hasOwnProperty ( key ) ) res.push ( _specialChars[key].toUpperCase () )
19
18
  else if ( !falseKeys.includes(key) ) res.push ( key.toUpperCase () )
20
19
  return res.sort ()
21
- }} // _readKeyEvent func.
20
+ } // _readKeyEvent func.
22
21
 
23
22
 
24
23
 
@@ -0,0 +1,28 @@
1
+ 'use strict'
2
+
3
+ function _registerShortcutEvents ( dependencies, pluginState ) {
4
+ let count = 0;
5
+ const
6
+ { regex } = dependencies
7
+ , {
8
+ listenOptions
9
+ , currentContext : { name: contextName }
10
+ , shortcuts
11
+ } = pluginState
12
+ ;
13
+ if ( contextName == null ) return 0
14
+ Object.entries ( shortcuts[contextName] ).forEach ( ([shortcutName, list ]) => { // Enable new context shortcuts and set a listenOptions 'maxSequence'
15
+ let isKeyboardEv = regex.test ( shortcutName );
16
+ if ( !isKeyboardEv ) return
17
+ count++
18
+ let sequenceArraySize = shortcutName.slice(4).split(',').length;
19
+ if ( listenOptions.maxSequence < sequenceArraySize ) listenOptions.maxSequence = sequenceArraySize
20
+ })
21
+ return count
22
+ } // _registerShortcutEvents func.
23
+
24
+
25
+
26
+ export default _registerShortcutEvents
27
+
28
+
@@ -0,0 +1,76 @@
1
+ 'use strict'
2
+
3
+ // import all plugin files here
4
+ import _listenDOM from './_listenDOM.js'
5
+ import _normalizeShortcutName from './_normalizeShortcutName.js'
6
+ import _readKeyEvent from './_readKeyEvent.js'
7
+ import _registerShortcutEvents from './_registerShortcutEvents.js'
8
+ import _specialChars from './_specialChars.js'
9
+
10
+
11
+
12
+ function pluginKey ( dependencies, state, options={} ) {
13
+ let
14
+ { currentContext, shortcuts, exposeShortcut } = state
15
+ , { inAPI } = dependencies
16
+ , deps = {
17
+ ev: dependencies.ev
18
+ , _specialChars
19
+ , _readKeyEvent
20
+ , mainDependencies : dependencies
21
+ , regex : /KEY\s*\:/i
22
+ }
23
+ , pluginState = {
24
+ currentContext
25
+ , shortcuts
26
+ , active : false
27
+ , listenOptions : {
28
+ keyWait : options.keyWait ? options.keyWait : 480 // 480 ms
29
+ , maxSequence : 1 // How many keys can be pressed in a sequence. Controlled automatically by 'changeContext' function.
30
+ , keyIgnore : null // Timer for ignoring key presses after max sequence or null. Not a public option.
31
+ }
32
+ , streamKeys : (options.streamKeys && ( typeof options.streamKeys === 'function')) ? options.streamKeys : false // Keyboard stream function
33
+ , exposeShortcut
34
+ }; // state
35
+
36
+ // Read shortcuts names from all context entities and normalize entries related to the plugin
37
+ inAPI._normalizeWithPlugins ( _normalizeShortcutName )
38
+
39
+ let
40
+ keysListener = _listenDOM ( deps, pluginState )
41
+ , countShortcuts = _registerShortcutEvents ( deps, pluginState )
42
+ ;
43
+
44
+ if ( countShortcuts > 0 ) keysListener.start ()
45
+
46
+ const pluginAPI = {
47
+ getPrefix : () => 'key'
48
+ , shortcutName : key => { // Format a key string according plugin needs
49
+ return _normalizeShortcutName ( key )
50
+ }
51
+ , contextChange : contextName => {
52
+ countShortcuts = _registerShortcutEvents ( deps, pluginState )
53
+ if ( countShortcuts < 1 ) { // Remove DOM listener if there are no shortcuts in the current context
54
+ keysListener.stop ()
55
+ }
56
+ if ( countShortcuts > 0 ) { // Add DOM listener if there are shortcuts in the current context
57
+ keysListener.start ()
58
+ }
59
+ }
60
+ , mute : () => keysListener.stop ()
61
+ , unmute : () => keysListener.start ()
62
+ , destroy : () => {
63
+ keysListener.stop ()
64
+ pluginState = null
65
+ pluginAPI = null
66
+ }
67
+ };
68
+ Object.freeze ( pluginAPI )
69
+ return pluginAPI
70
+ } // pluginKey func.
71
+
72
+
73
+
74
+ export default pluginKey
75
+
76
+