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

divio / django-cms / #30120

14 Nov 2025 01:00AM UTC coverage: 89.921%. Remained the same
#30120

push

travis-ci

web-flow
Merge 73415788f into c38b75715

1333 of 2144 branches covered (62.17%)

416 of 567 new or added lines in 27 files covered. (73.37%)

421 existing lines in 12 files now uncovered.

9207 of 10239 relevant lines covered (89.92%)

11.18 hits per line

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

27.59
/cms/static/cms/js/modules/trap.js
1

2

3
// ES6 Focus Trap module without jQuery, basoed on jquery.trap.js
4
// Copyright (c) 2011, 2012 Julien Wajsberg, 2025 Fabian Braun
5

6
const DATA_ISTRAPPING_KEY = '__trap_isTrapping';
1✔
7

8
/**
9
 * Returns all visible, focusable elements inside a container.
10
 * Focusable elements include links, buttons, inputs, textareas, selects, and elements with tabindex.
11
 * Elements must be visible (offsetParent !== null).
12
 * Sorted by tabIndex (positive first, then normal order).
13
 *
14
 * @param {HTMLElement} container - The container to search within.
15
 * @returns {HTMLElement[]} Array of focusable elements.
16
 */
17
function getFocusableElementsInContainer(container) {
NEW
18
    const elements = Array.from(container.querySelectorAll(
×
19
        'a[href], link[href], [draggable="true"], [contenteditable="true"], input:not([disabled]), ' +
20
        'button:not([disabled]), textarea:not([disabled]), select:not([disabled]), [tabindex], summary'
NEW
21
    )).filter(el => el.offsetParent !== null);
×
22

23
    // Sort by tabIndex: positive first, then normal
NEW
24
    const normal = elements.filter(el => !el.tabIndex || el.tabIndex === 0).map((v, i) => ({ v, t: 0, i }));
×
NEW
25
    const special = elements.filter(el => el.tabIndex > 0).map((v, i) => ({ v, t: v.tabIndex, i: i + normal.length }));
×
NEW
26
    const all = [...normal, ...special].sort((a, b) => (a.t - b.t) || (a.i - b.i));
×
27

NEW
28
    return all.map(obj => obj.v);
×
29
}
30

31
/**
32
 * Handles Tab and Shift+Tab key events to keep focus inside the container.
33
 * Moves focus to the next or previous focusable element.
34
 *
35
 * @param {HTMLElement} container - The container element.
36
 * @param {HTMLElement} elt - The currently focused element.
37
 * @param {boolean} goReverse - If true, move backwards (Shift+Tab).
38
 * @returns {boolean} True if tab event was processed, false otherwise.
39
 */
40
function processTab(container, elt, goReverse) {
NEW
41
    const focussable = getFocusableElementsInContainer(container);
×
42

NEW
43
    setTimeout(() => {
×
NEW
44
        const newFocus = container.ownerDocument.activeElement;
×
45

NEW
46
        if (newFocus === null || newFocus === container.ownerDocument.body || !container.contains(newFocus)) {
×
NEW
47
            if (goReverse) {
×
NEW
48
                try {
×
NEW
49
                    focussable[focussable.length - 1].focus();
×
50
                } catch {}
51
            } else {
NEW
52
                try {
×
NEW
53
                    focussable[0].focus();
×
54
                } catch {}
55
            }
56
        }
57
    }, 0);
58
}
59

60
/**
61
 * Keydown event handler for trapping focus.
62
 * Only processes Tab key events.
63
 *
64
 * @param {KeyboardEvent} e
65
 * @private
66
 */
67
function onKeyPress(e) {
NEW
68
    if (e.key === 'Tab') {
×
NEW
69
        const container = e.currentTarget;
×
NEW
70
        const goReverse = !!e.shiftKey;
×
71

NEW
72
        processTab(container, container.ownerDocument.activeElement, goReverse);
×
73
    }
74
}
75

76
/**
77
 * Enables focus trap for a given element. Focus will cycle within the element on Tab/Shift+Tab.
78
 *
79
 * @param {HTMLElement} element - The element to trap focus inside.
80
 */
81
export function trap(element) {
82
    if (!element) {
56✔
83
        return;
3✔
84
    }
85
    element.addEventListener('keydown', onKeyPress);
53✔
86
    element[DATA_ISTRAPPING_KEY] = true;
53✔
87
}
88

89
/**
90
 * Disables focus trap for a given element. Removes the keydown event handler.
91
 *
92
 * @param {HTMLElement} element - The element to remove focus trap from.
93
 */
94
export function untrap(element) {
95
    if (!element) {
9!
NEW
96
        return;
×
97
    }
98
    element.removeEventListener('keydown', onKeyPress);
9✔
99
    delete element[DATA_ISTRAPPING_KEY];
9✔
100
}
101

102
/**
103
 * Checks if focus trap is currently enabled for the given element.
104
 *
105
 * @param {HTMLElement} element - The element to check.
106
 * @returns {boolean} True if focus trap is active, false otherwise.
107
 */
108
export function isTrapping(element) {
NEW
109
    return !!(element && element[DATA_ISTRAPPING_KEY]);
×
110
}
111

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