@peter.naydenov/shortcuts 3.1.4 → 3.2.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.
package/Changelog.md CHANGED
@@ -1,6 +1,9 @@
1
1
  ## Release History
2
2
 
3
3
 
4
+ ### 3.2.0 ( 2025-08-15)
5
+ - [x] Plugin 'form' was added. It allows you to listen for form elements changes.
6
+
4
7
 
5
8
  ### 3.1.4 ( 2025-05-3)
6
9
  - [x] Dependency update. @peter.naydenov/notice - v.2.4.1
package/README.md CHANGED
@@ -8,11 +8,12 @@
8
8
 
9
9
 
10
10
  Describe all page activities as list of shortcuts wrapped in contexts. Switch among available contexts.
11
- Library has a plugin system to extend the possible `shortcut names`/`event coverage`. Plugins role is to convert DOM events to shortcut strings, then the core part will trigger the action functions related to the shortcut. At the moment we have 2 plugins:
11
+ Library has a plugin system to extend the possible `shortcut names`/`event coverage`. Plugins role is to convert DOM events to shortcut strings, then the core part will trigger the action functions related to the shortcut. At the moment we have 3 plugins:
12
12
  - `key` - converts keyboard events to shortcut names;
13
13
  - `click` - converts mouse events to shortcut names;
14
+ - `form` - watches for form elements changes;
14
15
 
15
- Planned work on the plugins for `scroll`, `drag-drop`, `input-change`, etc...
16
+ Planned work on the plugins for `scroll`, `drag-drop`, etc...
16
17
 
17
18
 
18
19
 
@@ -249,6 +250,67 @@ Special characters that are available for your shortcut descriptions:
249
250
 
250
251
 
251
252
 
253
+
254
+
255
+ ## Plugin 'form' Shortcut Descriptions
256
+ `Form` plugin is used to watch for changes in inputs, textareas, select and textarea elements. All 3 possible shortcuts are predefined: 'form: watch', 'form: define' and 'form: action'.
257
+ ```js
258
+ `form: watch` // (function). Should return list of elements to watch
259
+ `form: define` // (function). Define every element you are watching as a type(own definition).
260
+ `form: action` // (function). Function should return a list of objects with action function definitions.
261
+ ```
262
+ Action definitions have 4 possible properties:
263
+ ```js
264
+ {
265
+ fn // (function) Action function
266
+ , type // (string) Type of the element. Available types are according to `form: define` reponses
267
+ , timing // (string) Possible values are: 'in', 'out', 'instant'
268
+ , wait // (number) It's a event reducer timer in milliseconds. Worsk only for 'timing: instant'
269
+ }
270
+ ```
271
+
272
+ Definition Example:
273
+ ```js
274
+ const shortcutScope = {
275
+ ...
276
+ , 'form : watch' : () => 'input, button' // Will select all inputs and buttons elements on the page.
277
+ , 'form : define' : ( target ) => { // Target is a DOM element selected by 'form: watch'
278
+ if ( target.tagName === 'INPUT' ) { // Will define inputs as 'input' type
279
+ return 'input' // (String) Custom according your preference
280
+ }
281
+ if ( target.tagName === 'BUTTON' ) { // Will define buttons as 'button' type
282
+ return 'button'
283
+ }
284
+ }
285
+ , 'form : action' : () => [
286
+ {
287
+ fn: ({target}) => { console.log ( target)}
288
+ , type : 'input' // According to 'form: define'
289
+ , timing : 'in' // on focus in
290
+ },
291
+ {
292
+ fn: ({target}) => { console.log ( 'extra')}
293
+ , type : 'input'
294
+ , timing : 'in' // on focus in
295
+ },
296
+ {
297
+ fn: () => { console.log ( 'Update content') }
298
+ , type : 'input'
299
+ , timing : 'instant' // on content change. on each change
300
+ , wait : 500 // Wait 500ms between changes.
301
+ },
302
+ {
303
+ fn: () => { console.log('It was a button') }
304
+ , type : 'button' // According to 'form: define'
305
+ , timing : 'out' // on focus out
306
+ }
307
+ ] // form: action
308
+ }
309
+ ```
310
+ `form:watch` can contains `.someClass` for selecting elements by class name or `#someId` for selecting elements by id. It's could be everything that works in querySelectorAll. The `form:define` gives you a way to separate different inputs and privide a custom callback for each of them or use single callback for all inputs.
311
+
312
+
313
+
252
314
  ## Action Functions
253
315
  Action functions are called when a shortcut is triggered. There is a difference among plugin action functions. Arguments are slightly different.
254
316
 
@@ -1 +1 @@
1
- "use strict";var e=require("@peter.naydenov/notice");var t={_normalizeWithPlugins:function(e,t){return function(e){const n=t.shortcuts;Object.keys(n).forEach((t=>{Object.entries(n[t]).forEach((([o,r])=>{const i=e(o);i!==o&&(delete n[t][o],n[t][i]=r)}))}))}},_readShortcutWithPlugins:function(e,t){return function(n){const{inAPI:o}=e,r=n.split(":")[0],i=o._systemAction(r,"none");let s=n;return-1!==i&&(s=t.plugins[i].shortcutName(n)),s}},_systemAction:function(e,t){return function(e,n,o=null){return t.plugins.findIndex((t=>t.getPrefix()===e&&(t[n]&&t[n](o),!0)))}},changeContext:function(e,t){const{shortcuts:n,currentContext:o}=t,{ev:r}=e;return function(e=!1){const i=o.name;if(!e)return r.reset(),void(o.name=null);i!==e&&(n[e]?(n[i]&&r.reset(),o.name=e,t.plugins.forEach((t=>t.contextChange(e))),Object.entries(n[e]).forEach((([e,t])=>{t.forEach((t=>r.on(e,t)))})),r.on("*",((...e)=>{t.exposeShortcut&&t.exposeShortcut(...e)}))):r.emit("@shortcuts-error",`Context '${e}' does not exist`))}},listShortcuts:function(e,t){const n=t.shortcuts;return function(e=null){if(null!=e){let t=n[e];return null==t?null:Object.entries(t).map((([e,t])=>e))}return Object.keys(n).map((e=>{let t={};return t.context=e,t.shortcuts=Object.entries(n[e]).map((([e,t])=>e)),t}))}},load:function(e,t){const{shortcuts:n,plugins:o}=t,{API:{changeContext:r,getContext:i}}=e;return function(e){const t=i(),s=o.map((e=>e.getPrefix().toUpperCase()));let u=!1;Object.entries(e).forEach((([e,r])=>{e===t&&(u=!0),n[e]={},Object.entries(r).forEach((([t,r])=>{let i=t,u=t.toUpperCase().trim(),c=s.map(((e,t)=>u.startsWith(e)?t:null)).filter((e=>null!==e));if(c.length){let e=c[0];i=o[e].shortcutName(t)}r instanceof Function&&(r=[r]),n[e][i]=r}))})),u&&(r(),r(t))}},unload:function(e,t){const{currentContext:n,shortcuts:o}=t,{ev:r}=e;return function(e){n.name!==e?o[e]?delete o[e]:r.emit("shortcuts-error",`Context '${e}' does not exist`):r.emit("shortcuts-error",`Context '${e}' can't be removed during is current active context. Change the context first`)}}};function n(e){const t=e.toUpperCase(),n=/KEY\s*\:/i.test(t),o=t.indexOf(":");if(!n)return e;return`KEY:${t.slice(o+1).split(",").map((e=>e.trim())).map((e=>e.split("+").map((e=>e.trim())).sort().join("+"))).join(",")}`}function o(e,t){let{shiftKey:n,altKey:o,ctrlKey:r}=e,i=e.code.replace("Key","").replace("Digit",""),s=[];return r&&s.push("CTRL"),n&&s.push("SHIFT"),o&&s.push("ALT"),t.hasOwnProperty(i)?s.push(t[i].toUpperCase()):["ControlLeft","ControlRight","ShiftLeft","ShiftRight","AltLeft","AltRight","Meta"].includes(i)||s.push(i.toUpperCase()),s.sort()}function r(e,t){let n=0;const{regex:o}=e,{listenOptions:r,currentContext:{name:i},shortcuts:s}=t;return null==i?0:(Object.entries(s[i]).forEach((([e,t])=>{if(!o.test(e))return;n++;let i=e.slice(4).split(",").length;r.maxSequence<i&&(r.maxSequence=i)})),n)}function i(){return{ArrowLeft:"LEFT",ArrowUp:"UP",ArrowRight:"RIGHT",ArrowDown:"DOWN",Enter:"ENTER",NumpadEnter:"ENTER",Escape:"ESC",Backspace:"BACKSPACE",Space:"SPACE",Tab:"TAB",Backquote:"`",BracketLeft:"[",BracketRight:"]",Equal:"=",Slash:"/",Backslash:"\\",IntlBackslash:"`",F1:"F1",F2:"F2",F3:"F3",F4:"F4",F5:"F5",F6:"F6",F7:"F7",F8:"F8",F9:"F9",F10:"F10",F11:"F11",F12:"F12"}}function s(e,t,n){const{listenOptions:{clickTarget:o}}=t;let r=n;return r===document||r===document.body?null:r.dataset[o]||"A"===r.nodeName?r:s(e,t,r.parentNode)}function u(e){const t=e.toUpperCase(),n=/CLICK\s*\:/i.test(t),o=["LEFT","MIDDLE","RIGHT"],r=["ALT","SHIFT","CTRL"];let i=null,s=[],u=0,c=t.indexOf(":");return n?(t.slice(c+1).trim().split("-").map((e=>e.trim())).forEach((e=>{o.includes(e)?i=e:r.includes(e)?s.push(e):isNaN(e)||(u=e)})),`CLICK:${i}-${u}${s.length>0?"-":""}${s.sort().join("-")}`):e}function c(e,t){let{shiftKey:n,altKey:o,ctrlKey:r,key:i,button:s}=e,u=`CLICK:${["LEFT","MIDDLE","RIGHT"][s]}-${t}`,c=[];return r&&c.push("CTRL"),n&&c.push("SHIFT"),o&&c.push("ALT"),c.length>0?`${u}${c.length>0?"-":""}${c.sort().join("-")}`:`${u}`}function l(e,t){let n=0;const{regex:o}=e,{listenOptions:r,currentContext:{name:i},shortcuts:s}=t;return null==i?0:(Object.entries(s[i]).forEach((([e,t])=>{if(!o.test(e))return;n++;let[,i]=e.slice(6).split("-");r.maxClicks<i&&(r.maxClicks=i)})),n)}exports.pluginClick=function(e,t,n){let{currentContext:o,shortcuts:r}=t,{inAPI:i}=e,a={ev:e.ev,_findTarget:s,_readClickEvent:c,mainDependencies:e,regex:/CLICK\s*\:/i},m={currentContext:o,shortcuts:r,listenOptions:{mouseWait:n.mouseWait?n.mouseWait:320,maxClicks:1,clickTarget:n.clickTarget?n.clickTarget:"click"}};i._normalizeWithPlugins(u);let p=function(e,t){const{ev:n,_findTarget:o,_readClickEvent:r,mainDependencies:i}=e,{listenOptions:s,currentContext:u}=t,{mouseWait:c}=s;let l=null,a=null,m=null,p=null,h=0;function f(){const e=r(a,h),t={target:l,targetProps:l?l.getBoundingClientRect():null,x:a.clientX,y:a.clientY,context:u.name,note:u.note,event:a,dependencies:i.extra,type:"click"};n.emit(e,t),m=null,p=null,l=null,a=null,h=0}function d(n){let r=s.maxClicks;return clearTimeout(m),p?(clearTimeout(p),void(p=setTimeout((()=>p=null),c))):(l=o(e,t,n.target),l&&l.dataset.hasOwnProperty("quickClick")&&(r=1),l&&"A"===l.tagName&&(r=1),a=n,h++,h>=r?(f(),void(r>1&&(p=setTimeout((()=>p=null),c)))):void(m=setTimeout(f,c)))}function g(n){let r=s.maxClicks;return clearTimeout(m),p?(clearTimeout(p),void(p=setTimeout((()=>p=null),c))):(l=o(e,t,n.target),l&&l.dataset.hasOwnProperty("quickClick")&&(r=1),l&&"A"===l.tagName&&(r=1),a=n,h++,h>=r?(f(),void(r>1&&(p=setTimeout((()=>p=null),c)))):void(m=setTimeout(f,c)))}return{start:function(){t.active||(window.addEventListener("contextmenu",g),document.addEventListener("click",d),t.active=!0)},stop:function(){t.active&&(window.removeEventListener("contextmenu",g),document.removeEventListener("click",d),t.active=!1)}}}(a,m),h=l(a,m);h>0&&p.start();let f={getPrefix:()=>"click",shortcutName:e=>u(e),contextChange:()=>{h=l(a,m),h<1&&p.stop(),h>0&&p.start()},mute:()=>p.stop(),unmute:()=>p.start(),destroy:()=>{p.stop(),m=null,f=null}};return Object.freeze(f),f},exports.pluginKey=function(e,t,s={}){let{currentContext:u,shortcuts:c,exposeShortcut:l}=t,{inAPI:a}=e,m={ev:e.ev,_specialChars:i,_readKeyEvent:o,mainDependencies:e,regex:/KEY\s*\:/i},p={currentContext:u,shortcuts:c,active:!1,listenOptions:{keyWait:s.keyWait?s.keyWait:480,maxSequence:1,keyIgnore:null},streamKeys:!(!s.streamKeys||"function"!=typeof s.streamKeys)&&s.streamKeys,exposeShortcut:l};a._normalizeWithPlugins(n);let h=function(e,t){const{ev:n,_specialChars:o,_readKeyEvent:r,mainDependencies:i}=e,{currentContext:s,streamKeys:u,listenOptions:c}=t,{keyWait:l}=c;let a=[],m=null,p=!0,h=!1;const f=()=>p=!1,d=()=>p=!0,g=()=>h=!0,x=()=>!1===p;function y(){let e=a.map((e=>[e.join("+")]));const t={wait:f,end:d,ignore:g,isWaiting:x,note:s.note,context:s.name,dependencies:i.extra,type:"key"};if(!p){let o=e.at(-1);n.emit(o,t),h&&(e=e.slice(0,-1),h=!1)}if(p){const o=`KEY:${e.join(",")}`;n.emit(o,t),a=[],m=null}}function C(t){if(clearTimeout(m),o.hasOwnProperty(t.code))return a.push(r(t,o)),u&&u({key:t.key,context:s.name,note:s.note,dependencies:e.extra}),c.keyIgnore?(clearTimeout(c.keyIgnore),void(c.keyIgnore=setTimeout((()=>c.keyIgnore=null),l))):p&&a.length===c.maxSequence?(y(),void(c.keyIgnore=setTimeout((()=>c.keyIgnore=null),l))):void(p?m=setTimeout(y,l):y())}function k(t){if(!o.hasOwnProperty(t.code)){if(clearTimeout(m),u&&u({key:t.key,context:s.name,note:s.note,dependencies:e.extra}),c.keyIgnore)return clearTimeout(c.keyIgnore),void(c.keyIgnore=setTimeout((()=>c.keyIgnore=null),l));if(a.push(r(t,o)),p&&a.length===c.maxSequence)return y(),void(c.keyIgnore=setTimeout((()=>c.keyIgnore=null),l));p?m=setTimeout(y,l):y()}}return{start:function(){t.active||(document.addEventListener("keydown",C),document.addEventListener("keypress",k),t.active=!0)},stop:function(){t.active&&(document.removeEventListener("keydown",C),document.removeEventListener("keypress",k),t.active=!1)}}}(m,p),f=r(m,p);f>0&&h.start();let d={getPrefix:()=>"key",shortcutName:e=>n(e),contextChange:e=>{f=r(m,p),f<1&&h.stop(),f>0&&h.start()},mute:()=>h.stop(),unmute:()=>h.start(),destroy:()=>{h.stop(),p=null,d=null}};return Object.freeze(d),d},exports.shortcuts=function(n={}){const o=e(),r={},i={},s={currentContext:{name:null,note:null},shortcuts:{},plugins:[],exposeShortcut:!(!n.onShortcut||"function"!=typeof n.onShortcut)&&n.onShortcut},u={ev:o,inAPI:r,API:i,extra:{}};return i.enablePlugin=(e,t={})=>{const n=e.name;if(-1===r._systemAction(n,"none")){let n;n=e(u,s,t),s.plugins.push(n)}},i.disablePlugin=e=>{const t=r._systemAction(e,"destroy");-1!==t&&(s.plugins=s.plugins.filter(((e,n)=>n!==t)))},i.mutePlugin=e=>r._systemAction(e,"mute"),i.unmutePlugin=e=>r._systemAction(e,"unmute"),i.getContext=()=>s.currentContext.name,i.getNote=()=>s.currentContext.note,i.setNote=(e=null)=>{"string"!=typeof e&&null!=e||(s.currentContext.note=e)},i.pause=(e="*")=>{let t=r._readShortcutWithPlugins(e);o.stop(t)},i.resume=(e="*")=>{const t=r._readShortcutWithPlugins(e);o.start(t)},i.emit=(e,...t)=>o.emit(r._readShortcutWithPlugins(e),...t),i.listContexts=()=>Object.keys(s.shortcuts),i.setDependencies=e=>u.extra={...u.extra,...e},i.getDependencies=()=>u.extra,Object.entries(t).forEach((([e,t])=>{e.startsWith("_")?r[e]=t(u,s):i[e]=t(u,s)})),i};
1
+ "use strict";var e=require("@peter.naydenov/notice");var t={_normalizeWithPlugins:function(e,t){return function(e){const n=t.shortcuts;Object.keys(n).forEach((t=>{Object.entries(n[t]).forEach((([o,r])=>{const i=e(o);i!==o&&(delete n[t][o],n[t][i]=r)}))}))}},_readShortcutWithPlugins:function(e,t){return function(n){const{inAPI:o}=e,r=n.split(":")[0],i=o._systemAction(r,"none");let s=n;return-1!==i&&(s=t.plugins[i].shortcutName(n)),s}},_systemAction:function(e,t){return function(e,n,o=null){return t.plugins.findIndex((t=>t.getPrefix()===e&&(t[n]&&t[n](o),!0)))}},changeContext:function(e,t){const{shortcuts:n,currentContext:o}=t,{ev:r}=e;return function(e=!1){const i=o.name;if(!e)return r.reset(),void(o.name=null);i!==e&&(n[e]?(n[i]&&r.reset(),o.name=e,t.plugins.forEach((t=>t.contextChange(e))),Object.entries(n[e]).forEach((([e,t])=>{t.forEach((t=>r.on(e,t)))})),r.on("*",((...e)=>{t.exposeShortcut&&t.exposeShortcut(...e)}))):r.emit("@shortcuts-error",`Context '${e}' does not exist`))}},listShortcuts:function(e,t){const n=t.shortcuts;return function(e=null){if(null!=e){let t=n[e];return null==t?null:Object.entries(t).map((([e,t])=>e))}return Object.keys(n).map((e=>{let t={};return t.context=e,t.shortcuts=Object.entries(n[e]).map((([e,t])=>e)),t}))}},load:function(e,t){const{shortcuts:n,plugins:o}=t,{API:{changeContext:r,getContext:i}}=e;return function(e){const t=i(),s=o.map((e=>e.getPrefix().toUpperCase()));let u=!1;Object.entries(e).forEach((([e,r])=>{e===t&&(u=!0),n[e]={},Object.entries(r).forEach((([t,r])=>{let i=t,u=t.toUpperCase().trim(),c=s.map(((e,t)=>u.startsWith(e)?t:null)).filter((e=>null!==e));if(c.length){let e=c[0];i=o[e].shortcutName(t)}r instanceof Function&&(r=[r]),n[e][i]=r}))})),u&&(r(),r(t))}},unload:function(e,t){const{currentContext:n,shortcuts:o}=t,{ev:r}=e;return function(e){n.name!==e?o[e]?delete o[e]:r.emit("shortcuts-error",`Context '${e}' does not exist`):r.emit("shortcuts-error",`Context '${e}' can't be removed during is current active context. Change the context first`)}}};function n(e){const t=e.toUpperCase(),n=/KEY\s*\:/i.test(t),o=t.indexOf(":");if(!n)return e;return`KEY:${t.slice(o+1).split(",").map((e=>e.trim())).map((e=>e.split("+").map((e=>e.trim())).sort().join("+"))).join(",")}`}function o(e,t){let{shiftKey:n,altKey:o,ctrlKey:r}=e,i=e.code.replace("Key","").replace("Digit",""),s=[];return r&&s.push("CTRL"),n&&s.push("SHIFT"),o&&s.push("ALT"),t.hasOwnProperty(i)?s.push(t[i].toUpperCase()):["ControlLeft","ControlRight","ShiftLeft","ShiftRight","AltLeft","AltRight","Meta"].includes(i)||s.push(i.toUpperCase()),s.sort()}function r(e,t){let n=0;const{regex:o}=e,{listenOptions:r,currentContext:{name:i},shortcuts:s}=t;return null==i?0:(Object.entries(s[i]).forEach((([e,t])=>{if(!o.test(e))return;n++;let i=e.slice(4).split(",").length;r.maxSequence<i&&(r.maxSequence=i)})),n)}function i(){return{ArrowLeft:"LEFT",ArrowUp:"UP",ArrowRight:"RIGHT",ArrowDown:"DOWN",Enter:"ENTER",NumpadEnter:"ENTER",Escape:"ESC",Backspace:"BACKSPACE",Space:"SPACE",Tab:"TAB",Backquote:"`",BracketLeft:"[",BracketRight:"]",Equal:"=",Slash:"/",Backslash:"\\",IntlBackslash:"`",F1:"F1",F2:"F2",F3:"F3",F4:"F4",F5:"F5",F6:"F6",F7:"F7",F8:"F8",F9:"F9",F10:"F10",F11:"F11",F12:"F12"}}function s(e,t,n){const{listenOptions:{clickTarget:o}}=t;let r=n;return r===document||r===document.body?null:r.dataset[o]||"A"===r.nodeName?r:s(e,t,r.parentNode)}function u(e){const t=e.toUpperCase(),n=/CLICK\s*\:/i.test(t),o=["LEFT","MIDDLE","RIGHT"],r=["ALT","SHIFT","CTRL"];let i=null,s=[],u=0,c=t.indexOf(":");return n?(t.slice(c+1).trim().split("-").map((e=>e.trim())).forEach((e=>{o.includes(e)?i=e:r.includes(e)?s.push(e):isNaN(e)||(u=e)})),`CLICK:${i}-${u}${s.length>0?"-":""}${s.sort().join("-")}`):e}function c(e,t){let{shiftKey:n,altKey:o,ctrlKey:r,key:i,button:s}=e,u=`CLICK:${["LEFT","MIDDLE","RIGHT"][s]}-${t}`,c=[];return r&&c.push("CTRL"),n&&c.push("SHIFT"),o&&c.push("ALT"),c.length>0?`${u}${c.length>0?"-":""}${c.sort().join("-")}`:`${u}`}function l(e,t){let n=0;const{regex:o}=e,{listenOptions:r,currentContext:{name:i},shortcuts:s}=t;return null==i?0:(Object.entries(s[i]).forEach((([e,t])=>{if(!o.test(e))return;n++;let[,i]=e.slice(6).split("-");r.maxClicks<i&&(r.maxClicks=i)})),n)}exports.pluginClick=function(e,t,n){let{currentContext:o,shortcuts:r}=t,{inAPI:i}=e,a={ev:e.ev,_findTarget:s,_readClickEvent:c,mainDependencies:e,regex:/CLICK\s*\:/i},m={currentContext:o,shortcuts:r,listenOptions:{mouseWait:n.mouseWait?n.mouseWait:320,maxClicks:1,clickTarget:n.clickTarget?n.clickTarget:"click"}};i._normalizeWithPlugins(u);let p=function(e,t){const{ev:n,_findTarget:o,_readClickEvent:r,mainDependencies:i}=e,{listenOptions:s,currentContext:u}=t,{mouseWait:c}=s;let l=null,a=null,m=null,p=null,h=0;function f(){const e=r(a,h),t={target:l,targetProps:l?l.getBoundingClientRect():null,x:a.clientX,y:a.clientY,context:u.name,note:u.note,event:a,dependencies:i.extra,type:"click"};n.emit(e,t),m=null,p=null,l=null,a=null,h=0}function d(n){let r=s.maxClicks;return clearTimeout(m),p?(clearTimeout(p),void(p=setTimeout((()=>p=null),c))):(l=o(e,t,n.target),l&&l.dataset.hasOwnProperty("quickClick")&&(r=1),l&&"A"===l.tagName&&(r=1),a=n,h++,h>=r?(f(),void(r>1&&(p=setTimeout((()=>p=null),c)))):void(m=setTimeout(f,c)))}function g(n){let r=s.maxClicks;return clearTimeout(m),p?(clearTimeout(p),void(p=setTimeout((()=>p=null),c))):(l=o(e,t,n.target),l&&l.dataset.hasOwnProperty("quickClick")&&(r=1),l&&"A"===l.tagName&&(r=1),a=n,h++,h>=r?(f(),void(r>1&&(p=setTimeout((()=>p=null),c)))):void(m=setTimeout(f,c)))}return{start:function(){t.active||(window.addEventListener("contextmenu",g),document.addEventListener("click",d),t.active=!0)},stop:function(){t.active&&(window.removeEventListener("contextmenu",g),document.removeEventListener("click",d),t.active=!1)}}}(a,m),h=l(a,m);h>0&&p.start();let f={getPrefix:()=>"click",shortcutName:e=>u(e),contextChange:()=>{h=l(a,m),h<1&&p.stop(),h>0&&p.start()},mute:()=>p.stop(),unmute:()=>p.start(),destroy:()=>{p.stop(),m=null,f=null}};return Object.freeze(f),f},exports.pluginKey=function(e,t,s={}){let{currentContext:u,shortcuts:c,exposeShortcut:l}=t,{inAPI:a}=e,m={ev:e.ev,_specialChars:i,_readKeyEvent:o,mainDependencies:e,regex:/KEY\s*\:/i},p={currentContext:u,shortcuts:c,active:!1,listenOptions:{keyWait:s.keyWait?s.keyWait:480,maxSequence:1,keyIgnore:null},streamKeys:!(!s.streamKeys||"function"!=typeof s.streamKeys)&&s.streamKeys};a._normalizeWithPlugins(n);let h=function(e,t){const{ev:n,_specialChars:o,_readKeyEvent:r,mainDependencies:i}=e,{currentContext:s,streamKeys:u,listenOptions:c}=t,{keyWait:l}=c;let a=[],m=null,p=!0,h=!1;const f=()=>p=!1,d=()=>p=!0,g=()=>h=!0,x=()=>!1===p;function y(){let e=a.map((e=>[e.join("+")]));const t={wait:f,end:d,ignore:g,isWaiting:x,note:s.note,context:s.name,dependencies:i.extra,type:"key"};if(!p){let o=e.at(-1);n.emit(o,t),h&&(e=e.slice(0,-1),h=!1)}if(p){const o=`KEY:${e.join(",")}`;n.emit(o,t),a=[],m=null}}function C(t){if(clearTimeout(m),o.hasOwnProperty(t.code))return a.push(r(t,o)),u&&u({key:t.key,context:s.name,note:s.note,dependencies:e.extra}),c.keyIgnore?(clearTimeout(c.keyIgnore),void(c.keyIgnore=setTimeout((()=>c.keyIgnore=null),l))):p&&a.length===c.maxSequence?(y(),void(c.keyIgnore=setTimeout((()=>c.keyIgnore=null),l))):void(p?m=setTimeout(y,l):y())}function k(t){if(!o.hasOwnProperty(t.code)){if(clearTimeout(m),u&&u({key:t.key,context:s.name,note:s.note,dependencies:e.extra}),c.keyIgnore)return clearTimeout(c.keyIgnore),void(c.keyIgnore=setTimeout((()=>c.keyIgnore=null),l));if(a.push(r(t,o)),p&&a.length===c.maxSequence)return y(),void(c.keyIgnore=setTimeout((()=>c.keyIgnore=null),l));p?m=setTimeout(y,l):y()}}return{start:function(){t.active||(document.addEventListener("keydown",C),document.addEventListener("keypress",k),t.active=!0)},stop:function(){t.active&&(document.removeEventListener("keydown",C),document.removeEventListener("keypress",k),t.active=!1)}}}(m,p),f=r(m,p);f>0&&h.start();let d={getPrefix:()=>"key",shortcutName:e=>n(e),contextChange:e=>{f=r(m,p),f<1&&h.stop(),f>0&&h.start()},mute:()=>h.stop(),unmute:()=>h.start(),destroy:()=>{h.stop(),p=null,d=null}};return Object.freeze(d),d},exports.shortcuts=function(n={}){const o=e(),r={},i={},s={currentContext:{name:null,note:null},shortcuts:{},plugins:[],exposeShortcut:!(!n.onShortcut||"function"!=typeof n.onShortcut)&&n.onShortcut},u={ev:o,inAPI:r,API:i,extra:{}};return i.enablePlugin=(e,t={})=>{const n=e.name;if(-1===r._systemAction(n,"none")){let n;n=e(u,s,t),s.plugins.push(n)}},i.disablePlugin=e=>{const t=r._systemAction(e,"destroy");-1!==t&&(s.plugins=s.plugins.filter(((e,n)=>n!==t)))},i.mutePlugin=e=>r._systemAction(e,"mute"),i.unmutePlugin=e=>r._systemAction(e,"unmute"),i.getContext=()=>s.currentContext.name,i.getNote=()=>s.currentContext.note,i.setNote=(e=null)=>{"string"!=typeof e&&null!=e||(s.currentContext.note=e)},i.pause=(e="*")=>{let t=r._readShortcutWithPlugins(e);o.stop(t)},i.resume=(e="*")=>{const t=r._readShortcutWithPlugins(e);o.start(t)},i.emit=(e,...t)=>o.emit(r._readShortcutWithPlugins(e),...t),i.listContexts=()=>Object.keys(s.shortcuts),i.setDependencies=e=>u.extra={...u.extra,...e},i.getDependencies=()=>u.extra,Object.entries(t).forEach((([e,t])=>{e.startsWith("_")?r[e]=t(u,s):i[e]=t(u,s)})),i};
@@ -1 +1 @@
1
- import t from"@peter.naydenov/notice";var e={_normalizeWithPlugins:function(t,e){return function(t){const n=e.shortcuts;Object.keys(n).forEach((e=>{Object.entries(n[e]).forEach((([o,r])=>{const i=t(o);i!==o&&(delete n[e][o],n[e][i]=r)}))}))}},_readShortcutWithPlugins:function(t,e){return function(n){const{inAPI:o}=t,r=n.split(":")[0],i=o._systemAction(r,"none");let s=n;return-1!==i&&(s=e.plugins[i].shortcutName(n)),s}},_systemAction:function(t,e){return function(t,n,o=null){return e.plugins.findIndex((e=>e.getPrefix()===t&&(e[n]&&e[n](o),!0)))}},changeContext:function(t,e){const{shortcuts:n,currentContext:o}=e,{ev:r}=t;return function(t=!1){const i=o.name;if(!t)return r.reset(),void(o.name=null);i!==t&&(n[t]?(n[i]&&r.reset(),o.name=t,e.plugins.forEach((e=>e.contextChange(t))),Object.entries(n[t]).forEach((([t,e])=>{e.forEach((e=>r.on(t,e)))})),r.on("*",((...t)=>{e.exposeShortcut&&e.exposeShortcut(...t)}))):r.emit("@shortcuts-error",`Context '${t}' does not exist`))}},listShortcuts:function(t,e){const n=e.shortcuts;return function(t=null){if(null!=t){let e=n[t];return null==e?null:Object.entries(e).map((([t,e])=>t))}return Object.keys(n).map((t=>{let e={};return e.context=t,e.shortcuts=Object.entries(n[t]).map((([t,e])=>t)),e}))}},load:function(t,e){const{shortcuts:n,plugins:o}=e,{API:{changeContext:r,getContext:i}}=t;return function(t){const e=i(),s=o.map((t=>t.getPrefix().toUpperCase()));let c=!1;Object.entries(t).forEach((([t,r])=>{t===e&&(c=!0),n[t]={},Object.entries(r).forEach((([e,r])=>{let i=e,c=e.toUpperCase().trim(),u=s.map(((t,e)=>c.startsWith(t)?e:null)).filter((t=>null!==t));if(u.length){let t=u[0];i=o[t].shortcutName(e)}r instanceof Function&&(r=[r]),n[t][i]=r}))})),c&&(r(),r(e))}},unload:function(t,e){const{currentContext:n,shortcuts:o}=e,{ev:r}=t;return function(t){n.name!==t?o[t]?delete o[t]:r.emit("shortcuts-error",`Context '${t}' does not exist`):r.emit("shortcuts-error",`Context '${t}' can't be removed during is current active context. Change the context first`)}}};function n(t){const e=t.toUpperCase(),n=/KEY\s*\:/i.test(e),o=e.indexOf(":");if(!n)return t;return`KEY:${e.slice(o+1).split(",").map((t=>t.trim())).map((t=>t.split("+").map((t=>t.trim())).sort().join("+"))).join(",")}`}function o(t,e){let{shiftKey:n,altKey:o,ctrlKey:r}=t,i=t.code.replace("Key","").replace("Digit",""),s=[];return r&&s.push("CTRL"),n&&s.push("SHIFT"),o&&s.push("ALT"),e.hasOwnProperty(i)?s.push(e[i].toUpperCase()):["ControlLeft","ControlRight","ShiftLeft","ShiftRight","AltLeft","AltRight","Meta"].includes(i)||s.push(i.toUpperCase()),s.sort()}function r(t,e){let n=0;const{regex:o}=t,{listenOptions:r,currentContext:{name:i},shortcuts:s}=e;return null==i?0:(Object.entries(s[i]).forEach((([t,e])=>{if(!o.test(t))return;n++;let i=t.slice(4).split(",").length;r.maxSequence<i&&(r.maxSequence=i)})),n)}function i(){return{ArrowLeft:"LEFT",ArrowUp:"UP",ArrowRight:"RIGHT",ArrowDown:"DOWN",Enter:"ENTER",NumpadEnter:"ENTER",Escape:"ESC",Backspace:"BACKSPACE",Space:"SPACE",Tab:"TAB",Backquote:"`",BracketLeft:"[",BracketRight:"]",Equal:"=",Slash:"/",Backslash:"\\",IntlBackslash:"`",F1:"F1",F2:"F2",F3:"F3",F4:"F4",F5:"F5",F6:"F6",F7:"F7",F8:"F8",F9:"F9",F10:"F10",F11:"F11",F12:"F12"}}function s(t,e,s={}){let{currentContext:c,shortcuts:u,exposeShortcut:l}=e,{inAPI:a}=t,m={ev:t.ev,_specialChars:i,_readKeyEvent:o,mainDependencies:t,regex:/KEY\s*\:/i},p={currentContext:c,shortcuts:u,active:!1,listenOptions:{keyWait:s.keyWait?s.keyWait:480,maxSequence:1,keyIgnore:null},streamKeys:!(!s.streamKeys||"function"!=typeof s.streamKeys)&&s.streamKeys,exposeShortcut:l};a._normalizeWithPlugins(n);let h=function(t,e){const{ev:n,_specialChars:o,_readKeyEvent:r,mainDependencies:i}=t,{currentContext:s,streamKeys:c,listenOptions:u}=e,{keyWait:l}=u;let a=[],m=null,p=!0,h=!1;const f=()=>p=!1,d=()=>p=!0,g=()=>h=!0,x=()=>!1===p;function y(){let t=a.map((t=>[t.join("+")]));const e={wait:f,end:d,ignore:g,isWaiting:x,note:s.note,context:s.name,dependencies:i.extra,type:"key"};if(!p){let o=t.at(-1);n.emit(o,e),h&&(t=t.slice(0,-1),h=!1)}if(p){const o=`KEY:${t.join(",")}`;n.emit(o,e),a=[],m=null}}function C(e){if(clearTimeout(m),o.hasOwnProperty(e.code))return a.push(r(e,o)),c&&c({key:e.key,context:s.name,note:s.note,dependencies:t.extra}),u.keyIgnore?(clearTimeout(u.keyIgnore),void(u.keyIgnore=setTimeout((()=>u.keyIgnore=null),l))):p&&a.length===u.maxSequence?(y(),void(u.keyIgnore=setTimeout((()=>u.keyIgnore=null),l))):void(p?m=setTimeout(y,l):y())}function k(e){if(!o.hasOwnProperty(e.code)){if(clearTimeout(m),c&&c({key:e.key,context:s.name,note:s.note,dependencies:t.extra}),u.keyIgnore)return clearTimeout(u.keyIgnore),void(u.keyIgnore=setTimeout((()=>u.keyIgnore=null),l));if(a.push(r(e,o)),p&&a.length===u.maxSequence)return y(),void(u.keyIgnore=setTimeout((()=>u.keyIgnore=null),l));p?m=setTimeout(y,l):y()}}return{start:function(){e.active||(document.addEventListener("keydown",C),document.addEventListener("keypress",k),e.active=!0)},stop:function(){e.active&&(document.removeEventListener("keydown",C),document.removeEventListener("keypress",k),e.active=!1)}}}(m,p),f=r(m,p);f>0&&h.start();let d={getPrefix:()=>"key",shortcutName:t=>n(t),contextChange:t=>{f=r(m,p),f<1&&h.stop(),f>0&&h.start()},mute:()=>h.stop(),unmute:()=>h.start(),destroy:()=>{h.stop(),p=null,d=null}};return Object.freeze(d),d}function c(t,e,n){const{listenOptions:{clickTarget:o}}=e;let r=n;return r===document||r===document.body?null:r.dataset[o]||"A"===r.nodeName?r:c(t,e,r.parentNode)}function u(t){const e=t.toUpperCase(),n=/CLICK\s*\:/i.test(e),o=["LEFT","MIDDLE","RIGHT"],r=["ALT","SHIFT","CTRL"];let i=null,s=[],c=0,u=e.indexOf(":");return n?(e.slice(u+1).trim().split("-").map((t=>t.trim())).forEach((t=>{o.includes(t)?i=t:r.includes(t)?s.push(t):isNaN(t)||(c=t)})),`CLICK:${i}-${c}${s.length>0?"-":""}${s.sort().join("-")}`):t}function l(t,e){let{shiftKey:n,altKey:o,ctrlKey:r,key:i,button:s}=t,c=`CLICK:${["LEFT","MIDDLE","RIGHT"][s]}-${e}`,u=[];return r&&u.push("CTRL"),n&&u.push("SHIFT"),o&&u.push("ALT"),u.length>0?`${c}${u.length>0?"-":""}${u.sort().join("-")}`:`${c}`}function a(t,e){let n=0;const{regex:o}=t,{listenOptions:r,currentContext:{name:i},shortcuts:s}=e;return null==i?0:(Object.entries(s[i]).forEach((([t,e])=>{if(!o.test(t))return;n++;let[,i]=t.slice(6).split("-");r.maxClicks<i&&(r.maxClicks=i)})),n)}function m(t,e,n){let{currentContext:o,shortcuts:r}=e,{inAPI:i}=t,s={ev:t.ev,_findTarget:c,_readClickEvent:l,mainDependencies:t,regex:/CLICK\s*\:/i},m={currentContext:o,shortcuts:r,listenOptions:{mouseWait:n.mouseWait?n.mouseWait:320,maxClicks:1,clickTarget:n.clickTarget?n.clickTarget:"click"}};i._normalizeWithPlugins(u);let p=function(t,e){const{ev:n,_findTarget:o,_readClickEvent:r,mainDependencies:i}=t,{listenOptions:s,currentContext:c}=e,{mouseWait:u}=s;let l=null,a=null,m=null,p=null,h=0;function f(){const t=r(a,h),e={target:l,targetProps:l?l.getBoundingClientRect():null,x:a.clientX,y:a.clientY,context:c.name,note:c.note,event:a,dependencies:i.extra,type:"click"};n.emit(t,e),m=null,p=null,l=null,a=null,h=0}function d(n){let r=s.maxClicks;return clearTimeout(m),p?(clearTimeout(p),void(p=setTimeout((()=>p=null),u))):(l=o(t,e,n.target),l&&l.dataset.hasOwnProperty("quickClick")&&(r=1),l&&"A"===l.tagName&&(r=1),a=n,h++,h>=r?(f(),void(r>1&&(p=setTimeout((()=>p=null),u)))):void(m=setTimeout(f,u)))}function g(n){let r=s.maxClicks;return clearTimeout(m),p?(clearTimeout(p),void(p=setTimeout((()=>p=null),u))):(l=o(t,e,n.target),l&&l.dataset.hasOwnProperty("quickClick")&&(r=1),l&&"A"===l.tagName&&(r=1),a=n,h++,h>=r?(f(),void(r>1&&(p=setTimeout((()=>p=null),u)))):void(m=setTimeout(f,u)))}return{start:function(){e.active||(window.addEventListener("contextmenu",g),document.addEventListener("click",d),e.active=!0)},stop:function(){e.active&&(window.removeEventListener("contextmenu",g),document.removeEventListener("click",d),e.active=!1)}}}(s,m),h=a(s,m);h>0&&p.start();let f={getPrefix:()=>"click",shortcutName:t=>u(t),contextChange:()=>{h=a(s,m),h<1&&p.stop(),h>0&&p.start()},mute:()=>p.stop(),unmute:()=>p.start(),destroy:()=>{p.stop(),m=null,f=null}};return Object.freeze(f),f}function p(n={}){const o=t(),r={},i={},s={currentContext:{name:null,note:null},shortcuts:{},plugins:[],exposeShortcut:!(!n.onShortcut||"function"!=typeof n.onShortcut)&&n.onShortcut},c={ev:o,inAPI:r,API:i,extra:{}};return i.enablePlugin=(t,e={})=>{const n=t.name;if(-1===r._systemAction(n,"none")){let n;n=t(c,s,e),s.plugins.push(n)}},i.disablePlugin=t=>{const e=r._systemAction(t,"destroy");-1!==e&&(s.plugins=s.plugins.filter(((t,n)=>n!==e)))},i.mutePlugin=t=>r._systemAction(t,"mute"),i.unmutePlugin=t=>r._systemAction(t,"unmute"),i.getContext=()=>s.currentContext.name,i.getNote=()=>s.currentContext.note,i.setNote=(t=null)=>{"string"!=typeof t&&null!=t||(s.currentContext.note=t)},i.pause=(t="*")=>{let e=r._readShortcutWithPlugins(t);o.stop(e)},i.resume=(t="*")=>{const e=r._readShortcutWithPlugins(t);o.start(e)},i.emit=(t,...e)=>o.emit(r._readShortcutWithPlugins(t),...e),i.listContexts=()=>Object.keys(s.shortcuts),i.setDependencies=t=>c.extra={...c.extra,...t},i.getDependencies=()=>c.extra,Object.entries(e).forEach((([t,e])=>{t.startsWith("_")?r[t]=e(c,s):i[t]=e(c,s)})),i}export{m as pluginClick,s as pluginKey,p as shortcuts};
1
+ import t from"@peter.naydenov/notice";var e={_normalizeWithPlugins:function(t,e){return function(t){const n=e.shortcuts;Object.keys(n).forEach((e=>{Object.entries(n[e]).forEach((([o,r])=>{const i=t(o);i!==o&&(delete n[e][o],n[e][i]=r)}))}))}},_readShortcutWithPlugins:function(t,e){return function(n){const{inAPI:o}=t,r=n.split(":")[0],i=o._systemAction(r,"none");let s=n;return-1!==i&&(s=e.plugins[i].shortcutName(n)),s}},_systemAction:function(t,e){return function(t,n,o=null){return e.plugins.findIndex((e=>e.getPrefix()===t&&(e[n]&&e[n](o),!0)))}},changeContext:function(t,e){const{shortcuts:n,currentContext:o}=e,{ev:r}=t;return function(t=!1){const i=o.name;if(!t)return r.reset(),void(o.name=null);i!==t&&(n[t]?(n[i]&&r.reset(),o.name=t,e.plugins.forEach((e=>e.contextChange(t))),Object.entries(n[t]).forEach((([t,e])=>{e.forEach((e=>r.on(t,e)))})),r.on("*",((...t)=>{e.exposeShortcut&&e.exposeShortcut(...t)}))):r.emit("@shortcuts-error",`Context '${t}' does not exist`))}},listShortcuts:function(t,e){const n=e.shortcuts;return function(t=null){if(null!=t){let e=n[t];return null==e?null:Object.entries(e).map((([t,e])=>t))}return Object.keys(n).map((t=>{let e={};return e.context=t,e.shortcuts=Object.entries(n[t]).map((([t,e])=>t)),e}))}},load:function(t,e){const{shortcuts:n,plugins:o}=e,{API:{changeContext:r,getContext:i}}=t;return function(t){const e=i(),s=o.map((t=>t.getPrefix().toUpperCase()));let c=!1;Object.entries(t).forEach((([t,r])=>{t===e&&(c=!0),n[t]={},Object.entries(r).forEach((([e,r])=>{let i=e,c=e.toUpperCase().trim(),u=s.map(((t,e)=>c.startsWith(t)?e:null)).filter((t=>null!==t));if(u.length){let t=u[0];i=o[t].shortcutName(e)}r instanceof Function&&(r=[r]),n[t][i]=r}))})),c&&(r(),r(e))}},unload:function(t,e){const{currentContext:n,shortcuts:o}=e,{ev:r}=t;return function(t){n.name!==t?o[t]?delete o[t]:r.emit("shortcuts-error",`Context '${t}' does not exist`):r.emit("shortcuts-error",`Context '${t}' can't be removed during is current active context. Change the context first`)}}};function n(t){const e=t.toUpperCase(),n=/KEY\s*\:/i.test(e),o=e.indexOf(":");if(!n)return t;return`KEY:${e.slice(o+1).split(",").map((t=>t.trim())).map((t=>t.split("+").map((t=>t.trim())).sort().join("+"))).join(",")}`}function o(t,e){let{shiftKey:n,altKey:o,ctrlKey:r}=t,i=t.code.replace("Key","").replace("Digit",""),s=[];return r&&s.push("CTRL"),n&&s.push("SHIFT"),o&&s.push("ALT"),e.hasOwnProperty(i)?s.push(e[i].toUpperCase()):["ControlLeft","ControlRight","ShiftLeft","ShiftRight","AltLeft","AltRight","Meta"].includes(i)||s.push(i.toUpperCase()),s.sort()}function r(t,e){let n=0;const{regex:o}=t,{listenOptions:r,currentContext:{name:i},shortcuts:s}=e;return null==i?0:(Object.entries(s[i]).forEach((([t,e])=>{if(!o.test(t))return;n++;let i=t.slice(4).split(",").length;r.maxSequence<i&&(r.maxSequence=i)})),n)}function i(){return{ArrowLeft:"LEFT",ArrowUp:"UP",ArrowRight:"RIGHT",ArrowDown:"DOWN",Enter:"ENTER",NumpadEnter:"ENTER",Escape:"ESC",Backspace:"BACKSPACE",Space:"SPACE",Tab:"TAB",Backquote:"`",BracketLeft:"[",BracketRight:"]",Equal:"=",Slash:"/",Backslash:"\\",IntlBackslash:"`",F1:"F1",F2:"F2",F3:"F3",F4:"F4",F5:"F5",F6:"F6",F7:"F7",F8:"F8",F9:"F9",F10:"F10",F11:"F11",F12:"F12"}}function s(t,e,s={}){let{currentContext:c,shortcuts:u,exposeShortcut:l}=e,{inAPI:a}=t,m={ev:t.ev,_specialChars:i,_readKeyEvent:o,mainDependencies:t,regex:/KEY\s*\:/i},p={currentContext:c,shortcuts:u,active:!1,listenOptions:{keyWait:s.keyWait?s.keyWait:480,maxSequence:1,keyIgnore:null},streamKeys:!(!s.streamKeys||"function"!=typeof s.streamKeys)&&s.streamKeys};a._normalizeWithPlugins(n);let h=function(t,e){const{ev:n,_specialChars:o,_readKeyEvent:r,mainDependencies:i}=t,{currentContext:s,streamKeys:c,listenOptions:u}=e,{keyWait:l}=u;let a=[],m=null,p=!0,h=!1;const f=()=>p=!1,d=()=>p=!0,g=()=>h=!0,x=()=>!1===p;function y(){let t=a.map((t=>[t.join("+")]));const e={wait:f,end:d,ignore:g,isWaiting:x,note:s.note,context:s.name,dependencies:i.extra,type:"key"};if(!p){let o=t.at(-1);n.emit(o,e),h&&(t=t.slice(0,-1),h=!1)}if(p){const o=`KEY:${t.join(",")}`;n.emit(o,e),a=[],m=null}}function C(e){if(clearTimeout(m),o.hasOwnProperty(e.code))return a.push(r(e,o)),c&&c({key:e.key,context:s.name,note:s.note,dependencies:t.extra}),u.keyIgnore?(clearTimeout(u.keyIgnore),void(u.keyIgnore=setTimeout((()=>u.keyIgnore=null),l))):p&&a.length===u.maxSequence?(y(),void(u.keyIgnore=setTimeout((()=>u.keyIgnore=null),l))):void(p?m=setTimeout(y,l):y())}function k(e){if(!o.hasOwnProperty(e.code)){if(clearTimeout(m),c&&c({key:e.key,context:s.name,note:s.note,dependencies:t.extra}),u.keyIgnore)return clearTimeout(u.keyIgnore),void(u.keyIgnore=setTimeout((()=>u.keyIgnore=null),l));if(a.push(r(e,o)),p&&a.length===u.maxSequence)return y(),void(u.keyIgnore=setTimeout((()=>u.keyIgnore=null),l));p?m=setTimeout(y,l):y()}}return{start:function(){e.active||(document.addEventListener("keydown",C),document.addEventListener("keypress",k),e.active=!0)},stop:function(){e.active&&(document.removeEventListener("keydown",C),document.removeEventListener("keypress",k),e.active=!1)}}}(m,p),f=r(m,p);f>0&&h.start();let d={getPrefix:()=>"key",shortcutName:t=>n(t),contextChange:t=>{f=r(m,p),f<1&&h.stop(),f>0&&h.start()},mute:()=>h.stop(),unmute:()=>h.start(),destroy:()=>{h.stop(),p=null,d=null}};return Object.freeze(d),d}function c(t,e,n){const{listenOptions:{clickTarget:o}}=e;let r=n;return r===document||r===document.body?null:r.dataset[o]||"A"===r.nodeName?r:c(t,e,r.parentNode)}function u(t){const e=t.toUpperCase(),n=/CLICK\s*\:/i.test(e),o=["LEFT","MIDDLE","RIGHT"],r=["ALT","SHIFT","CTRL"];let i=null,s=[],c=0,u=e.indexOf(":");return n?(e.slice(u+1).trim().split("-").map((t=>t.trim())).forEach((t=>{o.includes(t)?i=t:r.includes(t)?s.push(t):isNaN(t)||(c=t)})),`CLICK:${i}-${c}${s.length>0?"-":""}${s.sort().join("-")}`):t}function l(t,e){let{shiftKey:n,altKey:o,ctrlKey:r,key:i,button:s}=t,c=`CLICK:${["LEFT","MIDDLE","RIGHT"][s]}-${e}`,u=[];return r&&u.push("CTRL"),n&&u.push("SHIFT"),o&&u.push("ALT"),u.length>0?`${c}${u.length>0?"-":""}${u.sort().join("-")}`:`${c}`}function a(t,e){let n=0;const{regex:o}=t,{listenOptions:r,currentContext:{name:i},shortcuts:s}=e;return null==i?0:(Object.entries(s[i]).forEach((([t,e])=>{if(!o.test(t))return;n++;let[,i]=t.slice(6).split("-");r.maxClicks<i&&(r.maxClicks=i)})),n)}function m(t,e,n){let{currentContext:o,shortcuts:r}=e,{inAPI:i}=t,s={ev:t.ev,_findTarget:c,_readClickEvent:l,mainDependencies:t,regex:/CLICK\s*\:/i},m={currentContext:o,shortcuts:r,listenOptions:{mouseWait:n.mouseWait?n.mouseWait:320,maxClicks:1,clickTarget:n.clickTarget?n.clickTarget:"click"}};i._normalizeWithPlugins(u);let p=function(t,e){const{ev:n,_findTarget:o,_readClickEvent:r,mainDependencies:i}=t,{listenOptions:s,currentContext:c}=e,{mouseWait:u}=s;let l=null,a=null,m=null,p=null,h=0;function f(){const t=r(a,h),e={target:l,targetProps:l?l.getBoundingClientRect():null,x:a.clientX,y:a.clientY,context:c.name,note:c.note,event:a,dependencies:i.extra,type:"click"};n.emit(t,e),m=null,p=null,l=null,a=null,h=0}function d(n){let r=s.maxClicks;return clearTimeout(m),p?(clearTimeout(p),void(p=setTimeout((()=>p=null),u))):(l=o(t,e,n.target),l&&l.dataset.hasOwnProperty("quickClick")&&(r=1),l&&"A"===l.tagName&&(r=1),a=n,h++,h>=r?(f(),void(r>1&&(p=setTimeout((()=>p=null),u)))):void(m=setTimeout(f,u)))}function g(n){let r=s.maxClicks;return clearTimeout(m),p?(clearTimeout(p),void(p=setTimeout((()=>p=null),u))):(l=o(t,e,n.target),l&&l.dataset.hasOwnProperty("quickClick")&&(r=1),l&&"A"===l.tagName&&(r=1),a=n,h++,h>=r?(f(),void(r>1&&(p=setTimeout((()=>p=null),u)))):void(m=setTimeout(f,u)))}return{start:function(){e.active||(window.addEventListener("contextmenu",g),document.addEventListener("click",d),e.active=!0)},stop:function(){e.active&&(window.removeEventListener("contextmenu",g),document.removeEventListener("click",d),e.active=!1)}}}(s,m),h=a(s,m);h>0&&p.start();let f={getPrefix:()=>"click",shortcutName:t=>u(t),contextChange:()=>{h=a(s,m),h<1&&p.stop(),h>0&&p.start()},mute:()=>p.stop(),unmute:()=>p.start(),destroy:()=>{p.stop(),m=null,f=null}};return Object.freeze(f),f}function p(n={}){const o=t(),r={},i={},s={currentContext:{name:null,note:null},shortcuts:{},plugins:[],exposeShortcut:!(!n.onShortcut||"function"!=typeof n.onShortcut)&&n.onShortcut},c={ev:o,inAPI:r,API:i,extra:{}};return i.enablePlugin=(t,e={})=>{const n=t.name;if(-1===r._systemAction(n,"none")){let n;n=t(c,s,e),s.plugins.push(n)}},i.disablePlugin=t=>{const e=r._systemAction(t,"destroy");-1!==e&&(s.plugins=s.plugins.filter(((t,n)=>n!==e)))},i.mutePlugin=t=>r._systemAction(t,"mute"),i.unmutePlugin=t=>r._systemAction(t,"unmute"),i.getContext=()=>s.currentContext.name,i.getNote=()=>s.currentContext.note,i.setNote=(t=null)=>{"string"!=typeof t&&null!=t||(s.currentContext.note=t)},i.pause=(t="*")=>{let e=r._readShortcutWithPlugins(t);o.stop(e)},i.resume=(t="*")=>{const e=r._readShortcutWithPlugins(t);o.start(e)},i.emit=(t,...e)=>o.emit(r._readShortcutWithPlugins(t),...e),i.listContexts=()=>Object.keys(s.shortcuts),i.setDependencies=t=>c.extra={...c.extra,...t},i.getDependencies=()=>c.extra,Object.entries(e).forEach((([t,e])=>{t.startsWith("_")?r[t]=e(c,s):i[t]=e(c,s)})),i}export{m as pluginClick,s as pluginKey,p as shortcuts};
@@ -1 +1 @@
1
- !function(e,t){"object"==typeof exports&&"undefined"!=typeof module?t(exports):"function"==typeof define&&define.amd?define(["exports"],t):t((e="undefined"!=typeof globalThis?globalThis:e||self).shortcuts={})}(this,(function(e){"use strict";var t={_normalizeWithPlugins:function(e,t){return function(e){const n=t.shortcuts;Object.keys(n).forEach((t=>{Object.entries(n[t]).forEach((([o,r])=>{const i=e(o);i!==o&&(delete n[t][o],n[t][i]=r)}))}))}},_readShortcutWithPlugins:function(e,t){return function(n){const{inAPI:o}=e,r=n.split(":")[0],i=o._systemAction(r,"none");let s=n;return-1!==i&&(s=t.plugins[i].shortcutName(n)),s}},_systemAction:function(e,t){return function(e,n,o=null){return t.plugins.findIndex((t=>t.getPrefix()===e&&(t[n]&&t[n](o),!0)))}},changeContext:function(e,t){const{shortcuts:n,currentContext:o}=t,{ev:r}=e;return function(e=!1){const i=o.name;if(!e)return r.reset(),void(o.name=null);i!==e&&(n[e]?(n[i]&&r.reset(),o.name=e,t.plugins.forEach((t=>t.contextChange(e))),Object.entries(n[e]).forEach((([e,t])=>{t.forEach((t=>r.on(e,t)))})),r.on("*",((...e)=>{t.exposeShortcut&&t.exposeShortcut(...e)}))):r.emit("@shortcuts-error",`Context '${e}' does not exist`))}},listShortcuts:function(e,t){const n=t.shortcuts;return function(e=null){if(null!=e){let t=n[e];return null==t?null:Object.entries(t).map((([e,t])=>e))}return Object.keys(n).map((e=>{let t={};return t.context=e,t.shortcuts=Object.entries(n[e]).map((([e,t])=>e)),t}))}},load:function(e,t){const{shortcuts:n,plugins:o}=t,{API:{changeContext:r,getContext:i}}=e;return function(e){const t=i(),s=o.map((e=>e.getPrefix().toUpperCase()));let c=!1;Object.entries(e).forEach((([e,r])=>{e===t&&(c=!0),n[e]={},Object.entries(r).forEach((([t,r])=>{let i=t,c=t.toUpperCase().trim(),u=s.map(((e,t)=>c.startsWith(e)?t:null)).filter((e=>null!==e));if(u.length){let e=u[0];i=o[e].shortcutName(t)}r instanceof Function&&(r=[r]),n[e][i]=r}))})),c&&(r(),r(t))}},unload:function(e,t){const{currentContext:n,shortcuts:o}=t,{ev:r}=e;return function(e){n.name!==e?o[e]?delete o[e]:r.emit("shortcuts-error",`Context '${e}' does not exist`):r.emit("shortcuts-error",`Context '${e}' can't be removed during is current active context. Change the context first`)}}};function n(e){const t=e.toUpperCase(),n=/KEY\s*\:/i.test(t),o=t.indexOf(":");if(!n)return e;return`KEY:${t.slice(o+1).split(",").map((e=>e.trim())).map((e=>e.split("+").map((e=>e.trim())).sort().join("+"))).join(",")}`}function o(e,t){let{shiftKey:n,altKey:o,ctrlKey:r}=e,i=e.code.replace("Key","").replace("Digit",""),s=[];return r&&s.push("CTRL"),n&&s.push("SHIFT"),o&&s.push("ALT"),t.hasOwnProperty(i)?s.push(t[i].toUpperCase()):["ControlLeft","ControlRight","ShiftLeft","ShiftRight","AltLeft","AltRight","Meta"].includes(i)||s.push(i.toUpperCase()),s.sort()}function r(e,t){let n=0;const{regex:o}=e,{listenOptions:r,currentContext:{name:i},shortcuts:s}=t;return null==i?0:(Object.entries(s[i]).forEach((([e,t])=>{if(!o.test(e))return;n++;let i=e.slice(4).split(",").length;r.maxSequence<i&&(r.maxSequence=i)})),n)}function i(){return{ArrowLeft:"LEFT",ArrowUp:"UP",ArrowRight:"RIGHT",ArrowDown:"DOWN",Enter:"ENTER",NumpadEnter:"ENTER",Escape:"ESC",Backspace:"BACKSPACE",Space:"SPACE",Tab:"TAB",Backquote:"`",BracketLeft:"[",BracketRight:"]",Equal:"=",Slash:"/",Backslash:"\\",IntlBackslash:"`",F1:"F1",F2:"F2",F3:"F3",F4:"F4",F5:"F5",F6:"F6",F7:"F7",F8:"F8",F9:"F9",F10:"F10",F11:"F11",F12:"F12"}}function s(e,t,n){const{listenOptions:{clickTarget:o}}=t;let r=n;return r===document||r===document.body?null:r.dataset[o]||"A"===r.nodeName?r:s(e,t,r.parentNode)}function c(e){const t=e.toUpperCase(),n=/CLICK\s*\:/i.test(t),o=["LEFT","MIDDLE","RIGHT"],r=["ALT","SHIFT","CTRL"];let i=null,s=[],c=0,u=t.indexOf(":");return n?(t.slice(u+1).trim().split("-").map((e=>e.trim())).forEach((e=>{o.includes(e)?i=e:r.includes(e)?s.push(e):isNaN(e)||(c=e)})),`CLICK:${i}-${c}${s.length>0?"-":""}${s.sort().join("-")}`):e}function u(e,t){let{shiftKey:n,altKey:o,ctrlKey:r,key:i,button:s}=e,c=`CLICK:${["LEFT","MIDDLE","RIGHT"][s]}-${t}`,u=[];return r&&u.push("CTRL"),n&&u.push("SHIFT"),o&&u.push("ALT"),u.length>0?`${c}${u.length>0?"-":""}${u.sort().join("-")}`:`${c}`}function l(e,t){let n=0;const{regex:o}=e,{listenOptions:r,currentContext:{name:i},shortcuts:s}=t;return null==i?0:(Object.entries(s[i]).forEach((([e,t])=>{if(!o.test(e))return;n++;let[,i]=e.slice(6).split("-");r.maxClicks<i&&(r.maxClicks=i)})),n)}e.pluginClick=function(e,t,n){let{currentContext:o,shortcuts:r}=t,{inAPI:i}=e,a={ev:e.ev,_findTarget:s,_readClickEvent:u,mainDependencies:e,regex:/CLICK\s*\:/i},f={currentContext:o,shortcuts:r,listenOptions:{mouseWait:n.mouseWait?n.mouseWait:320,maxClicks:1,clickTarget:n.clickTarget?n.clickTarget:"click"}};i._normalizeWithPlugins(c);let m=function(e,t){const{ev:n,_findTarget:o,_readClickEvent:r,mainDependencies:i}=e,{listenOptions:s,currentContext:c}=t,{mouseWait:u}=s;let l=null,a=null,f=null,m=null,p=0;function h(){const e=r(a,p),t={target:l,targetProps:l?l.getBoundingClientRect():null,x:a.clientX,y:a.clientY,context:c.name,note:c.note,event:a,dependencies:i.extra,type:"click"};n.emit(e,t),f=null,m=null,l=null,a=null,p=0}function d(n){let r=s.maxClicks;return clearTimeout(f),m?(clearTimeout(m),void(m=setTimeout((()=>m=null),u))):(l=o(e,t,n.target),l&&l.dataset.hasOwnProperty("quickClick")&&(r=1),l&&"A"===l.tagName&&(r=1),a=n,p++,p>=r?(h(),void(r>1&&(m=setTimeout((()=>m=null),u)))):void(f=setTimeout(h,u)))}function g(n){let r=s.maxClicks;return clearTimeout(f),m?(clearTimeout(m),void(m=setTimeout((()=>m=null),u))):(l=o(e,t,n.target),l&&l.dataset.hasOwnProperty("quickClick")&&(r=1),l&&"A"===l.tagName&&(r=1),a=n,p++,p>=r?(h(),void(r>1&&(m=setTimeout((()=>m=null),u)))):void(f=setTimeout(h,u)))}return{start:function(){t.active||(window.addEventListener("contextmenu",g),document.addEventListener("click",d),t.active=!0)},stop:function(){t.active&&(window.removeEventListener("contextmenu",g),document.removeEventListener("click",d),t.active=!1)}}}(a,f),p=l(a,f);p>0&&m.start();let h={getPrefix:()=>"click",shortcutName:e=>c(e),contextChange:()=>{p=l(a,f),p<1&&m.stop(),p>0&&m.start()},mute:()=>m.stop(),unmute:()=>m.start(),destroy:()=>{m.stop(),f=null,h=null}};return Object.freeze(h),h},e.pluginKey=function(e,t,s={}){let{currentContext:c,shortcuts:u,exposeShortcut:l}=t,{inAPI:a}=e,f={ev:e.ev,_specialChars:i,_readKeyEvent:o,mainDependencies:e,regex:/KEY\s*\:/i},m={currentContext:c,shortcuts:u,active:!1,listenOptions:{keyWait:s.keyWait?s.keyWait:480,maxSequence:1,keyIgnore:null},streamKeys:!(!s.streamKeys||"function"!=typeof s.streamKeys)&&s.streamKeys,exposeShortcut:l};a._normalizeWithPlugins(n);let p=function(e,t){const{ev:n,_specialChars:o,_readKeyEvent:r,mainDependencies:i}=e,{currentContext:s,streamKeys:c,listenOptions:u}=t,{keyWait:l}=u;let a=[],f=null,m=!0,p=!1;const h=()=>m=!1,d=()=>m=!0,g=()=>p=!0,y=()=>!1===m;function x(){let e=a.map((e=>[e.join("+")]));const t={wait:h,end:d,ignore:g,isWaiting:y,note:s.note,context:s.name,dependencies:i.extra,type:"key"};if(!m){let o=e.at(-1);n.emit(o,t),p&&(e=e.slice(0,-1),p=!1)}if(m){const o=`KEY:${e.join(",")}`;n.emit(o,t),a=[],f=null}}function C(t){if(clearTimeout(f),o.hasOwnProperty(t.code))return a.push(r(t,o)),c&&c({key:t.key,context:s.name,note:s.note,dependencies:e.extra}),u.keyIgnore?(clearTimeout(u.keyIgnore),void(u.keyIgnore=setTimeout((()=>u.keyIgnore=null),l))):m&&a.length===u.maxSequence?(x(),void(u.keyIgnore=setTimeout((()=>u.keyIgnore=null),l))):void(m?f=setTimeout(x,l):x())}function k(t){if(!o.hasOwnProperty(t.code)){if(clearTimeout(f),c&&c({key:t.key,context:s.name,note:s.note,dependencies:e.extra}),u.keyIgnore)return clearTimeout(u.keyIgnore),void(u.keyIgnore=setTimeout((()=>u.keyIgnore=null),l));if(a.push(r(t,o)),m&&a.length===u.maxSequence)return x(),void(u.keyIgnore=setTimeout((()=>u.keyIgnore=null),l));m?f=setTimeout(x,l):x()}}return{start:function(){t.active||(document.addEventListener("keydown",C),document.addEventListener("keypress",k),t.active=!0)},stop:function(){t.active&&(document.removeEventListener("keydown",C),document.removeEventListener("keypress",k),t.active=!1)}}}(f,m),h=r(f,m);h>0&&p.start();let d={getPrefix:()=>"key",shortcutName:e=>n(e),contextChange:e=>{h=r(f,m),h<1&&p.stop(),h>0&&p.start()},mute:()=>p.stop(),unmute:()=>p.start(),destroy:()=>{p.stop(),m=null,d=null}};return Object.freeze(d),d},e.shortcuts=function(e={}){const n=function(){let e={"*":[]},t={},n=[],o=!1,r="";return{on:function(t,n){e[t]||(e[t]=[]),e[t].push(n)},once:function(e,n){"*"!==e&&(t[e]||(t[e]=[]),t[e].push(n))},off:function(n,o){if(o)return e[n]&&(e[n]=e[n].filter((e=>e!==o))),t[n]&&(t[n]=t[n].filter((e=>e!==o))),e[n]&&0===e[n].length&&delete e[n],void(t[n]&&0===t[n].length&&delete e[n]);t[n]&&delete t[n],e[n]&&delete e[n]},reset:function(){e={"*":[]},t={},n=[]},emit:function(){const[i,...s]=arguments;function c(t){let o=!1;"*"!==t&&(n.includes(t)||(e[t].every((e=>{const t=e(...s);return"string"!=typeof t||"STOP"!==t.toUpperCase()||(o=!0,!1)})),o||e["*"].forEach((e=>e(i,...s)))))}if(o&&(console.log(`${r} Event "${i}" was triggered.`),s.length>0&&(console.log("Arguments:"),console.log(...s),console.log("^----"))),"*"!==i){if(t[i]){if(n.includes(i))return;t[i].forEach((e=>e(...s))),delete t[i]}e[i]&&c(i)}else Object.keys(e).forEach((e=>c(e)))},stop:function(o){if("*"!==o)n.push(o);else{const o=Object.keys(e),r=Object.keys(t);n=[...r,...o]}},start:function(e){n="*"!==e?n.filter((t=>e!=t)):[]},debug:function(e,t){o=!!e,t&&"string"==typeof t&&(r=t)}}}(),o={},r={},i={currentContext:{name:null,note:null},shortcuts:{},plugins:[],exposeShortcut:!(!e.onShortcut||"function"!=typeof e.onShortcut)&&e.onShortcut},s={ev:n,inAPI:o,API:r,extra:{}};return r.enablePlugin=(e,t={})=>{const n=e.name;if(-1===o._systemAction(n,"none")){let n;n=e(s,i,t),i.plugins.push(n)}},r.disablePlugin=e=>{const t=o._systemAction(e,"destroy");-1!==t&&(i.plugins=i.plugins.filter(((e,n)=>n!==t)))},r.mutePlugin=e=>o._systemAction(e,"mute"),r.unmutePlugin=e=>o._systemAction(e,"unmute"),r.getContext=()=>i.currentContext.name,r.getNote=()=>i.currentContext.note,r.setNote=(e=null)=>{"string"!=typeof e&&null!=e||(i.currentContext.note=e)},r.pause=(e="*")=>{let t=o._readShortcutWithPlugins(e);n.stop(t)},r.resume=(e="*")=>{const t=o._readShortcutWithPlugins(e);n.start(t)},r.emit=(e,...t)=>n.emit(o._readShortcutWithPlugins(e),...t),r.listContexts=()=>Object.keys(i.shortcuts),r.setDependencies=e=>s.extra={...s.extra,...e},r.getDependencies=()=>s.extra,Object.entries(t).forEach((([e,t])=>{e.startsWith("_")?o[e]=t(s,i):r[e]=t(s,i)})),r}}));
1
+ !function(e,t){"object"==typeof exports&&"undefined"!=typeof module?t(exports):"function"==typeof define&&define.amd?define(["exports"],t):t((e="undefined"!=typeof globalThis?globalThis:e||self).shortcuts={})}(this,(function(e){"use strict";var t={_normalizeWithPlugins:function(e,t){return function(e){const n=t.shortcuts;Object.keys(n).forEach((t=>{Object.entries(n[t]).forEach((([o,r])=>{const i=e(o);i!==o&&(delete n[t][o],n[t][i]=r)}))}))}},_readShortcutWithPlugins:function(e,t){return function(n){const{inAPI:o}=e,r=n.split(":")[0],i=o._systemAction(r,"none");let s=n;return-1!==i&&(s=t.plugins[i].shortcutName(n)),s}},_systemAction:function(e,t){return function(e,n,o=null){return t.plugins.findIndex((t=>t.getPrefix()===e&&(t[n]&&t[n](o),!0)))}},changeContext:function(e,t){const{shortcuts:n,currentContext:o}=t,{ev:r}=e;return function(e=!1){const i=o.name;if(!e)return r.reset(),void(o.name=null);i!==e&&(n[e]?(n[i]&&r.reset(),o.name=e,t.plugins.forEach((t=>t.contextChange(e))),Object.entries(n[e]).forEach((([e,t])=>{t.forEach((t=>r.on(e,t)))})),r.on("*",((...e)=>{t.exposeShortcut&&t.exposeShortcut(...e)}))):r.emit("@shortcuts-error",`Context '${e}' does not exist`))}},listShortcuts:function(e,t){const n=t.shortcuts;return function(e=null){if(null!=e){let t=n[e];return null==t?null:Object.entries(t).map((([e,t])=>e))}return Object.keys(n).map((e=>{let t={};return t.context=e,t.shortcuts=Object.entries(n[e]).map((([e,t])=>e)),t}))}},load:function(e,t){const{shortcuts:n,plugins:o}=t,{API:{changeContext:r,getContext:i}}=e;return function(e){const t=i(),s=o.map((e=>e.getPrefix().toUpperCase()));let c=!1;Object.entries(e).forEach((([e,r])=>{e===t&&(c=!0),n[e]={},Object.entries(r).forEach((([t,r])=>{let i=t,c=t.toUpperCase().trim(),u=s.map(((e,t)=>c.startsWith(e)?t:null)).filter((e=>null!==e));if(u.length){let e=u[0];i=o[e].shortcutName(t)}r instanceof Function&&(r=[r]),n[e][i]=r}))})),c&&(r(),r(t))}},unload:function(e,t){const{currentContext:n,shortcuts:o}=t,{ev:r}=e;return function(e){n.name!==e?o[e]?delete o[e]:r.emit("shortcuts-error",`Context '${e}' does not exist`):r.emit("shortcuts-error",`Context '${e}' can't be removed during is current active context. Change the context first`)}}};function n(e){const t=e.toUpperCase(),n=/KEY\s*\:/i.test(t),o=t.indexOf(":");if(!n)return e;return`KEY:${t.slice(o+1).split(",").map((e=>e.trim())).map((e=>e.split("+").map((e=>e.trim())).sort().join("+"))).join(",")}`}function o(e,t){let{shiftKey:n,altKey:o,ctrlKey:r}=e,i=e.code.replace("Key","").replace("Digit",""),s=[];return r&&s.push("CTRL"),n&&s.push("SHIFT"),o&&s.push("ALT"),t.hasOwnProperty(i)?s.push(t[i].toUpperCase()):["ControlLeft","ControlRight","ShiftLeft","ShiftRight","AltLeft","AltRight","Meta"].includes(i)||s.push(i.toUpperCase()),s.sort()}function r(e,t){let n=0;const{regex:o}=e,{listenOptions:r,currentContext:{name:i},shortcuts:s}=t;return null==i?0:(Object.entries(s[i]).forEach((([e,t])=>{if(!o.test(e))return;n++;let i=e.slice(4).split(",").length;r.maxSequence<i&&(r.maxSequence=i)})),n)}function i(){return{ArrowLeft:"LEFT",ArrowUp:"UP",ArrowRight:"RIGHT",ArrowDown:"DOWN",Enter:"ENTER",NumpadEnter:"ENTER",Escape:"ESC",Backspace:"BACKSPACE",Space:"SPACE",Tab:"TAB",Backquote:"`",BracketLeft:"[",BracketRight:"]",Equal:"=",Slash:"/",Backslash:"\\",IntlBackslash:"`",F1:"F1",F2:"F2",F3:"F3",F4:"F4",F5:"F5",F6:"F6",F7:"F7",F8:"F8",F9:"F9",F10:"F10",F11:"F11",F12:"F12"}}function s(e,t,n){const{listenOptions:{clickTarget:o}}=t;let r=n;return r===document||r===document.body?null:r.dataset[o]||"A"===r.nodeName?r:s(e,t,r.parentNode)}function c(e){const t=e.toUpperCase(),n=/CLICK\s*\:/i.test(t),o=["LEFT","MIDDLE","RIGHT"],r=["ALT","SHIFT","CTRL"];let i=null,s=[],c=0,u=t.indexOf(":");return n?(t.slice(u+1).trim().split("-").map((e=>e.trim())).forEach((e=>{o.includes(e)?i=e:r.includes(e)?s.push(e):isNaN(e)||(c=e)})),`CLICK:${i}-${c}${s.length>0?"-":""}${s.sort().join("-")}`):e}function u(e,t){let{shiftKey:n,altKey:o,ctrlKey:r,key:i,button:s}=e,c=`CLICK:${["LEFT","MIDDLE","RIGHT"][s]}-${t}`,u=[];return r&&u.push("CTRL"),n&&u.push("SHIFT"),o&&u.push("ALT"),u.length>0?`${c}${u.length>0?"-":""}${u.sort().join("-")}`:`${c}`}function l(e,t){let n=0;const{regex:o}=e,{listenOptions:r,currentContext:{name:i},shortcuts:s}=t;return null==i?0:(Object.entries(s[i]).forEach((([e,t])=>{if(!o.test(e))return;n++;let[,i]=e.slice(6).split("-");r.maxClicks<i&&(r.maxClicks=i)})),n)}e.pluginClick=function(e,t,n){let{currentContext:o,shortcuts:r}=t,{inAPI:i}=e,a={ev:e.ev,_findTarget:s,_readClickEvent:u,mainDependencies:e,regex:/CLICK\s*\:/i},f={currentContext:o,shortcuts:r,listenOptions:{mouseWait:n.mouseWait?n.mouseWait:320,maxClicks:1,clickTarget:n.clickTarget?n.clickTarget:"click"}};i._normalizeWithPlugins(c);let m=function(e,t){const{ev:n,_findTarget:o,_readClickEvent:r,mainDependencies:i}=e,{listenOptions:s,currentContext:c}=t,{mouseWait:u}=s;let l=null,a=null,f=null,m=null,p=0;function h(){const e=r(a,p),t={target:l,targetProps:l?l.getBoundingClientRect():null,x:a.clientX,y:a.clientY,context:c.name,note:c.note,event:a,dependencies:i.extra,type:"click"};n.emit(e,t),f=null,m=null,l=null,a=null,p=0}function d(n){let r=s.maxClicks;return clearTimeout(f),m?(clearTimeout(m),void(m=setTimeout((()=>m=null),u))):(l=o(e,t,n.target),l&&l.dataset.hasOwnProperty("quickClick")&&(r=1),l&&"A"===l.tagName&&(r=1),a=n,p++,p>=r?(h(),void(r>1&&(m=setTimeout((()=>m=null),u)))):void(f=setTimeout(h,u)))}function g(n){let r=s.maxClicks;return clearTimeout(f),m?(clearTimeout(m),void(m=setTimeout((()=>m=null),u))):(l=o(e,t,n.target),l&&l.dataset.hasOwnProperty("quickClick")&&(r=1),l&&"A"===l.tagName&&(r=1),a=n,p++,p>=r?(h(),void(r>1&&(m=setTimeout((()=>m=null),u)))):void(f=setTimeout(h,u)))}return{start:function(){t.active||(window.addEventListener("contextmenu",g),document.addEventListener("click",d),t.active=!0)},stop:function(){t.active&&(window.removeEventListener("contextmenu",g),document.removeEventListener("click",d),t.active=!1)}}}(a,f),p=l(a,f);p>0&&m.start();let h={getPrefix:()=>"click",shortcutName:e=>c(e),contextChange:()=>{p=l(a,f),p<1&&m.stop(),p>0&&m.start()},mute:()=>m.stop(),unmute:()=>m.start(),destroy:()=>{m.stop(),f=null,h=null}};return Object.freeze(h),h},e.pluginKey=function(e,t,s={}){let{currentContext:c,shortcuts:u,exposeShortcut:l}=t,{inAPI:a}=e,f={ev:e.ev,_specialChars:i,_readKeyEvent:o,mainDependencies:e,regex:/KEY\s*\:/i},m={currentContext:c,shortcuts:u,active:!1,listenOptions:{keyWait:s.keyWait?s.keyWait:480,maxSequence:1,keyIgnore:null},streamKeys:!(!s.streamKeys||"function"!=typeof s.streamKeys)&&s.streamKeys};a._normalizeWithPlugins(n);let p=function(e,t){const{ev:n,_specialChars:o,_readKeyEvent:r,mainDependencies:i}=e,{currentContext:s,streamKeys:c,listenOptions:u}=t,{keyWait:l}=u;let a=[],f=null,m=!0,p=!1;const h=()=>m=!1,d=()=>m=!0,g=()=>p=!0,y=()=>!1===m;function x(){let e=a.map((e=>[e.join("+")]));const t={wait:h,end:d,ignore:g,isWaiting:y,note:s.note,context:s.name,dependencies:i.extra,type:"key"};if(!m){let o=e.at(-1);n.emit(o,t),p&&(e=e.slice(0,-1),p=!1)}if(m){const o=`KEY:${e.join(",")}`;n.emit(o,t),a=[],f=null}}function C(t){if(clearTimeout(f),o.hasOwnProperty(t.code))return a.push(r(t,o)),c&&c({key:t.key,context:s.name,note:s.note,dependencies:e.extra}),u.keyIgnore?(clearTimeout(u.keyIgnore),void(u.keyIgnore=setTimeout((()=>u.keyIgnore=null),l))):m&&a.length===u.maxSequence?(x(),void(u.keyIgnore=setTimeout((()=>u.keyIgnore=null),l))):void(m?f=setTimeout(x,l):x())}function k(t){if(!o.hasOwnProperty(t.code)){if(clearTimeout(f),c&&c({key:t.key,context:s.name,note:s.note,dependencies:e.extra}),u.keyIgnore)return clearTimeout(u.keyIgnore),void(u.keyIgnore=setTimeout((()=>u.keyIgnore=null),l));if(a.push(r(t,o)),m&&a.length===u.maxSequence)return x(),void(u.keyIgnore=setTimeout((()=>u.keyIgnore=null),l));m?f=setTimeout(x,l):x()}}return{start:function(){t.active||(document.addEventListener("keydown",C),document.addEventListener("keypress",k),t.active=!0)},stop:function(){t.active&&(document.removeEventListener("keydown",C),document.removeEventListener("keypress",k),t.active=!1)}}}(f,m),h=r(f,m);h>0&&p.start();let d={getPrefix:()=>"key",shortcutName:e=>n(e),contextChange:e=>{h=r(f,m),h<1&&p.stop(),h>0&&p.start()},mute:()=>p.stop(),unmute:()=>p.start(),destroy:()=>{p.stop(),m=null,d=null}};return Object.freeze(d),d},e.shortcuts=function(e={}){const n=function(){let e={"*":[]},t={},n=new Set,o=!1,r="";return{on:function(t,n){e[t]||(e[t]=[]),e[t].push(n)},once:function(e,n){"*"!==e&&(t[e]||(t[e]=[]),t[e].push(n))},off:function(n,o){if(o)return e[n]&&(e[n]=e[n].filter((e=>e!==o))),t[n]&&(t[n]=t[n].filter((e=>e!==o))),e[n]&&0===e[n].length&&delete e[n],void(t[n]&&0===t[n].length&&delete e[n]);t[n]&&delete t[n],e[n]&&delete e[n]},reset:function(){e={"*":[]},t={},n=new Set},emit:function(){const[i,...s]=arguments;function c(t){let o=!1;"*"!==t&&(n.has(t)||(e[t].every((e=>{const t=e(...s);return"string"!=typeof t||"STOP"!==t.toUpperCase()||(o=!0,!1)})),o||e["*"].forEach((e=>e(i,...s)))))}if(o&&(console.log(`${r} Event "${i}" was triggered.`),s.length>0&&(console.log("Arguments:"),console.log(...s),console.log("^----"))),"*"!==i){if(t[i]){if(n.has(i))return;t[i].forEach((e=>e(...s))),delete t[i]}e[i]&&c(i)}else Object.keys(e).forEach((e=>c(e)))},stop:function(o){if("*"!==o)n.add(o);else{const o=Object.keys(e),r=Object.keys(t);n=new Set([...r,...o])}},start:function(e){"*"!==e?n.delete(e):n.clear()},debug:function(e,t){o=!!e,t&&"string"==typeof t&&(r=t)}}}(),o={},r={},i={currentContext:{name:null,note:null},shortcuts:{},plugins:[],exposeShortcut:!(!e.onShortcut||"function"!=typeof e.onShortcut)&&e.onShortcut},s={ev:n,inAPI:o,API:r,extra:{}};return r.enablePlugin=(e,t={})=>{const n=e.name;if(-1===o._systemAction(n,"none")){let n;n=e(s,i,t),i.plugins.push(n)}},r.disablePlugin=e=>{const t=o._systemAction(e,"destroy");-1!==t&&(i.plugins=i.plugins.filter(((e,n)=>n!==t)))},r.mutePlugin=e=>o._systemAction(e,"mute"),r.unmutePlugin=e=>o._systemAction(e,"unmute"),r.getContext=()=>i.currentContext.name,r.getNote=()=>i.currentContext.note,r.setNote=(e=null)=>{"string"!=typeof e&&null!=e||(i.currentContext.note=e)},r.pause=(e="*")=>{let t=o._readShortcutWithPlugins(e);n.stop(t)},r.resume=(e="*")=>{const t=o._readShortcutWithPlugins(e);n.start(t)},r.emit=(e,...t)=>n.emit(o._readShortcutWithPlugins(e),...t),r.listContexts=()=>Object.keys(i.shortcuts),r.setDependencies=e=>s.extra={...s.extra,...e},r.getDependencies=()=>s.extra,Object.entries(t).forEach((([e,t])=>{e.startsWith("_")?o[e]=t(s,i):r[e]=t(s,i)})),r}}));
package/index.html CHANGED
@@ -22,7 +22,7 @@
22
22
  </head>
23
23
  <body>
24
24
  <script type="module">
25
- import { shortcuts, pluginKey, pluginClick } from '/src/main.js'
25
+ import { shortcuts, pluginKey, pluginClick, pluginForm } from './src/main.js'
26
26
 
27
27
  const options = {
28
28
  onShortcut ( shortcut, { context, note }) {
@@ -36,9 +36,14 @@
36
36
  const short = shortcuts ( options );
37
37
  short.enablePlugin( pluginKey )
38
38
  short.enablePlugin( pluginClick )
39
+ short.enablePlugin ( pluginForm )
39
40
 
40
41
 
41
- short.load ( { general : {
42
+ short.load ( {
43
+ test : {
44
+ 'key:r' : () => console.log ( 'R was clicked' )
45
+ },
46
+ general : {
42
47
  'key:q' : [ () => console.log('Q was clicked') ]
43
48
  , 'key:r,o,s' : () => console.log ( 'ROS was clicked' )
44
49
  , 'click:left-2' : () => console.log ( 'Mouse left click 2' )
@@ -67,15 +72,54 @@
67
72
  setTimeout ( end, 3000 )
68
73
  }
69
74
  ]
70
- }
75
+ , 'form : watch' : () => 'input, button'
76
+ , 'form : define' : (target) => {
77
+ if ( target.tagName === 'INPUT' ) {
78
+ return 'input'
79
+ }
80
+ if ( target.tagName === 'BUTTON' ) {
81
+ return 'button'
82
+ }
83
+ }
84
+ , 'form : action' : () => [
85
+ {
86
+ fn: ({target}) => { console.log ( target)}
87
+ , type : 'input'
88
+ , timing : 'in'
89
+ },
90
+ {
91
+ fn: ({target}) => { console.log ( 'extra')}
92
+ , type : 'input'
93
+ , timing : 'in'
94
+ },
95
+ {
96
+ fn: () => { console.log ( 'Update content') }
97
+ , type : 'input'
98
+ , timing : 'instant'
99
+ , wait : 500
100
+ },
101
+ {
102
+ fn: () => { console.log('It was a button') }
103
+ , type : 'button'
104
+ , timing : 'out'
105
+ }
106
+ ] // form: action
107
+ }
71
108
  })
72
109
  short.changeContext ( 'general' )
73
- short.setNote ( 'some' )
110
+
111
+
112
+ // short.changeContext ( )
113
+ // short.changeContext ( 'general' )
114
+
115
+
74
116
  </script>
75
117
  <div class="block" data-click="red-block">
76
118
  <span>hello</span>
77
119
  </div>
78
120
 
79
121
  <button class="big-btn" data-click="mega-button" draggable>Mega button</button>
122
+ <p><input id="name" type="text" placeholder="Name"/></p>
123
+ <p><input id="age" type="text" placeholder="Age" /></p>
80
124
  </body>
81
125
  </html>
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@peter.naydenov/shortcuts",
3
3
  "description": "Context control of shortcuts based on keyboard and mouse events",
4
- "version": "3.1.4",
4
+ "version": "3.2.0",
5
5
  "license": "MIT",
6
6
  "author": "Peter Naydenov",
7
7
  "main": "./dist/shortcuts.umd.js",
@@ -29,19 +29,19 @@
29
29
  "@peter.naydenov/notice": "^2.4.1"
30
30
  },
31
31
  "devDependencies": {
32
- "@peter.naydenov/visual-controller-for-react": "^3.0.0",
33
- "@rollup/plugin-commonjs": "^28.0.3",
32
+ "@peter.naydenov/visual-controller-for-react": "^3.0.1",
33
+ "@rollup/plugin-commonjs": "^28.0.6",
34
34
  "@rollup/plugin-node-resolve": "^16.0.1",
35
35
  "@rollup/plugin-terser": "^0.4.4",
36
- "@vitejs/plugin-react": "^4.4.1",
37
- "@vitest/browser": "^3.1.2",
36
+ "@vitejs/plugin-react": "^5.0.0",
37
+ "@vitest/browser": "^3.2.4",
38
38
  "ask-for-promise": "^3.0.1",
39
- "chai": "^5.2.0",
40
- "mocha": "^11.2.2",
41
- "playwright": "^1.52.0",
39
+ "chai": "^5.2.1",
40
+ "mocha": "^11.7.1",
41
+ "playwright": "^1.54.2",
42
42
  "react": "^19.0.1",
43
43
  "react-dom": "^19.0.1",
44
- "vite": "^6.3.4"
44
+ "vite": "^7.1.2"
45
45
  },
46
46
  "keywords": [
47
47
  "shortcut",
package/src/main.js CHANGED
@@ -4,7 +4,7 @@
4
4
  * Shortcuts
5
5
  * ========
6
6
  *
7
- * Create shortcuts for your web application based on keyboard and mouse events.
7
+ * Create shortcuts for your web application based on keyboard and mouse and DOM events.
8
8
  * Repository: https://github.com/PeterNaydenov/shortcuts
9
9
  *
10
10
  * History notes:
@@ -13,6 +13,7 @@
13
13
  * - Method 'emit' was added on September 30st, 2023
14
14
  * - Version 2.0.0 was published on October 16th, 2023
15
15
  * - Version 3.0.0. Plugin system. Published on March 5th, 2024
16
+ * - Version 3.2.0. Added plugin 'form'. Published on August 15th, 2025
16
17
  */
17
18
 
18
19
 
@@ -23,6 +24,7 @@ import methods from './methods/index.js'
23
24
  // Plugins
24
25
  import pluginKey from './plugins/key/index.js'
25
26
  import pluginClick from './plugins/click/index.js'
27
+ import pluginForm from './plugins/form/index.js'
26
28
 
27
29
 
28
30
 
@@ -194,6 +196,7 @@ export { main as shortcuts }
194
196
  export {
195
197
  pluginKey
196
198
  , pluginClick
199
+ , pluginForm
197
200
  }
198
201
 
199
202
 
@@ -29,7 +29,9 @@ return function changeContext ( contextName = false ) {
29
29
  const current = currentContext.name;
30
30
 
31
31
  if ( !contextName ) { // Switch off all shortcuts if contextName is not defined
32
- ev.reset ()
32
+ ev.reset ()
33
+ // All DOM/Mouse listeners provides trigger signal to notice(the event emitter).
34
+ // If event emitter was reset, all DOM/Mouse listener signals will be ignored.
33
35
  currentContext.name = null
34
36
  return
35
37
  }
@@ -0,0 +1,18 @@
1
+ const _defaults = {
2
+ watch : () => 'input, select, textarea, button, a'
3
+ , define: (el) => {
4
+ if ( el.type === 'checkbox' || el.type === 'radio' ) {
5
+ return 'checkbox'
6
+ }
7
+ if ( el.type == 'button' || el.type=='submit' ) {
8
+ return 'button'
9
+ }
10
+ return 'input'
11
+ } // define
12
+ } // defaults
13
+
14
+
15
+
16
+ export default _defaults
17
+
18
+
@@ -0,0 +1,90 @@
1
+ 'use strict'
2
+
3
+
4
+
5
+ function _listenDOM ( dependencies, state ) {
6
+ const { ev } = dependencies;
7
+ let timeout = null;
8
+
9
+ function setupData ( dependencies, state, event, type) {
10
+ return {
11
+ target : event.target
12
+ // TODO: Find if is possible to add some size and positioning data
13
+ , context : state.currentContext.name
14
+ , note : state.currentContext.note
15
+ , event
16
+ , dependencies : dependencies.mainDependencies.extra
17
+ , type
18
+ }
19
+ } // setupData func.
20
+
21
+ function listenFocusIn ( event ) { // Timing: in
22
+ const
23
+ { callbacks, typeFn } = state
24
+ , target = event.target
25
+ , type = typeFn ( target )
26
+ , prop = setupData ( dependencies, state, event, type )
27
+ , key = `${type}/in`
28
+ ;
29
+ if ( callbacks[key] == null ) return
30
+ ev.emit ( key, prop, callbacks[key] )
31
+ } // focusIn func.
32
+
33
+ function listenFocusOut ( event ) { // Timing: out
34
+ const
35
+ { callbacks, typeFn } = state
36
+ , prop = setupData ( dependencies, state, event, "form-out" )
37
+ , target = event.target
38
+ , type = typeFn ( target )
39
+ , key = `${type}/out`
40
+ ;
41
+ if ( callbacks[key] == null ) return
42
+ ev.emit ( key, prop, callbacks[key] )
43
+ } // focusOut func.
44
+
45
+
46
+ function listenInput ( event ) { // Timing: instant
47
+ const
48
+ { callbacks, typeFn } = state
49
+ , prop = setupData ( dependencies, state, event, "form-instant" )
50
+ , target = event.target
51
+ , type = typeFn ( target )
52
+ , wait = state.wait[`${type}`]
53
+ , key = `${type}/instant`
54
+ ;
55
+ if ( callbacks[key] == null ) return
56
+ if ( wait === 0 ) {
57
+ ev.emit ( key, prop, callbacks[key] )
58
+ return
59
+ }
60
+ let fn = () => ev.emit ( key, prop, callbacks[key] )
61
+ clearTimeout ( timeout )
62
+ timeout = setTimeout ( fn, wait )
63
+ } // input func.
64
+
65
+
66
+ function start () {
67
+ if ( state.active ) return
68
+ document.addEventListener ( 'focusin', listenFocusIn );
69
+ document.addEventListener ( 'focusout', listenFocusOut );
70
+ document.addEventListener ( 'input', listenInput );
71
+ state.active = true
72
+ } // start func.
73
+
74
+
75
+ function stop () {
76
+ if ( !state.active ) return
77
+ document.removeEventListener ( 'focusin', listenFocusIn );
78
+ document.removeEventListener ( 'focusout', listenFocusOut );
79
+ document.removeEventListener ( 'input', listenInput );
80
+ state.active = false
81
+ } // stop func.
82
+
83
+ return { start, stop }
84
+ } // _listenDOM func.
85
+
86
+
87
+
88
+ export default _listenDOM
89
+
90
+
@@ -0,0 +1,21 @@
1
+ 'use strict'
2
+
3
+ function _normalizeShortcutName ( name ) {
4
+ const
5
+ upperCase = name.toUpperCase ()
6
+ , regex = /FORM\s*\:/i
7
+ , isKeyboardShortcut = regex.test ( upperCase )
8
+ , sliceIndex = upperCase.indexOf ( ':' )
9
+ ;
10
+
11
+ if ( !isKeyboardShortcut ) return name
12
+ let shortcut = upperCase.slice(sliceIndex+1).trim ()
13
+
14
+ return `FORM:${shortcut}`
15
+ } // _normalizeShortcutName func.
16
+
17
+
18
+
19
+ export default _normalizeShortcutName
20
+
21
+
@@ -0,0 +1,71 @@
1
+ 'use strict'
2
+
3
+ function _registerShortcutEvents ( dependencies, pluginState ) {
4
+ const
5
+ { regex, _defaults, ev } = dependencies
6
+ , {
7
+ currentContext : { name: contextName }
8
+ , shortcuts
9
+ , callbacks
10
+ } = pluginState
11
+ ;
12
+ let watch=[], define=[], action=[];
13
+ if ( contextName == null ) return false
14
+ Object.entries ( shortcuts[contextName] ).forEach ( ([shortcutName, list ]) => {
15
+ let isFormEv = regex.test ( shortcutName );
16
+ if ( !isFormEv ) return
17
+ if ( shortcutName === 'FORM:WATCH' ) watch = list
18
+ if ( shortcutName === 'FORM:DEFINE' ) define = list
19
+ if ( shortcutName === 'FORM:ACTION' ) action = list
20
+ })
21
+
22
+ if ( action.length === 0 ) return false
23
+
24
+ let setTypes = new Set ();
25
+ if ( define.length === 0 ) define = [ _defaults.define ]
26
+ if ( watch.length === 0 ) watch = [ _defaults.watch ]
27
+ let watchList = watch.map ( el => el() )
28
+ .reduce ( ( res, el) => {
29
+ res.push ( el )
30
+ return res
31
+ }, [])
32
+ pluginState.watchList = document.querySelectorAll ( watchList )
33
+ pluginState.watchList.forEach ( el => setTypes.add (define[0](el)) )
34
+
35
+ pluginState.typeFn = define[0] ? define[0] : _defaults.define
36
+ action.forEach ( act => {
37
+
38
+ if ( !(act instanceof Function)) {
39
+ console.warn ( `Warning: The 'form:action' should be a function.` )
40
+ return false
41
+ }
42
+ let list = act ()
43
+ if ( !(list instanceof Array) ) {
44
+ console.warn ( `Warning: The 'form:action' function should RETURN an array.` )
45
+ return false
46
+ }
47
+ act().forEach ( ({fn, type, timing, wait=0 }) => {
48
+ if ( setTypes.has ( type) && fn instanceof Function ) {
49
+ let key = `${type}/${timing}`
50
+ const hasProperty = callbacks.hasOwnProperty ( key );
51
+ hasProperty ?
52
+ callbacks[key].push ( fn ) :
53
+ callbacks[key] = [ fn ]
54
+ if ( !hasProperty ) {
55
+ ev.on ( key, ( props, callbacks ) => { // Register the 'type/timing' as an event
56
+ callbacks.forEach ( cb => { if ( cb instanceof Function ) cb ( props ) })
57
+ })
58
+ }
59
+ } // if function
60
+ if ( timing === 'instant' ) pluginState.wait[`${type}`] = wait
61
+ }) // for each act
62
+ }) // for each action
63
+ if ( Object.keys(pluginState.callbacks).length > 0 ) return true
64
+ else return false
65
+ } // _registerShortcutEvents func.
66
+
67
+
68
+
69
+ export default _registerShortcutEvents
70
+
71
+
@@ -0,0 +1,84 @@
1
+ 'use strict'
2
+
3
+ // import all plugin files here
4
+ import _listenDOM from './_listenDOM.js'
5
+ import _normalizeShortcutName from './_normalizeShortcutName.js'
6
+ import _registerShortcutEvents from './_registerShortcutEvents.js'
7
+ import _defaults from './_defaults.js'
8
+
9
+
10
+
11
+ function pluginForm ( dependencies, state, options ) {
12
+ /**
13
+ * 'form: watch' - A function. Should return a string. Define a selection that will be watched for changes. example: 'input, select.color, textarea, #name'
14
+ * 'form: define' - A function that receives every watched element. Should return text value that represents the type of the
15
+ * element according custom specification. Types could be specific for every single form.
16
+ * 'form:action' - List of Callback objects.
17
+ * Callback definition object:
18
+ * {
19
+ * fn: function to be called
20
+ * type: fn should be executed on type of the element
21
+ * timing: 'in' | 'out' | 'instant' - when to execute the function
22
+ * wait: time in milliseconds to wait before executing the function again. Works only with mode 'instant'.
23
+ * }
24
+ */
25
+
26
+ let
27
+ { currentContext, shortcuts } = state
28
+ , { inAPI } = dependencies
29
+ , deps = {
30
+ ev: dependencies.ev
31
+ , mainDependencies : dependencies
32
+ , regex : /FORM\s*\:/i
33
+ , _defaults
34
+ }
35
+ , pluginState = {
36
+ currentContext
37
+ , shortcuts
38
+ , callbacks : {} // Functions callbacks arranged by 'type/timing' : [ callback, ...otherCallbacks ]
39
+ , typeFn : '' // Type definition function
40
+ , watchList : [] // list of watched elements
41
+ , wait : {} // wait time for 'instant' mode
42
+ } // pluginState
43
+ ;
44
+ function resetState () {
45
+ pluginState.callbacks = {}
46
+ pluginState.typeFn = ''
47
+ pluginState.watchList = []
48
+ pluginState.wait = {}
49
+ } // resetState func.
50
+
51
+ // Read shortcuts names from all context entities and normalize entries related to the plugin
52
+ inAPI._normalizeWithPlugins ( _normalizeShortcutName )
53
+ let formListener = _listenDOM ( deps, pluginState );
54
+
55
+ if ( currentContext.name ) {
56
+ let hasFormShortcuts = _registerShortcutEvents ( deps, pluginState )
57
+ if ( hasFormShortcuts ) formListener.start ()
58
+ }
59
+
60
+ let pluginAPI = {
61
+ getPrefix : ( ) => 'form'
62
+ , shortcutName : key => { // Format a key string according plugin needs
63
+ return _normalizeShortcutName ( key )
64
+ }
65
+ , contextChange : ( ) => {
66
+ resetState ()
67
+ let hasFormShortcuts = _registerShortcutEvents ( deps, pluginState )
68
+ if ( hasFormShortcuts ) formListener.start ()
69
+ else formListener.stop ()
70
+ }
71
+ , mute : () => formListener.stop ()
72
+ , unmute : () => formListener.start ()
73
+ , destroy : () => {
74
+ formListener.stop ()
75
+ pluginState = null
76
+ pluginAPI = null
77
+ }
78
+ }; // pluginAPI
79
+ Object.freeze ( pluginAPI )
80
+ return pluginAPI
81
+ } // pluginForm func.
82
+
83
+
84
+ export default pluginForm
@@ -8,6 +8,7 @@ import '../test-components/style.css'
8
8
  import {
9
9
  pluginClick,
10
10
  pluginKey
11
+ , pluginForm
11
12
  , shortcuts
12
13
  } from '../src/main.js'
13
14
  import { expect } from 'chai'
@@ -35,6 +36,17 @@ short.load ({
35
36
  , extra : {
36
37
  'key : p,r,o,b,a': () => b = true
37
38
  }
39
+ , extend : {
40
+ 'form : watch' : () => 'input'
41
+ , 'form : define' : () => 'input'
42
+ , 'form : action' : () => [
43
+ {
44
+ fn : (e) => console.log ( e.target )
45
+ , type : 'input'
46
+ , mode : 'in'
47
+ }
48
+ ]
49
+ }
38
50
  })
39
51
 
40
52
  beforeEach ( () => {
@@ -245,7 +257,7 @@ test ( 'List shortcuts', () => {
245
257
  let all = short.listShortcuts ();
246
258
  expect ( all ).to.be.an ( 'array' )
247
259
 
248
- expect ( all ).to.have.lengthOf ( 2 )
260
+ expect ( all ).to.have.lengthOf ( 3 )
249
261
  expect ( all[0] ).to.have.property ( 'context' )
250
262
  expect ( all[0] ).to.have.property ( 'shortcuts' )
251
263
  expect ( all[0].shortcuts ).to.be.an('array')
@@ -3,6 +3,8 @@ return <>
3
3
  <div className="block" data-click="red"><span id='rspan'>Red</span> </div>
4
4
  <button className="big-btn" data-click="mega">Mega button</button>
5
5
  <p>Some text with <a href="#" target="_blank">link <span id="anchor">in</span></a> it</p>
6
+ <p><input id="name" type="text" /></p>
7
+ <p><input type="text" id="age" /></p>
6
8
  </>
7
9
  }
8
10