Skip to content
Permalink

Comparing changes

Choose two branches to see what’s changed or to start a new pull request. If you need to, you can also or learn more about diff comparisons.

Open a pull request

Create a new pull request by comparing changes across two branches. If you need to, you can also . Learn more about diff comparisons here.
base repository: jaywcjlove/hotkeys-js
Failed to load repositories. Confirm that selected base ref is valid, then try again.
Loading
base: v3.13.3
Choose a base ref
...
head repository: jaywcjlove/hotkeys-js
Failed to load repositories. Confirm that selected head ref is valid, then try again.
Loading
compare: master
Choose a head ref

Commits on Jan 13, 2024

  1. fix: Keyboard event memory leak (#469)

    * fix: Keyboard event memory leak
    
    * fix: add dist
    qiongshusheng authored Jan 13, 2024
    Copy the full SHA
    816d76d View commit details
  2. released v3.13.4 #469

    jaywcjlove committed Jan 13, 2024
    Copy the full SHA
    5b0cd1f View commit details

Commits on Jan 14, 2024

  1. website: update example.

    jaywcjlove committed Jan 14, 2024
    Copy the full SHA
    7f389ce View commit details
  2. Copy the full SHA
    bb1046c View commit details
  3. test: fix test case issue.

    jaywcjlove committed Jan 14, 2024
    Copy the full SHA
    f2e1ab1 View commit details
  4. released v3.13.5 #470

    jaywcjlove committed Jan 14, 2024
    Copy the full SHA
    adc70eb View commit details

Commits on Feb 2, 2024

  1. fix: fix filter func (#473)

    do not ignore several types of INPUT
    rockindolphin authored Feb 2, 2024
    Copy the full SHA
    4fc526d View commit details
  2. Copy the full SHA
    99025bb View commit details
  3. released v3.13.6 #473

    jaywcjlove committed Feb 2, 2024
    Copy the full SHA
    aa2f124 View commit details

Commits on Feb 7, 2024

  1. Copy the full SHA
    1e802bd View commit details
  2. released v3.13.7 (#475)

    jaywcjlove committed Feb 7, 2024
    Copy the full SHA
    a595c55 View commit details

Commits on Jun 23, 2024

  1. Copy the full SHA
    b003877 View commit details

Commits on Dec 6, 2024

  1. Copy the full SHA
    a067370 View commit details

Commits on Dec 7, 2024

  1. Copy the full SHA
    d5d0aef View commit details
  2. Copy the full SHA
    5d39bf3 View commit details
  3. test: update test case.

    jaywcjlove committed Dec 7, 2024
    Copy the full SHA
    2fa546a View commit details
  4. revert(chore): downgrade puppeteer dependency to ~13.7.0

    Reverting update #356 as it caused compatibility issues with the current project setup.
    jaywcjlove committed Dec 7, 2024
    Copy the full SHA
    29e7bc2 View commit details

Commits on Dec 11, 2024

  1. Copy the full SHA
    0dd7438 View commit details
  2. released v3.13.8 #502

    jaywcjlove committed Dec 11, 2024
    Copy the full SHA
    335c15c View commit details

Commits on Dec 12, 2024

  1. Copy the full SHA
    bcdd3f6 View commit details
  2. released v3.13.9 #504

    jaywcjlove committed Dec 12, 2024
    Copy the full SHA
    6c0b6a6 View commit details
Showing with 8,496 additions and 7,420 deletions.
  1. +3 −8 .github/FUNDING.yml
  2. +5 −4 .github/workflows/ci.yml
  3. +31 −0 .github/workflows/pr.yml
  4. +121 −34 dist/hotkeys.common.js
  5. +2 −2 dist/hotkeys.common.min.js
  6. +121 −34 dist/hotkeys.esm.js
  7. +121 −34 dist/hotkeys.js
  8. +2 −2 dist/hotkeys.min.js
  9. +7,887 −7,160 package-lock.json
  10. +1 −1 package.json
  11. +88 −44 src/index.js
  12. +9 −1 src/utils.js
  13. +15 −0 test/run.test.js
  14. +90 −96 website/App.js
11 changes: 3 additions & 8 deletions .github/FUNDING.yml
Original file line number Diff line number Diff line change
@@ -1,8 +1,3 @@
# These are supported funding model platforms

# github: jaywcjlove
# patreon: # Replace with a single Patreon username
# open_collective: # Replace with a single Open Collective username
# ko_fi: # Replace with a single Ko-fi username
# tidelift: # npm/hotkeys-js
custom: https://jaywcjlove.github.io/#/sponsor
ko_fi: jaywcjlove
buy_me_a_coffee: jaywcjlove
custom: ["https://www.paypal.me/kennyiseeyou", "https://jaywcjlove.github.io/#/sponsor"]
9 changes: 5 additions & 4 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -16,10 +16,10 @@ jobs:
contents: write
id-token: write
steps:
- uses: actions/checkout@v3
- uses: actions/setup-node@v3
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: 18
node-version: 20
registry-url: 'https://registry.npmjs.org'

# - name: Cache dependencies
@@ -59,7 +59,7 @@ jobs:
uses: jaywcjlove/changelog-generator@main

- name: Build and Deploy
uses: peaceiris/actions-gh-pages@v3
uses: peaceiris/actions-gh-pages@v4
with:
commit_message: ${{steps.tag_version.outputs.tag}} ${{ github.event.head_commit.message }}
github_token: ${{ secrets.GITHUB_TOKEN }}
@@ -82,6 +82,7 @@ jobs:
uses: ncipollo/release-action@v1
if: steps.create_tag.outputs.successful
with:
allowUpdates: true
token: ${{ secrets.GITHUB_TOKEN }}
name: ${{ steps.create_tag.outputs.version }}
tag: ${{ steps.create_tag.outputs.version }}
31 changes: 31 additions & 0 deletions .github/workflows/pr.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
name: CI-PR
on:
pull_request:

env:
SKIP_PREFLIGHT_CHECK: true

jobs:
build-deploy:
runs-on: ubuntu-latest
permissions:
contents: write
id-token: write
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: 20
registry-url: 'https://registry.npmjs.org'

- name: Look Changelog
uses: jaywcjlove/changelog-generator@main
with:
token: ${{ secrets.GITHUB_TOKEN }}
filter-author: (jaywcjlove|小弟调调™|dependabot\[bot\]|Renovate Bot)
filter: (^[\s]+?[R|r]elease)|(^[R|r]elease)

- run: npm install
- run: npm run build
- run: npm run test

155 changes: 121 additions & 34 deletions dist/hotkeys.common.js
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
/**!
* hotkeys-js v3.13.3
* hotkeys-js v3.13.7
* A simple micro-library for defining and dispatching keyboard shortcuts. It has no dependencies.
*
* Copyright (c) 2023 kenny wong <wowohoo@qq.com>
* Copyright (c) 2024 kenny wong <wowohoo@qq.com>
* https://github.com/jaywcjlove/hotkeys-js.git
*
* @website: https://jaywcjlove.github.io/hotkeys-js
@@ -19,9 +19,14 @@ function addEvent(object, event, method, useCapture) {
if (object.addEventListener) {
object.addEventListener(event, method, useCapture);
} else if (object.attachEvent) {
object.attachEvent("on".concat(event), () => {
method(window.event);
});
object.attachEvent("on".concat(event), method);
}
}
function removeEvent(object, event, method, useCapture) {
if (object.removeEventListener) {
object.removeEventListener(event, method, useCapture);
} else if (object.detachEvent) {
object.detachEvent("on".concat(event), method);
}
}

@@ -156,9 +161,9 @@ for (let k = 1; k < 20; k++) {
}

let _downKeys = []; // 记录摁下的绑定键
let winListendFocus = false; // window是否已经监听了focus事件
let winListendFocus = null; // window是否已经监听了focus事件
let _scope = 'all'; // 默认热键范围
const elementHasBindEvent = []; // 已绑定事件的节点记录
const elementEventMap = new Map(); // 已绑定事件的节点记录

// 返回键码
const code = x => _keyMap[x.toLowerCase()] || _modifier[x.toLowerCase()] || x.toUpperCase().charCodeAt(0);
@@ -209,8 +214,9 @@ function filter(event) {
tagName
} = target;
let flag = true;
const isInput = tagName === 'INPUT' && !['checkbox', 'radio', 'range', 'button', 'file', 'reset', 'submit', 'color'].includes(target.type);
// ignore: isContentEditable === 'true', <input> and <textarea> when readOnly state is false, <select>
if (target.isContentEditable || (tagName === 'INPUT' || tagName === 'TEXTAREA' || tagName === 'SELECT') && !target.readOnly) {
if (target.isContentEditable || (isInput || tagName === 'TEXTAREA' || tagName === 'SELECT') && !target.readOnly) {
flag = false;
}
return flag;
@@ -235,7 +241,17 @@ function deleteScope(scope, newScope) {
if (Object.prototype.hasOwnProperty.call(_handlers, key)) {
handlers = _handlers[key];
for (i = 0; i < handlers.length;) {
if (handlers[i].scope === scope) handlers.splice(i, 1);else i++;
if (handlers[i].scope === scope) {
const deleteItems = handlers.splice(i, 1);
deleteItems.forEach(_ref2 => {
let {
element
} = _ref2;
return removeKeyEvent(element);
});
} else {
i++;
}
}
}
}
@@ -270,7 +286,11 @@ function clearModifier(event) {
function unbind(keysInfo) {
// unbind(), unbind all keys
if (typeof keysInfo === 'undefined') {
Object.keys(_handlers).forEach(key => delete _handlers[key]);
Object.keys(_handlers).forEach(key => {
Array.isArray(_handlers[key]) && _handlers[key].forEach(info => eachUnbind(info));
delete _handlers[key];
});
removeKeyEvent(null);
} else if (Array.isArray(keysInfo)) {
// support like : unbind([{key: 'ctrl+a', scope: 's1'}, {key: 'ctrl-a', scope: 's2', splitKey: '-'}])
keysInfo.forEach(info => {
@@ -300,13 +320,13 @@ function unbind(keysInfo) {
}

// 解除绑定某个范围的快捷键
const eachUnbind = _ref2 => {
const eachUnbind = _ref3 => {
let {
key,
scope,
method,
splitKey = '+'
} = _ref2;
} = _ref3;
const multipleKeys = getKeys(key);
multipleKeys.forEach(originKey => {
const unbindKeys = originKey.split(splitKey);
@@ -317,11 +337,15 @@ const eachUnbind = _ref2 => {
// 判断是否传入范围,没有就获取范围
if (!scope) scope = getScope();
const mods = len > 1 ? getMods(_modifier, unbindKeys) : [];
const unbindElements = [];
_handlers[keyCode] = _handlers[keyCode].filter(record => {
// 通过函数判断,是否解除绑定,函数相等直接返回
const isMatchingMethod = method ? record.method === method : true;
return !(isMatchingMethod && record.scope === scope && compareArray(record.mods, mods));
const isUnbind = isMatchingMethod && record.scope === scope && compareArray(record.mods, mods);
if (isUnbind) unbindElements.push(record.element);
return !isUnbind;
});
unbindElements.forEach(element => removeKeyEvent(element));
});
};

@@ -445,10 +469,12 @@ function dispatch(event, element) {
}
// key 不在 _handlers 中返回
if (!(key in _handlers)) return;
for (let i = 0; i < _handlers[key].length; i++) {
if (event.type === 'keydown' && _handlers[key][i].keydown || event.type === 'keyup' && _handlers[key][i].keyup) {
if (_handlers[key][i].key) {
const record = _handlers[key][i];
const handlerKey = _handlers[key];
const keyLen = handlerKey.length;
for (let i = 0; i < keyLen; i++) {
if (event.type === 'keydown' && handlerKey[i].keydown || event.type === 'keyup' && handlerKey[i].keyup) {
if (handlerKey[i].key) {
const record = handlerKey[i];
const {
splitKey
} = record;
@@ -465,11 +491,6 @@ function dispatch(event, element) {
}
}
}

// 判断 element 是否已经绑定事件
function isElementBind(element) {
return elementHasBindEvent.indexOf(element) > -1;
}
function hotkeys(key, option, method) {
_downKeys = [];
const keys = getKeys(key); // 需要处理的快捷键列表
@@ -528,21 +549,35 @@ function hotkeys(key, option, method) {
});
}
// 在全局document上设置快捷键
if (typeof element !== 'undefined' && !isElementBind(element) && window) {
elementHasBindEvent.push(element);
addEvent(element, 'keydown', e => {
dispatch(e, element);
}, capture);
if (typeof element !== 'undefined' && window) {
if (!elementEventMap.has(element)) {
const keydownListener = function () {
let event = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : window.event;
return dispatch(event, element);
};
const keyupListenr = function () {
let event = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : window.event;
dispatch(event, element);
clearModifier(event);
};
elementEventMap.set(element, {
keydownListener,
keyupListenr,
capture
});
addEvent(element, 'keydown', keydownListener, capture);
addEvent(element, 'keyup', keyupListenr, capture);
}
if (!winListendFocus) {
winListendFocus = true;
addEvent(window, 'focus', () => {
const listener = () => {
_downKeys = [];
}, capture);
};
winListendFocus = {
listener,
capture
};
addEvent(window, 'focus', listener, capture);
}
addEvent(element, 'keyup', e => {
dispatch(e, element);
clearModifier(e);
}, capture);
}
}
function trigger(shortcut) {
@@ -556,6 +591,58 @@ function trigger(shortcut) {
});
});
}

// 销毁事件,unbind之后判断element上是否还有键盘快捷键,如果没有移除监听
function removeKeyEvent(element) {
const values = Object.values(_handlers).flat();
const findindex = values.findIndex(_ref4 => {
let {
element: el
} = _ref4;
return el === element;
});
if (findindex < 0) {
const {
keydownListener,
keyupListenr,
capture
} = elementEventMap.get(element) || {};
if (keydownListener && keyupListenr) {
removeEvent(element, 'keyup', keyupListenr, capture);
removeEvent(element, 'keydown', keydownListener, capture);
elementEventMap.delete(element);
}
}
if (values.length <= 0 || elementEventMap.size <= 0) {
// 移除所有的元素上的监听
const eventKeys = Object.keys(elementEventMap);
eventKeys.forEach(el => {
const {
keydownListener,
keyupListenr,
capture
} = elementEventMap.get(el) || {};
if (keydownListener && keyupListenr) {
removeEvent(el, 'keyup', keyupListenr, capture);
removeEvent(el, 'keydown', keydownListener, capture);
elementEventMap.delete(el);
}
});
// 清空 elementEventMap
elementEventMap.clear();
// 清空 _handlers
Object.keys(_handlers).forEach(key => delete _handlers[key]);
// 移除window上的focus监听
if (winListendFocus) {
const {
listener,
capture
} = winListendFocus;
removeEvent(window, 'focus', listener, capture);
winListendFocus = null;
}
}
}
const _api = {
getPressedKeyString,
setScope,
Loading