diff --git a/src/widgets/Dialogs.js b/src/widgets/Dialogs.js index cfeececc72c..4c3bf7e9b76 100644 --- a/src/widgets/Dialogs.js +++ b/src/widgets/Dialogs.js @@ -51,6 +51,14 @@ define(function (require, exports, module) { DIALOG_ID_EXT_CHANGED = "ext-changed-dialog", DIALOG_ID_EXT_DELETED = "ext-deleted-dialog", DIALOG_ID_LIVE_DEVELOPMENT = "live-development-error-dialog"; + + /** + * A stack of keydown event handler functions that corresponds to the + * current stack of modal dialogs. + * + * @type {Array.} + */ + var _keydownListeners = []; function _dismissDialog(dlg, buttonId) { dlg.data("buttonId", buttonId); @@ -71,7 +79,9 @@ define(function (require, exports, module) { var inFormField = ($(e.target).filter(":input").length > 0), inTextArea = (e.target.tagName === "TEXTAREA"); - if (e.which === KeyEvent.DOM_VK_RETURN && !inTextArea) { // enter key in single-line text input still dismisses + if (e.which === KeyEvent.DOM_VK_ESCAPE) { + buttonId = DIALOG_BTN_CANCEL; + } else if (e.which === KeyEvent.DOM_VK_RETURN && !inTextArea) { // enter key in single-line text input still dismisses // Click primary button primaryBtn.click(); } else if (e.which === KeyEvent.DOM_VK_SPACE) { @@ -177,7 +187,22 @@ define(function (require, exports, module) { // Remove keydown event handler window.document.body.removeEventListener("keydown", handleKeyDown, true); - KeyBindingManager.setEnabled(true); + + // Remove this dialog's listener from the stack of listeners. (It + // might not be on the top of the stack if the dialogs are somehow + // dismissed out of order.) + _keydownListeners = _keydownListeners.filter(function (listener) { + return listener !== handleKeyDown; + }); + + // Restore the previous listener if there was one; otherwise return + // control to the KeyBindingManager. + if (_keydownListeners.length > 0) { + var previousListener = _keydownListeners[_keydownListeners.length - 1]; + window.document.body.addEventListener("keydown", previousListener, true); + } else { + KeyBindingManager.setEnabled(true); + } }).one("shown", function () { // Set focus to the default button var primaryBtn = $dlg.find(".primary"); @@ -186,9 +211,18 @@ define(function (require, exports, module) { primaryBtn.focus(); } - // Listen for dialog keyboard shortcuts + // If this is the only dialog then also disable the KeyBindingManager; + // otherwise, remove and the previous dialog's listener before installing the new one. + if (_keydownListeners.length > 0) { + var previousListener = _keydownListeners[_keydownListeners.length - 1]; + window.document.body.removeEventListener("keydown", previousListener, true); + } else { + KeyBindingManager.setEnabled(false); + } + _keydownListeners.push(handleKeyDown); + + // Handle this dialog's keyboard events with the current listener window.document.body.addEventListener("keydown", handleKeyDown, true); - KeyBindingManager.setEnabled(false); }); // Click handler for buttons @@ -202,7 +236,7 @@ define(function (require, exports, module) { $dlg.modal({ backdrop: "static", show: true, - keyboard: autoDismiss + keyboard: false // handle the ESC key ourselves so we can deal with nested dialogs }); return promise;