diff options
Diffstat (limited to 'components/bindings/content/text.xml')
-rw-r--r-- | components/bindings/content/text.xml | 386 |
1 files changed, 386 insertions, 0 deletions
diff --git a/components/bindings/content/text.xml b/components/bindings/content/text.xml new file mode 100644 index 000000000..ed998cee4 --- /dev/null +++ b/components/bindings/content/text.xml @@ -0,0 +1,386 @@ +<?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/. --> + + +<bindings id="textBindings" + xmlns="http://www.mozilla.org/xbl" + xmlns:xul="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" + xmlns:html="http://www.w3.org/1999/xhtml"> + + <!-- bound to <description>s --> + <binding id="text-base" role="xul:text"> + <implementation implements="nsIDOMXULDescriptionElement"> + <property name="disabled" onset="if (val) this.setAttribute('disabled', 'true'); + else this.removeAttribute('disabled'); + return val;" + onget="return this.getAttribute('disabled') == 'true';"/> + <property name="value" onget="return this.getAttribute('value');" + onset="this.setAttribute('value', val); return val;"/> + <property name="crop" onget="return this.getAttribute('crop');" + onset="this.setAttribute('crop', val); return val;"/> + </implementation> + </binding> + + <binding id="text-label" extends="chrome://global/content/bindings/text.xml#text-base"> + <implementation implements="nsIDOMXULLabelElement"> + <property name="accessKey"> + <getter> + <![CDATA[ + var accessKey = this.getAttribute('accesskey'); + return accessKey ? accessKey[0] : null; + ]]> + </getter> + <setter> + <![CDATA[ + this.setAttribute('accesskey', val); + return val; + ]]> + </setter> + </property> + + <property name="control" onget="return getAttribute('control');"> + <setter> + <![CDATA[ + // After this gets set, the label will use the binding #label-control + this.setAttribute('control', val); + return val; + ]]> + </setter> + </property> + </implementation> + </binding> + + <binding id="label-control" extends="chrome://global/content/bindings/text.xml#text-label"> + <content> + <children/><html:span anonid="accessKeyParens"></html:span> + </content> + <implementation implements="nsIDOMXULLabelElement"> + <constructor> + <![CDATA[ + this.formatAccessKey(true); + ]]> + </constructor> + + <method name="formatAccessKey"> + <parameter name="firstTime"/> + <body> + <![CDATA[ + var control = this.labeledControlElement; + if (!control) { + var bindingParent = document.getBindingParent(this); + if (bindingParent instanceof Components.interfaces.nsIDOMXULLabeledControlElement) { + control = bindingParent; // For controls that make the <label> an anon child + } + } + if (control) { + control.labelElement = this; + } + + var accessKey = this.accessKey; + // No need to remove existing formatting the first time. + if (firstTime && !accessKey) + return; + + if (this.mInsertSeparator === undefined) { + try { + var prefs = Components.classes["@mozilla.org/preferences-service;1"]. + getService(Components.interfaces.nsIPrefBranch); + this.mUnderlineAccesskey = (prefs.getIntPref("ui.key.menuAccessKey") != 0); + + const nsIPrefLocalizedString = + Components.interfaces.nsIPrefLocalizedString; + + const prefNameInsertSeparator = + "intl.menuitems.insertseparatorbeforeaccesskeys"; + const prefNameAlwaysAppendAccessKey = + "intl.menuitems.alwaysappendaccesskeys"; + + var val = prefs.getComplexValue(prefNameInsertSeparator, + nsIPrefLocalizedString).data; + this.mInsertSeparator = (val == "true"); + + val = prefs.getComplexValue(prefNameAlwaysAppendAccessKey, + nsIPrefLocalizedString).data; + this.mAlwaysAppendAccessKey = (val == "true"); + } + catch (e) { + this.mInsertSeparator = true; + } + } + + if (!this.mUnderlineAccesskey) + return; + + var afterLabel = document.getAnonymousElementByAttribute(this, "anonid", "accessKeyParens"); + afterLabel.textContent = ""; + + var oldAccessKey = this.getElementsByAttribute('class', 'accesskey').item(0); + if (oldAccessKey) { // Clear old accesskey + this.mergeElement(oldAccessKey); + } + + var oldHiddenSpan = + this.getElementsByAttribute('class', 'hiddenColon').item(0); + if (oldHiddenSpan) { + this.mergeElement(oldHiddenSpan); + } + + var labelText = this.textContent; + if (!accessKey || !labelText || !control) { + return; + } + var accessKeyIndex = -1; + if (!this.mAlwaysAppendAccessKey) { + accessKeyIndex = labelText.indexOf(accessKey); + if (accessKeyIndex < 0) { // Try again in upper case + accessKeyIndex = + labelText.toUpperCase().indexOf(accessKey.toUpperCase()); + } + } + + const HTML_NS = "http://www.w3.org/1999/xhtml"; + var span = document.createElementNS(HTML_NS, "span"); + span.className = "accesskey"; + + // Note that if you change the following code, see the comment of + // nsTextBoxFrame::UpdateAccessTitle. + + // If accesskey is not in string, append in parentheses + if (accessKeyIndex < 0) { + // If end is colon, we should insert before colon. + // i.e., "label:" -> "label(X):" + var colonHidden = false; + if (/:$/.test(labelText)) { + labelText = labelText.slice(0, -1); + var hiddenSpan = document.createElementNS(HTML_NS, "span"); + hiddenSpan.className = "hiddenColon"; + hiddenSpan.style.display = "none"; + // Hide the last colon by using span element. + // I.e., label<span style="display:none;">:</span> + this.wrapChar(hiddenSpan, labelText.length); + colonHidden = true; + } + // If end is space(U+20), + // we should not add space before parentheses. + var endIsSpace = false; + if (/ $/.test(labelText)) { + endIsSpace = true; + } + if (this.mInsertSeparator && !endIsSpace) + afterLabel.textContent = " ("; + else + afterLabel.textContent = "("; + span.textContent = accessKey.toUpperCase(); + afterLabel.appendChild(span); + if (!colonHidden) + afterLabel.appendChild(document.createTextNode(")")); + else + afterLabel.appendChild(document.createTextNode("):")); + return; + } + this.wrapChar(span, accessKeyIndex); + ]]> + </body> + </method> + + <method name="wrapChar"> + <parameter name="element"/> + <parameter name="index"/> + <body> + <![CDATA[ + var treeWalker = document.createTreeWalker(this, + NodeFilter.SHOW_TEXT, + null); + var node = treeWalker.nextNode(); + while (index >= node.length) { + index -= node.length; + node = treeWalker.nextNode(); + } + if (index) { + node = node.splitText(index); + } + node.parentNode.insertBefore(element, node); + if (node.length > 1) { + node.splitText(1); + } + element.appendChild(node); + ]]> + </body> + </method> + + <method name="mergeElement"> + <parameter name="element"/> + <body> + <![CDATA[ + if (element.previousSibling instanceof Text) { + element.previousSibling.appendData(element.textContent) + } + else { + element.parentNode.insertBefore(element.firstChild, element); + } + element.parentNode.removeChild(element); + ]]> + </body> + </method> + + <field name="mUnderlineAccesskey"> + !/Mac/.test(navigator.platform) + </field> + <field name="mInsertSeparator"/> + <field name="mAlwaysAppendAccessKey">false</field> + + <property name="accessKey"> + <getter> + <![CDATA[ + var accessKey = null; + var labeledEl = this.labeledControlElement; + if (labeledEl) { + accessKey = labeledEl.getAttribute('accesskey'); + } + if (!accessKey) { + accessKey = this.getAttribute('accesskey'); + } + return accessKey ? accessKey[0] : null; + ]]> + </getter> + <setter> + <![CDATA[ + // If this label already has an accesskey attribute store it here as well + if (this.hasAttribute('accesskey')) { + this.setAttribute('accesskey', val); + } + var control = this.labeledControlElement; + if (control) { + control.setAttribute('accesskey', val); + } + this.formatAccessKey(false); + return val; + ]]> + </setter> + </property> + + <property name="labeledControlElement" readonly="true" + onget="var control = this.control; return control ? document.getElementById(control) : null;" /> + + <property name="control" onget="return this.getAttribute('control');"> + <setter> + <![CDATA[ + var control = this.labeledControlElement; + if (control) { + control.labelElement = null; // No longer pointed to be this label + } + this.setAttribute('control', val); + this.formatAccessKey(false); + return val; + ]]> + </setter> + </property> + + </implementation> + + <handlers> + <handler event="click" action="if (this.disabled) return; + var controlElement = this.labeledControlElement; + if(controlElement) + controlElement.focus(); + "/> + </handlers> + </binding> + + <binding id="text-link" extends="chrome://global/content/bindings/text.xml#text-label" role="xul:link"> + <implementation> + <property name="href" onget="return this.getAttribute('href');" + onset="this.setAttribute('href', val); return val;" /> + <method name="open"> + <parameter name="aEvent"/> + <body> + <![CDATA[ + var href = this.href; + if (!href || this.disabled || aEvent.defaultPrevented) + return; + + var uri = null; + try { + const nsISSM = Components.interfaces.nsIScriptSecurityManager; + const secMan = + Components.classes["@mozilla.org/scriptsecuritymanager;1"] + .getService(nsISSM); + + const ioService = + Components.classes["@mozilla.org/network/io-service;1"] + .getService(Components.interfaces.nsIIOService); + + uri = ioService.newURI(href, null, null); + + let principal; + if (this.getAttribute("useoriginprincipal") == "true") { + principal = this.nodePrincipal; + } else { + principal = secMan.createNullPrincipal({}); + } + try { + secMan.checkLoadURIWithPrincipal(principal, uri, + nsISSM.DISALLOW_INHERIT_PRINCIPAL); + } + catch (ex) { + var msg = "Error: Cannot open a " + uri.scheme + ": link using \ + the text-link binding."; + Components.utils.reportError(msg); + return; + } + + const cID = "@mozilla.org/uriloader/external-protocol-service;1"; + const nsIEPS = Components.interfaces.nsIExternalProtocolService; + var protocolSvc = Components.classes[cID].getService(nsIEPS); + + // if the scheme is not an exposed protocol, then opening this link + // should be deferred to the system's external protocol handler + if (!protocolSvc.isExposedProtocol(uri.scheme)) { + protocolSvc.loadUrl(uri); + aEvent.preventDefault() + return; + } + + } + catch (ex) { + Components.utils.reportError(ex); + } + + aEvent.preventDefault(); + href = uri ? uri.spec : href; + + // Try handing off the link to the host application, e.g. for + // opening it in a tabbed browser. + var linkHandled = Components.classes["@mozilla.org/supports-PRBool;1"] + .createInstance(Components.interfaces.nsISupportsPRBool); + linkHandled.data = false; + let {shiftKey, ctrlKey, metaKey, altKey, button} = aEvent; + let data = {shiftKey, ctrlKey, metaKey, altKey, button, href}; + Components.classes["@mozilla.org/observer-service;1"] + .getService(Components.interfaces.nsIObserverService) + .notifyObservers(linkHandled, "handle-xul-text-link", JSON.stringify(data)); + if (linkHandled.data) + return; + + // otherwise, fall back to opening the anchor directly + var win = window; + if (window instanceof Components.interfaces.nsIDOMChromeWindow) { + while (win.opener && !win.opener.closed) + win = win.opener; + } + win.open(href); + ]]> + </body> + </method> + </implementation> + + <handlers> + <handler event="click" phase="capturing" button="0" action="this.open(event)"/> + <handler event="click" phase="capturing" button="1" action="this.open(event)"/> + <handler event="keypress" preventdefault="true" keycode="VK_RETURN" action="this.click()" /> + </handlers> + </binding> + +</bindings> |