| 1 | /* A modal dialog owns the keyboard. |
| 2 | * |
| 3 | * A native modal <dialog> makes the page behind it inert to the pointer |
| 4 | * and to focus, but not to key events: a keydown on the dialog's own |
| 5 | * button still travels to document and window, where commands.js, |
| 6 | * clipboard.js and the vendored grid all listen. So with the Keyboard |
| 7 | * shortcuts dialog open, Ctrl+D filled the selection down behind it and |
| 8 | * a letter key started an edit in a cell nobody could see — persisted |
| 9 | * writes made from a dialog (Astra, #39 finding 1). |
| 10 | * |
| 11 | * So while any modal dialog is open, key and clipboard events stop here, |
| 12 | * at the window in the capture phase, before any page listener hears |
| 13 | * them. Stopping propagation is not preventing the default: Tab still |
| 14 | * moves inside the dialog, Enter and Space still press its buttons, |
| 15 | * typing still reaches its fields and Escape still closes it. A dialog |
| 16 | * that needs key handling of its own adds it on the dialog element in |
| 17 | * the capture phase. |
| 18 | * |
| 19 | * Loaded first of the page's scripts, so its listener is registered |
| 20 | * before any other window listener. General on purpose: it is about |
| 21 | * every modal on every page, not about one dialog. */ |
| 22 | (function () { |
| 23 | 'use strict'; |
| 24 | |
| 25 | function modalOpen() { |
| 26 | try { |
| 27 | return !!document.querySelector('dialog:modal'); |
| 28 | } catch (e) { |
| 29 | return !!document.querySelector('dialog[open]'); |
| 30 | } |
| 31 | } |
| 32 | |
| 33 | ['keydown', 'keypress', 'keyup', 'beforeinput', 'paste', 'cut', 'copy'].forEach(function (type) { |
| 34 | window.addEventListener(type, function (e) { |
| 35 | if (modalOpen()) e.stopImmediatePropagation(); |
| 36 | }, true); |
| 37 | }); |
| 38 | }()); |
| 39 | |