diff options
Diffstat (limited to 'toolkit/content/widgets/moz-input-box.js')
-rw-r--r-- | toolkit/content/widgets/moz-input-box.js | 224 |
1 files changed, 224 insertions, 0 deletions
diff --git a/toolkit/content/widgets/moz-input-box.js b/toolkit/content/widgets/moz-input-box.js new file mode 100644 index 0000000000..caddc3ffe0 --- /dev/null +++ b/toolkit/content/widgets/moz-input-box.js @@ -0,0 +1,224 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +"use strict"; + +// This is loaded into chrome windows with the subscript loader. Wrap in +// a block to prevent accidentally leaking globals onto `window`. +{ + const cachedFragments = { + get editMenuItems() { + return ` + <menuitem data-l10n-id="text-action-undo" cmd="cmd_undo"></menuitem> + <menuitem data-l10n-id="text-action-redo" cmd="cmd_redo"></menuitem> + <menuseparator></menuseparator> + <menuitem data-l10n-id="text-action-cut" cmd="cmd_cut"></menuitem> + <menuitem data-l10n-id="text-action-copy" cmd="cmd_copy"></menuitem> + <menuitem data-l10n-id="text-action-paste" cmd="cmd_paste"></menuitem> + <menuitem data-l10n-id="text-action-delete" cmd="cmd_delete"></menuitem> + <menuitem data-l10n-id="text-action-select-all" cmd="cmd_selectAll"></menuitem> + `; + }, + get normal() { + delete this.normal; + this.normal = MozXULElement.parseXULToFragment( + ` + <menupopup class="textbox-contextmenu"> + ${this.editMenuItems} + </menupopup> + ` + ); + MozXULElement.insertFTLIfNeeded("toolkit/global/textActions.ftl"); + return this.normal; + }, + get spellcheck() { + delete this.spellcheck; + this.spellcheck = MozXULElement.parseXULToFragment( + ` + <menupopup class="textbox-contextmenu"> + <menuitem data-l10n-id="text-action-spell-no-suggestions" anonid="spell-no-suggestions" disabled="true"></menuitem> + <menuitem data-l10n-id="text-action-spell-add-to-dictionary" anonid="spell-add-to-dictionary" oncommand="this.parentNode.parentNode.spellCheckerUI.addToDictionary();"></menuitem> + <menuitem data-l10n-id="text-action-spell-undo-add-to-dictionary" anonid="spell-undo-add-to-dictionary" oncommand="this.parentNode.parentNode.spellCheckerUI.undoAddToDictionary();"></menuitem> + <menuseparator anonid="spell-suggestions-separator"></menuseparator> + ${this.editMenuItems} + <menuseparator anonid="spell-check-separator"></menuseparator> + <menuitem data-l10n-id="text-action-spell-check-toggle" type="checkbox" anonid="spell-check-enabled" oncommand="this.parentNode.parentNode.spellCheckerUI.toggleEnabled();"></menuitem> + <menu data-l10n-id="text-action-spell-dictionaries" anonid="spell-dictionaries"> + <menupopup anonid="spell-dictionaries-menu" onpopupshowing="event.stopPropagation();" onpopuphiding="event.stopPropagation();"></menupopup> + </menu> + </menupopup> + ` + ); + return this.spellcheck; + }, + }; + + class MozInputBox extends MozXULElement { + static get observedAttributes() { + return ["spellcheck"]; + } + + attributeChangedCallback(name, oldValue, newValue) { + if (name === "spellcheck" && oldValue != newValue) { + this._initUI(); + } + } + + connectedCallback() { + this._initUI(); + } + + _initUI() { + this.spellcheck = this.hasAttribute("spellcheck"); + if (this.menupopup) { + this.menupopup.remove(); + } + + this.setAttribute("context", "_child"); + this.appendChild( + this.spellcheck + ? cachedFragments.spellcheck.cloneNode(true) + : cachedFragments.normal.cloneNode(true) + ); + this.menupopup = this.querySelector(".textbox-contextmenu"); + + this.menupopup.addEventListener("popupshowing", event => { + let input = this._input; + if (document.commandDispatcher.focusedElement != input) { + input.focus(); + } + this._doPopupItemEnabling(event); + }); + + if (this.spellcheck) { + this.menupopup.addEventListener("popuphiding", event => { + if (this.spellCheckerUI) { + this.spellCheckerUI.clearSuggestionsFromMenu(); + this.spellCheckerUI.clearDictionaryListFromMenu(); + } + }); + } + + this.menupopup.addEventListener("command", event => { + var cmd = event.originalTarget.getAttribute("cmd"); + if (cmd) { + this.doCommand(cmd); + event.stopPropagation(); + } + }); + } + + _doPopupItemEnablingSpell(event) { + var spellui = this.spellCheckerUI; + if (!spellui || !spellui.canSpellCheck) { + this._setMenuItemVisibility("spell-no-suggestions", false); + this._setMenuItemVisibility("spell-check-enabled", false); + this._setMenuItemVisibility("spell-check-separator", false); + this._setMenuItemVisibility("spell-add-to-dictionary", false); + this._setMenuItemVisibility("spell-undo-add-to-dictionary", false); + this._setMenuItemVisibility("spell-suggestions-separator", false); + this._setMenuItemVisibility("spell-dictionaries", false); + return; + } + + spellui.initFromEvent(event.rangeParent, event.rangeOffset); + + var enabled = spellui.enabled; + var showUndo = spellui.canSpellCheck && spellui.canUndo(); + + var enabledCheckbox = this.getMenuItem("spell-check-enabled"); + enabledCheckbox.setAttribute("checked", enabled); + + var overMisspelling = spellui.overMisspelling; + this._setMenuItemVisibility("spell-add-to-dictionary", overMisspelling); + this._setMenuItemVisibility("spell-undo-add-to-dictionary", showUndo); + this._setMenuItemVisibility( + "spell-suggestions-separator", + overMisspelling || showUndo + ); + + // suggestion list + var suggestionsSeparator = this.getMenuItem("spell-no-suggestions"); + var numsug = spellui.addSuggestionsToMenuOnParent( + event.target, + suggestionsSeparator, + 5 + ); + this._setMenuItemVisibility( + "spell-no-suggestions", + overMisspelling && numsug == 0 + ); + + // dictionary list + var dictionariesMenu = this.getMenuItem("spell-dictionaries-menu"); + var numdicts = spellui.addDictionaryListToMenu(dictionariesMenu, null); + this._setMenuItemVisibility( + "spell-dictionaries", + enabled && numdicts > 1 + ); + } + + _doPopupItemEnabling(event) { + if (this.spellcheck) { + this._doPopupItemEnablingSpell(event); + } + + let popupNode = event.target; + var children = popupNode.childNodes; + for (var i = 0; i < children.length; i++) { + var command = children[i].getAttribute("cmd"); + if (command) { + var controller = + document.commandDispatcher.getControllerForCommand(command); + var enabled = controller.isCommandEnabled(command); + if (enabled) { + children[i].removeAttribute("disabled"); + } else { + children[i].setAttribute("disabled", "true"); + } + } + } + } + + get spellCheckerUI() { + if (!this._spellCheckInitialized) { + this._spellCheckInitialized = true; + + try { + const { InlineSpellChecker } = ChromeUtils.importESModule( + "resource://gre/modules/InlineSpellChecker.sys.mjs" + ); + this.InlineSpellCheckerUI = new InlineSpellChecker( + this._input.editor + ); + } catch (ex) {} + } + + return this.InlineSpellCheckerUI; + } + + getMenuItem(anonid) { + return this.querySelector(`[anonid="${anonid}"]`); + } + + _setMenuItemVisibility(anonid, visible) { + this.getMenuItem(anonid).hidden = !visible; + } + + doCommand(command) { + var controller = + document.commandDispatcher.getControllerForCommand(command); + controller.doCommand(command); + } + + get _input() { + return ( + this.getElementsByAttribute("anonid", "input")[0] || + this.querySelector(".textbox-input") + ); + } + } + + customElements.define("moz-input-box", MozInputBox); +} |