diff options
Diffstat (limited to 'toolkit/content/widgets/textbox.xml')
-rw-r--r-- | toolkit/content/widgets/textbox.xml | 646 |
1 files changed, 646 insertions, 0 deletions
diff --git a/toolkit/content/widgets/textbox.xml b/toolkit/content/widgets/textbox.xml new file mode 100644 index 000000000..f166fb78a --- /dev/null +++ b/toolkit/content/widgets/textbox.xml @@ -0,0 +1,646 @@ +<?xml version="1.0"?> +<!-- 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/. --> + + +<!DOCTYPE bindings [ + <!ENTITY % textcontextDTD SYSTEM "chrome://global/locale/textcontext.dtd" > + %textcontextDTD; +]> + +<bindings id="textboxBindings" + xmlns="http://www.mozilla.org/xbl" + xmlns:html="http://www.w3.org/1999/xhtml" + xmlns:xul="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" + xmlns:xbl="http://www.mozilla.org/xbl"> + + <binding id="textbox" extends="xul:box" role="xul:textbox"> + <resources> + <stylesheet src="chrome://global/content/textbox.css"/> + <stylesheet src="chrome://global/skin/textbox.css"/> + </resources> + + <content> + <children/> + <xul:hbox class="textbox-input-box" flex="1" xbl:inherits="context,spellcheck"> + <html:input class="textbox-input" anonid="input" + xbl:inherits="value,type,maxlength,disabled,size,readonly,placeholder,tabindex,accesskey,noinitialfocus,mozactionhint,spellcheck"/> + </xul:hbox> + </content> + + <implementation implements="nsIDOMXULTextBoxElement, nsIDOMXULLabeledControlElement"> + <!-- nsIDOMXULLabeledControlElement --> + <field name="crop">""</field> + <field name="image">""</field> + <field name="command">""</field> + <field name="accessKey">""</field> + + <field name="mInputField">null</field> + <field name="mIgnoreClick">false</field> + <field name="mIgnoreFocus">false</field> + <field name="mEditor">null</field> + + <property name="inputField" readonly="true"> + <getter><![CDATA[ + if (!this.mInputField) + this.mInputField = document.getAnonymousElementByAttribute(this, "anonid", "input"); + return this.mInputField; + ]]></getter> + </property> + + <property name="value" onset="this.inputField.value = val; return val;" + onget="return this.inputField.value;"/> + <property name="defaultValue" onset="this.inputField.defaultValue = val; return val;" + onget="return this.inputField.defaultValue;"/> + <property name="label" onset="this.setAttribute('label', val); return val;" + onget="return this.getAttribute('label') || + (this.labelElement ? this.labelElement.value : + this.placeholder);"/> + <property name="placeholder" onset="this.inputField.placeholder = val; return val;" + onget="return this.inputField.placeholder;"/> + <property name="emptyText" onset="this.placeholder = val; return val;" + onget="return this.placeholder;"/> + <property name="type" onset="if (val) this.setAttribute('type', val); + else this.removeAttribute('type'); return val;" + onget="return this.getAttribute('type');"/> + <property name="maxLength" onset="this.inputField.maxLength = val; return val;" + onget="return this.inputField.maxLength;"/> + <property name="disabled" onset="this.inputField.disabled = val; + if (val) this.setAttribute('disabled', 'true'); + else this.removeAttribute('disabled'); return val;" + onget="return this.inputField.disabled;"/> + <property name="tabIndex" onget="return parseInt(this.getAttribute('tabindex'));" + onset="this.inputField.tabIndex = val; + if (val) this.setAttribute('tabindex', val); + else this.removeAttribute('tabindex'); return val;"/> + <property name="size" onset="this.inputField.size = val; return val;" + onget="return this.inputField.size;"/> + <property name="readOnly" onset="this.inputField.readOnly = val; + if (val) this.setAttribute('readonly', 'true'); + else this.removeAttribute('readonly'); return val;" + onget="return this.inputField.readOnly;"/> + <property name="clickSelectsAll" + onget="return this.getAttribute('clickSelectsAll') == 'true';" + onset="if (val) this.setAttribute('clickSelectsAll', 'true'); + else this.removeAttribute('clickSelectsAll'); return val;" /> + + <property name="editor" readonly="true"> + <getter><![CDATA[ + if (!this.mEditor) { + const nsIDOMNSEditableElement = Components.interfaces.nsIDOMNSEditableElement; + this.mEditor = this.inputField.QueryInterface(nsIDOMNSEditableElement).editor; + } + return this.mEditor; + ]]></getter> + </property> + + <method name="reset"> + <body><![CDATA[ + this.value = this.defaultValue; + try { + this.editor.transactionManager.clear(); + return true; + } + catch (e) {} + return false; + ]]></body> + </method> + + <method name="select"> + <body> + this.inputField.select(); + </body> + </method> + + <property name="controllers" readonly="true" onget="return this.inputField.controllers"/> + <property name="textLength" readonly="true" + onget="return this.inputField.textLength;"/> + <property name="selectionStart" onset="this.inputField.selectionStart = val; return val;" + onget="return this.inputField.selectionStart;"/> + <property name="selectionEnd" onset="this.inputField.selectionEnd = val; return val;" + onget="return this.inputField.selectionEnd;"/> + + <method name="setSelectionRange"> + <parameter name="aSelectionStart"/> + <parameter name="aSelectionEnd"/> + <body> + this.inputField.setSelectionRange( aSelectionStart, aSelectionEnd ); + </body> + </method> + + <method name="_setNewlineHandling"> + <body><![CDATA[ + var str = this.getAttribute("newlines"); + if (str && this.editor) { + const nsIPlaintextEditor = Components.interfaces.nsIPlaintextEditor; + for (var x in nsIPlaintextEditor) { + if (/^eNewlines/.test(x)) { + if (str == RegExp.rightContext.toLowerCase()) { + this.editor.QueryInterface(nsIPlaintextEditor) + .newlineHandling = nsIPlaintextEditor[x]; + break; + } + } + } + } + ]]></body> + </method> + + <method name="_maybeSelectAll"> + <body><![CDATA[ + if (!this.mIgnoreClick && this.clickSelectsAll && + document.activeElement == this.inputField && + this.inputField.selectionStart == this.inputField.selectionEnd) + this.editor.selectAll(); + ]]></body> + </method> + + <constructor><![CDATA[ + var str = this.boxObject.getProperty("value"); + if (str) { + this.inputField.value = str; + this.boxObject.removeProperty("value"); + } + + this._setNewlineHandling(); + + if (this.hasAttribute("emptytext")) + this.placeholder = this.getAttribute("emptytext"); + ]]></constructor> + + <destructor> + <![CDATA[ + var field = this.inputField; + if (field && field.value) + this.boxObject.setProperty('value', field.value); + this.mInputField = null; + ]]> + </destructor> + + </implementation> + + <handlers> + <handler event="focus" phase="capturing"> + <![CDATA[ + if (this.hasAttribute("focused")) + return; + + switch (event.originalTarget) { + case this: + // Forward focus to actual HTML input + this.inputField.focus(); + break; + case this.inputField: + if (this.mIgnoreFocus) { + this.mIgnoreFocus = false; + } else if (this.clickSelectsAll) { + try { + const nsIEditorIMESupport = + Components.interfaces.nsIEditorIMESupport; + let imeEditor = this.editor.QueryInterface(nsIEditorIMESupport); + if (!imeEditor || !imeEditor.composing) + this.editor.selectAll(); + } catch (e) {} + } + break; + default: + // Allow other children (e.g. URL bar buttons) to get focus + return; + } + this.setAttribute("focused", "true"); + ]]> + </handler> + + <handler event="blur" phase="capturing"> + <![CDATA[ + this.removeAttribute("focused"); + + // don't trigger clickSelectsAll when switching application windows + if (window == window.top && + window.constructor == ChromeWindow && + document.activeElement == this.inputField) + this.mIgnoreFocus = true; + ]]> + </handler> + + <handler event="mousedown"> + <![CDATA[ + this.mIgnoreClick = this.hasAttribute("focused"); + + if (!this.mIgnoreClick) { + this.mIgnoreFocus = true; + this.inputField.setSelectionRange(0, 0); + if (event.originalTarget == this || + event.originalTarget == this.inputField.parentNode) + this.inputField.focus(); + } + ]]> + </handler> + + <handler event="click" action="this._maybeSelectAll();"/> + +#ifndef XP_WIN + <handler event="contextmenu"> + // Only care about context clicks on the textbox itself. + if (event.target != this) + return; + + if (!event.button) // context menu opened via keyboard shortcut + return; + this._maybeSelectAll(); + // see bug 576135 comment 4 + let box = this.inputField.parentNode; + let menu = document.getAnonymousElementByAttribute(box, "anonid", "input-box-contextmenu"); + box._doPopupItemEnabling(menu); + </handler> +#endif + </handlers> + </binding> + + <binding id="timed-textbox" extends="chrome://global/content/bindings/textbox.xml#textbox"> + <implementation> + <constructor><![CDATA[ + try { + var consoleService = Components.classes["@mozilla.org/consoleservice;1"] + .getService(Components.interfaces.nsIConsoleService); + var scriptError = Components.classes["@mozilla.org/scripterror;1"] + .createInstance(Components.interfaces.nsIScriptError); + scriptError.init("Timed textboxes are deprecated. Consider using type=\"search\" instead.", + this.ownerDocument.location.href, null, null, + null, scriptError.warningFlag, "XUL Widgets"); + consoleService.logMessage(scriptError); + } catch (e) {} + ]]></constructor> + <field name="_timer">null</field> + <property name="timeout" + onset="this.setAttribute('timeout', val); return val;" + onget="return parseInt(this.getAttribute('timeout')) || 0;"/> + <property name="value" + onget="return this.inputField.value;"> + <setter><![CDATA[ + this.inputField.value = val; + if (this._timer) + clearTimeout(this._timer); + return val; + ]]></setter> + </property> + <method name="_fireCommand"> + <parameter name="me"/> + <body> + <![CDATA[ + me._timer = null; + me.doCommand(); + ]]> + </body> + </method> + </implementation> + <handlers> + <handler event="input"> + <![CDATA[ + if (this._timer) + clearTimeout(this._timer); + this._timer = this.timeout && setTimeout(this._fireCommand, this.timeout, this); + ]]> + </handler> + <handler event="keypress" keycode="VK_RETURN"> + <![CDATA[ + if (this._timer) + clearTimeout(this._timer); + this._fireCommand(this); + event.preventDefault(); + ]]> + </handler> + </handlers> + </binding> + + <binding id="search-textbox" extends="chrome://global/content/bindings/textbox.xml#textbox"> + <content> + <children/> + <xul:hbox class="textbox-input-box" flex="1" xbl:inherits="context,spellcheck" align="center"> + <html:input class="textbox-input" anonid="input" mozactionhint="search" + xbl:inherits="value,type,maxlength,disabled,size,readonly,placeholder,tabindex,accesskey,mozactionhint,spellcheck"/> + <xul:deck class="textbox-search-icons" anonid="search-icons"> + <xul:image class="textbox-search-icon" anonid="searchbutton-icon" + xbl:inherits="src=image,label=searchbuttonlabel,searchbutton,disabled"/> + <xul:image class="textbox-search-clear" + onclick="document.getBindingParent(this)._clearSearch();" + label="&searchTextBox.clear.label;" + xbl:inherits="disabled"/> + </xul:deck> + </xul:hbox> + </content> + <implementation> + <field name="_timer">null</field> + <field name="_searchIcons"> + document.getAnonymousElementByAttribute(this, "anonid", "search-icons"); + </field> + <field name="_searchButtonIcon"> + document.getAnonymousElementByAttribute(this, "anonid", "searchbutton-icon"); + </field> + <property name="timeout" + onset="this.setAttribute('timeout', val); return val;" + onget="return parseInt(this.getAttribute('timeout')) || 500;"/> + <property name="searchButton" + onget="return this.getAttribute('searchbutton') == 'true';"> + <setter><![CDATA[ + if (val) { + this.setAttribute("searchbutton", "true"); + this.removeAttribute("aria-autocomplete"); + // Hack for the button to get the right accessible: + this._searchButtonIcon.setAttribute("onclick", "true"); + } else { + this.removeAttribute("searchbutton"); + this._searchButtonIcon.removeAttribute("onclick"); + this.setAttribute("aria-autocomplete", "list"); + } + return val; + ]]></setter> + </property> + <property name="value" + onget="return this.inputField.value;"> + <setter><![CDATA[ + this.inputField.value = val; + + if (val) + this._searchIcons.selectedIndex = this.searchButton ? 0 : 1; + else + this._searchIcons.selectedIndex = 0; + + if (this._timer) + clearTimeout(this._timer); + + return val; + ]]></setter> + </property> + <constructor><![CDATA[ + // Ensure the button state is up to date: + this.searchButton = this.searchButton; + this._searchButtonIcon.addEventListener("click", (e) => this._iconClick(e), false); + ]]></constructor> + <method name="_fireCommand"> + <parameter name="me"/> + <body><![CDATA[ + if (me._timer) + clearTimeout(me._timer); + me._timer = null; + me.doCommand(); + ]]></body> + </method> + <method name="_iconClick"> + <body><![CDATA[ + if (this.searchButton) + this._enterSearch(); + else + this.focus(); + ]]></body> + </method> + <method name="_enterSearch"> + <body><![CDATA[ + if (this.disabled) + return; + if (this.searchButton && this.value && !this.readOnly) + this._searchIcons.selectedIndex = 1; + this._fireCommand(this); + ]]></body> + </method> + <method name="_clearSearch"> + <body><![CDATA[ + if (!this.disabled && !this.readOnly && this.value) { + this.value = ""; + this._fireCommand(this); + this._searchIcons.selectedIndex = 0; + return true; + } + return false; + ]]></body> + </method> + </implementation> + <handlers> + <handler event="input"> + <![CDATA[ + if (this.searchButton) { + this._searchIcons.selectedIndex = 0; + return; + } + if (this._timer) + clearTimeout(this._timer); + this._timer = this.timeout && setTimeout(this._fireCommand, this.timeout, this); + this._searchIcons.selectedIndex = this.value ? 1 : 0; + ]]> + </handler> + <handler event="keypress" keycode="VK_ESCAPE"> + <![CDATA[ + if (this._clearSearch()) { + event.preventDefault(); + event.stopPropagation(); + } + ]]> + </handler> + <handler event="keypress" keycode="VK_RETURN"> + <![CDATA[ + this._enterSearch(); + event.preventDefault(); + event.stopPropagation(); + ]]> + </handler> + </handlers> + </binding> + + <binding id="textarea" extends="chrome://global/content/bindings/textbox.xml#textbox"> + <content> + <xul:hbox class="textbox-input-box" flex="1" xbl:inherits="context,spellcheck"> + <html:textarea class="textbox-textarea" anonid="input" + xbl:inherits="xbl:text=value,disabled,tabindex,rows,cols,readonly,wrap,placeholder,mozactionhint,spellcheck"><children/></html:textarea> + </xul:hbox> + </content> + </binding> + + <binding id="input-box"> + <content context="_child"> + <children/> + <xul:menupopup anonid="input-box-contextmenu" + class="textbox-contextmenu" + onpopupshowing="var input = + this.parentNode.getElementsByAttribute('anonid', 'input')[0]; + if (document.commandDispatcher.focusedElement != input) + input.focus(); + this.parentNode._doPopupItemEnabling(this);" + oncommand="var cmd = event.originalTarget.getAttribute('cmd'); if(cmd) { this.parentNode.doCommand(cmd); event.stopPropagation(); }"> + <xul:menuitem label="&undoCmd.label;" accesskey="&undoCmd.accesskey;" cmd="cmd_undo"/> + <xul:menuseparator/> + <xul:menuitem label="&cutCmd.label;" accesskey="&cutCmd.accesskey;" cmd="cmd_cut"/> + <xul:menuitem label="©Cmd.label;" accesskey="©Cmd.accesskey;" cmd="cmd_copy"/> + <xul:menuitem label="&pasteCmd.label;" accesskey="&pasteCmd.accesskey;" cmd="cmd_paste"/> + <xul:menuitem label="&deleteCmd.label;" accesskey="&deleteCmd.accesskey;" cmd="cmd_delete"/> + <xul:menuseparator/> + <xul:menuitem label="&selectAllCmd.label;" accesskey="&selectAllCmd.accesskey;" cmd="cmd_selectAll"/> + </xul:menupopup> + </content> + + <implementation> + <method name="_doPopupItemEnabling"> + <parameter name="popupNode"/> + <body> + <![CDATA[ + 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"); + } + } + ]]> + </body> + </method> + + <method name="_setMenuItemVisibility"> + <parameter name="anonid"/> + <parameter name="visible"/> + <body><![CDATA[ + document.getAnonymousElementByAttribute(this, "anonid", anonid). + hidden = ! visible; + ]]></body> + </method> + + <method name="doCommand"> + <parameter name="command"/> + <body> + <![CDATA[ + var controller = document.commandDispatcher.getControllerForCommand(command); + controller.doCommand(command); + ]]> + </body> + </method> + </implementation> + </binding> + + <binding id="input-box-spell" extends="chrome://global/content/bindings/textbox.xml#input-box"> + <content context="_child"> + <children/> + <xul:menupopup anonid="input-box-contextmenu" + class="textbox-contextmenu" + onpopupshowing="var input = + this.parentNode.getElementsByAttribute('anonid', 'input')[0]; + if (document.commandDispatcher.focusedElement != input) + input.focus(); + this.parentNode._doPopupItemEnablingSpell(this);" + onpopuphiding="this.parentNode._doPopupItemDisabling(this);" + oncommand="var cmd = event.originalTarget.getAttribute('cmd'); if(cmd) { this.parentNode.doCommand(cmd); event.stopPropagation(); }"> + <xul:menuitem label="&spellNoSuggestions.label;" anonid="spell-no-suggestions" disabled="true"/> + <xul:menuitem label="&spellAddToDictionary.label;" accesskey="&spellAddToDictionary.accesskey;" anonid="spell-add-to-dictionary" + oncommand="this.parentNode.parentNode.spellCheckerUI.addToDictionary();"/> + <xul:menuitem label="&spellUndoAddToDictionary.label;" accesskey="&spellUndoAddToDictionary.accesskey;" anonid="spell-undo-add-to-dictionary" + oncommand="this.parentNode.parentNode.spellCheckerUI.undoAddToDictionary();"/> + <xul:menuseparator anonid="spell-suggestions-separator"/> + <xul:menuitem label="&undoCmd.label;" accesskey="&undoCmd.accesskey;" cmd="cmd_undo"/> + <xul:menuseparator/> + <xul:menuitem label="&cutCmd.label;" accesskey="&cutCmd.accesskey;" cmd="cmd_cut"/> + <xul:menuitem label="©Cmd.label;" accesskey="©Cmd.accesskey;" cmd="cmd_copy"/> + <xul:menuitem label="&pasteCmd.label;" accesskey="&pasteCmd.accesskey;" cmd="cmd_paste"/> + <xul:menuitem label="&deleteCmd.label;" accesskey="&deleteCmd.accesskey;" cmd="cmd_delete"/> + <xul:menuseparator/> + <xul:menuitem label="&selectAllCmd.label;" accesskey="&selectAllCmd.accesskey;" cmd="cmd_selectAll"/> + <xul:menuseparator anonid="spell-check-separator"/> + <xul:menuitem label="&spellCheckToggle.label;" type="checkbox" accesskey="&spellCheckToggle.accesskey;" anonid="spell-check-enabled" + oncommand="this.parentNode.parentNode.spellCheckerUI.toggleEnabled();"/> + <xul:menu label="&spellDictionaries.label;" accesskey="&spellDictionaries.accesskey;" anonid="spell-dictionaries"> + <xul:menupopup anonid="spell-dictionaries-menu" + onpopupshowing="event.stopPropagation();" + onpopuphiding="event.stopPropagation();"/> + </xul:menu> + </xul:menupopup> + </content> + + <implementation> + <field name="_spellCheckInitialized">false</field> + <field name="_enabledCheckbox"> + document.getAnonymousElementByAttribute(this, "anonid", "spell-check-enabled"); + </field> + <field name="_suggestionsSeparator"> + document.getAnonymousElementByAttribute(this, "anonid", "spell-no-suggestions"); + </field> + <field name="_dictionariesMenu"> + document.getAnonymousElementByAttribute(this, "anonid", "spell-dictionaries-menu"); + </field> + + <property name="spellCheckerUI" readonly="true"> + <getter><![CDATA[ + if (!this._spellCheckInitialized) { + this._spellCheckInitialized = true; + + const CI = Components.interfaces; + if (!(document instanceof CI.nsIDOMXULDocument)) + return null; + + var textbox = document.getBindingParent(this); + if (!textbox || !(textbox instanceof CI.nsIDOMXULTextBoxElement)) + return null; + + try { + Components.utils.import("resource://gre/modules/InlineSpellChecker.jsm", this); + this.InlineSpellCheckerUI = new this.InlineSpellChecker(textbox.editor); + } catch (ex) { } + } + + return this.InlineSpellCheckerUI; + ]]></getter> + </property> + + <method name="_doPopupItemEnablingSpell"> + <parameter name="popupNode"/> + <body> + <![CDATA[ + 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(document.popupRangeParent, + document.popupRangeOffset); + + var enabled = spellui.enabled; + var showUndo = spellui.canSpellCheck && spellui.canUndo(); + this._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 numsug = spellui.addSuggestionsToMenu(popupNode, this._suggestionsSeparator, 5); + this._setMenuItemVisibility("spell-no-suggestions", overMisspelling && numsug == 0); + + // dictionary list + var numdicts = spellui.addDictionaryListToMenu(this._dictionariesMenu, null); + this._setMenuItemVisibility("spell-dictionaries", enabled && numdicts > 1); + + this._doPopupItemEnabling(popupNode); + ]]> + </body> + </method> + <method name="_doPopupItemDisabling"> + <body><![CDATA[ + if (this.spellCheckerUI) { + this.spellCheckerUI.clearSuggestionsFromMenu(); + this.spellCheckerUI.clearDictionaryListFromMenu(); + } + ]]></body> + </method> + </implementation> + </binding> + +</bindings> |