• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

divio / django-cms / #30167

24 Nov 2025 12:49PM UTC coverage: 89.964% (+14.8%) from 75.132%
#30167

push

travis-ci

web-flow
Merge 565a22220 into 5f080488d

1337 of 2146 branches covered (62.3%)

9206 of 10233 relevant lines covered (89.96%)

11.2 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

58.18
/cms/static/cms/js/modules/keyboard.js
1
// Minimal Keyboard-Modul mit Kontext und Event-Registrierung
2

3
/* global KeyboardEvent */
4
const DEFAULT_CONTEXT = 'cms';
1✔
5
const contexts = {
1✔
6
    DEFAULT_CONTEXT: {}
7
};
8
let lastKey = null;
1✔
9

10
function isInputFocused() {
11
    const activeElement = document.activeElement;
2✔
12

13
    if (!activeElement) {
2!
14
        return false;
×
15
    }
16
    const tagName = activeElement.tagName.toLowerCase();
2✔
17

18
    return tagName === 'input' || tagName === 'textarea' || tagName === 'select' || activeElement.isContentEditable;
2✔
19
}
20

21
function setContext(ctx) {
22
    document.documentElement.dataset.cmsKbContext = ctx;
145✔
23
}
24

25
function getContext() {
26
    return document.documentElement.dataset.cmsKbContext || DEFAULT_CONTEXT;
235✔
27
}
28

29
function bind(key, callback, ctx = null) {
194✔
30
    if (Array.isArray(key)) {
194!
31
        key.forEach(k => bind(k, callback, ctx));
×
32
        return;
×
33
    }
34
    const context = ctx || getContext();
194✔
35

36
    if (!contexts[context]) {
194✔
37
        contexts[context] = {};
1✔
38
    }
39
    contexts[context][key] = callback;
194✔
40
}
41

42
function unbind(key, ctx = null) {
×
43
    if (Array.isArray(key)) {
×
44
        key.forEach(k => unbind(k, ctx));
×
45
        return;
×
46
    }
47
    const context = ctx || getContext();
×
48

49
    if (contexts[context]) {
×
50
        delete contexts[context][key];
×
51
    }
52
}
53

54
function toKeyCode(event) {
55
    const isLetter = /^Key[a-zA-Z]$/.test(event.code) || event.code === 'Space' || event.code === 'Enter';
1✔
56
    let key = /^Key[a-zA-Z]$/.test(event.code) ? event.code.slice(-1) : event.key;
1!
57

58
    if (event.code === 'Space') {
1!
59
        key = 'space';
×
60
    }
61
    if (isLetter) {
1!
62
        if (event.altKey) {
×
63
            key = `alt+${key}`;
×
64
        }
65
        if (event.ctrlKey) {
×
66
            key = `ctrl+${key}`;
×
67
        }
68
        if (event.shiftKey) {
×
69
            key = `shift+${key}`;
×
70
        }
71
    }
72
    return key.toLowerCase();
1✔
73
}
74

75
function handleKeydown(event) {
76
    if (isInputFocused()) {
2✔
77
        return;
1✔
78
    }
79
    const context = contexts[getContext()];
1✔
80
    const key = toKeyCode(event);
1✔
81

82
    if (context) {
1!
83
        if (context[key]) {
1!
84
            context[key](event);
1✔
85
        } else if (lastKey) {
×
86
            const comboKey = `${lastKey} > ${key}`;
×
87

88
            if (context[comboKey]) {
×
89
                context[comboKey](event);
×
90
            }
91
        }
92
    }
93
    lastKey = null;
1✔
94
}
95

96
function handleKeyup(event) {
97
    if (isInputFocused()) {
×
98
        return;
×
99
    }
100
    lastKey = toKeyCode(event);
×
101
}
102

103
function pressKey(key) {
104
    const event = new KeyboardEvent('keydown', { key });
2✔
105

106
    handleKeydown(event);
2✔
107
}
108

109
window.addEventListener('keydown', handleKeydown);
1✔
110
window.addEventListener('keyup', handleKeyup);
1✔
111

112
const keyboard = {
1✔
113
    setContext,
114
    getContext,
115
    bind,
116
    unbind,
117
    pressKey
118
};
119

120
export default keyboard;
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc