FoxitPDFSDKforWeb v11.0.2
Foxit PDF SDK for Web
/builds/foxit/phantom/cloud/phantompdf_online/foxitwebsdk/doxygen/workspace/headerfiles/shared/Shortcut.js

键盘快捷键绑定功能

Shortcut.register(dom, "ctrl+a", ()=>{ console.log("组合按键,需同时按下键盘") }) Shortcut.register(dom, "a,b,c", ()=>{ console.log("顺序按键,按顺序按下键盘") }) Shortcut.register(dom, ["ctrl+a", "ctrl+b"], ()=>{ console.log("多个组合按键") })

class Shortcut {
static getInstance () {
if (window.Shortcut) {
return window.Shortcut;
}
return window.Shortcut = new Shortcut();
}
constructor () {
this.events = {};
}
register (dom, key, callback) {
const keyArray = (key instanceof Array) ? key : [key];
for (let index = 0; index < keyArray.length; index++) {
this._addEventData(dom, keyArray[index], callback);
}
this._addKeydownEvent();
return this;
}
unregister (dom, key) {
let surplus = 0;
const keys = key ? (key instanceof Array ? key : [key]) : Object.keys(this.events);
for (let index = 0; index < keys.length; index++) {
surplus = this._removeEventData(dom, keys[index]);
}
if (surplus === 0) {
this.keydownEvent && this.keydownEvent.destroy();
this.keydownEvent = undefined;
this.mouseoverEvent && this.mouseoverEvent.destroy();
this.mouseoverEvent = undefined;
}
}
_addEvent (eventName, callback, dom) {
if (!eventName || !callback) return ;
const Dom = dom || document;
Dom.addEventListener(eventName, callback);
const destroy = () => {
Dom.removeEventListener(eventName, callback);
}
return { destroy };
}
_addKeydownEvent () {
if (this.keydownEvent) return ;
this.mouseoverEvent = this._addEvent('mouseover', e => {
this.mouseoverElement = e.target;
}, document);
this.keydownEvent = this._addEvent('keydown', e => {
const callback = this._matchEventData(this._getKeyFromEvent(e));
if (callback) {
callback(e);
}
});
}
_addEventData (dom, key, callback) {
if (!this.events[key]) {
this.events[key] = [];
}
const eventArray = this.events[key];
for (let index = 0; index < eventArray.length; index++) {
const event = eventArray[index];
if (event[0] === dom) {
console.error('Duplicate binding of shortcut key:', key, ', on dom:', dom);
return;
}
}
this.events[key].push([
dom, callback
]);
}
_removeEventData (dom, key) {
const eventArray = this.events[key] || [];
for (let index = 0; index < eventArray.length; index++) {
const event = eventArray[index];
if (event[0] === dom) {
eventArray.splice(index, 1);
if (eventArray.length === 0) {
delete this.events[key];
}
break;
}
}
return Object.keys(this.events).length;
}
_matchEventData (key) {
let ret;
const prevKey = this._getPrevKey();
const events = this.events;
const keyByPrev = prevKey ? `${prevKey},${key}` : '';
const search = (key) => {
if (!key) return null;
for (let name in events) {
if (this._isKeyEqual(name, key)) {
return events[name];
}
}
return null;
}
ret = search(key) || search(keyByPrev);
if (ret) {
this._removePrevKey();
} else {
this._addPrevKey(keyByPrev || key);
}
return this._matchEventCallback(ret);
}
_matchEventCallback (eventArray) {
if (!eventArray || eventArray.length === 0) return null;
// 过滤不存在的dom
let newEventArray = eventArray.filter(item => {
const Dom = item[0];
if (Dom === document) return true;
const rect = Dom.getBoundingClientRect();
const isVisible = rect.x + rect.y + rect.width + rect.height > 0;
return isVisible;
});
// dom按层级排序,上到下
eventArray.length > 1 && newEventArray.sort((next, current) => {
const Next = next[0];
const Current = current[0];
if (Next.contains(Current)) return -1;
else if (Current.contains(Next)) return 1;
return 0;
}).reverse();
let callback;
const elements = [document.activeElement, this.mouseoverElement];
for (let index = 0; index < elements.length; index++) {
const currentAtiveElement = elements[index];
if (!currentAtiveElement) continue;
const isCurrentAtiveElementEditable = ['INPUT', 'TEXTAREA'].includes(currentAtiveElement.nodeName) || currentAtiveElement.contentEditable === 'true';
for (let index = 0; index < newEventArray.length; index++) {
const eventItem = newEventArray[index];
const eventItemDom = eventItem[0];
const eventItemCallback = eventItem[1];
if (eventItemDom === currentAtiveElement) {
return eventItemCallback;
} else if (!isCurrentAtiveElementEditable && eventItemDom.contains(currentAtiveElement)) {
return eventItemCallback;
}
}
}
return callback;
}
_addPrevKey (key) {
this._removePrevKey();
this._prev_key = key;
this._prev_key_timer = setTimeout(() => {
this._removePrevKey();
}, 2000);
}
_getPrevKey () {
return this._prev_key;
}
_removePrevKey () {
this._prev_key = undefined;
this._prev_key_timer && clearTimeout(this._prev_key_timer);
this._prev_key_timer = undefined;
}
_getKeyFromEvent (e) {
const { key, keyCode, code, altKey, ctrlKey, shiftKey, metaKey } = e;
const array = [key];
if (altKey && key !== 'Alt') {
array.unshift('Alt');
}
if (ctrlKey && key !== 'Control') {
array.unshift('Ctrl');
}
if (shiftKey && key !== 'Shift') {
array.unshift('Shift');
}
if (metaKey && key !== 'Meta') {
array.unshift('Meta');
}
return array.join('+');
}
_isKeyEqual (key1, key2) {
key1 = key1.toLowerCase().replaceAll('control', 'ctrl').replaceAll('escape', 'esc');
key2 = key2.toLowerCase().replaceAll('control', 'ctrl').replaceAll('escape', 'esc');
if (key1.indexOf('+') > -1 && key2.indexOf('+') > -1) {
return key1.split('+').sort((a,b) => {
if (a.charCodeAt(0) > b.charCodeAt(0)) return -1;
else if (a.charCodeAt(0) < b.charCodeAt(0)) return 1;
return 0;
}).join('+') === key2.split('+').sort((a,b) => {
if (a.charCodeAt(0) > b.charCodeAt(0)) return -1;
else if (a.charCodeAt(0) < b.charCodeAt(0)) return 1;
return 0;
}).join('+');
}
return key1 === key2;
}
}
export default Shortcut.getInstance()

Foxit Software Corporation Logo
@2025 Foxit Software Incorporated. All rights reserved.