diff options
Diffstat (limited to 'toolkit/content/widgets/radio.xml')
-rw-r--r-- | toolkit/content/widgets/radio.xml | 526 |
1 files changed, 526 insertions, 0 deletions
diff --git a/toolkit/content/widgets/radio.xml b/toolkit/content/widgets/radio.xml new file mode 100644 index 000000000..de3acfbf6 --- /dev/null +++ b/toolkit/content/widgets/radio.xml @@ -0,0 +1,526 @@ +<?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="radioBindings" + xmlns="http://www.mozilla.org/xbl" + xmlns:xul="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" + xmlns:xbl="http://www.mozilla.org/xbl"> + + <binding id="radiogroup" role="xul:radiogroup" + extends="chrome://global/content/bindings/general.xml#basecontrol"> + <resources> + <stylesheet src="chrome://global/skin/radio.css"/> + </resources> + + <implementation implements="nsIDOMXULSelectControlElement"> + <constructor> + <![CDATA[ + if (this.getAttribute("disabled") == "true") + this.disabled = true; + + var children = this._getRadioChildren(); + var length = children.length; + for (var i = 0; i < length; i++) { + if (children[i].getAttribute("selected") == "true") { + this.selectedIndex = i; + return; + } + } + + var value = this.value; + if (value) + this.value = value; + else + this.selectedIndex = 0; + ]]> + </constructor> + + <property name="value" onget="return this.getAttribute('value');"> + <setter> + <![CDATA[ + this.setAttribute("value", val); + var children = this._getRadioChildren(); + for (var i = 0; i < children.length; i++) { + if (String(children[i].value) == String(val)) { + this.selectedItem = children[i]; + break; + } + } + return val; + ]]> + </setter> + </property> + <property name="disabled"> + <getter> + <![CDATA[ + if (this.getAttribute('disabled') == 'true') + return true; + var children = this._getRadioChildren(); + for (var i = 0; i < children.length; ++i) { + if (!children[i].hidden && !children[i].collapsed && !children[i].disabled) + return false; + } + return true; + ]]> + </getter> + <setter> + <![CDATA[ + if (val) + this.setAttribute('disabled', 'true'); + else + this.removeAttribute('disabled'); + var children = this._getRadioChildren(); + for (var i = 0; i < children.length; ++i) { + children[i].disabled = val; + } + return val; + ]]> + </setter> + </property> + + <property name="itemCount" readonly="true" + onget="return this._getRadioChildren().length"/> + + <property name="selectedIndex"> + <getter> + <![CDATA[ + var children = this._getRadioChildren(); + for (var i = 0; i < children.length; ++i) { + if (children[i].selected) + return i; + } + return -1; + ]]> + </getter> + <setter> + <![CDATA[ + this.selectedItem = this._getRadioChildren()[val]; + return val; + ]]> + </setter> + </property> + + <property name="selectedItem"> + <getter> + <![CDATA[ + var children = this._getRadioChildren(); + for (var i = 0; i < children.length; ++i) { + if (children[i].selected) + return children[i]; + } + return null; + ]]> + </getter> + <setter> + <![CDATA[ + var focused = this.getAttribute("focused") == "true"; + var alreadySelected = false; + + if (val) { + alreadySelected = val.getAttribute("selected") == "true"; + val.setAttribute("focused", focused); + val.setAttribute("selected", "true"); + this.setAttribute("value", val.value); + } + else { + this.removeAttribute("value"); + } + + // uncheck all other group nodes + var children = this._getRadioChildren(); + var previousItem = null; + for (var i = 0; i < children.length; ++i) { + if (children[i] != val) { + if (children[i].getAttribute("selected") == "true") + previousItem = children[i]; + + children[i].removeAttribute("selected"); + children[i].removeAttribute("focused"); + } + } + + var event = document.createEvent("Events"); + event.initEvent("select", false, true); + this.dispatchEvent(event); + + if (!alreadySelected && focused) { + // Only report if actual change + var myEvent; + if (val) { + myEvent = document.createEvent("Events"); + myEvent.initEvent("RadioStateChange", true, true); + val.dispatchEvent(myEvent); + } + + if (previousItem) { + myEvent = document.createEvent("Events"); + myEvent.initEvent("RadioStateChange", true, true); + previousItem.dispatchEvent(myEvent); + } + } + + return val; + ]]> + </setter> + </property> + + <property name="focusedItem"> + <getter> + <![CDATA[ + var children = this._getRadioChildren(); + for (var i = 0; i < children.length; ++i) { + if (children[i].getAttribute("focused") == "true") + return children[i]; + } + return null; + ]]> + </getter> + <setter> + <![CDATA[ + if (val) val.setAttribute("focused", "true"); + + // unfocus all other group nodes + var children = this._getRadioChildren(); + for (var i = 0; i < children.length; ++i) { + if (children[i] != val) + children[i].removeAttribute("focused"); + } + return val; + ]]> + </setter> + </property> + + <method name="checkAdjacentElement"> + <parameter name="aNextFlag"/> + <body> + <![CDATA[ + var currentElement = this.focusedItem || this.selectedItem; + var i; + var children = this._getRadioChildren(); + for (i = 0; i < children.length; ++i ) { + if (children[i] == currentElement) + break; + } + var index = i; + + if (aNextFlag) { + do { + if (++i == children.length) + i = 0; + if (i == index) + break; + } + while (children[i].hidden || children[i].collapsed || children[i].disabled); + // XXX check for display/visibility props too + + this.selectedItem = children[i]; + children[i].doCommand(); + } + else { + do { + if (i == 0) + i = children.length; + if (--i == index) + break; + } + while (children[i].hidden || children[i].collapsed || children[i].disabled); + // XXX check for display/visibility props too + + this.selectedItem = children[i]; + children[i].doCommand(); + } + ]]> + </body> + </method> + <field name="_radioChildren">null</field> + <method name="_getRadioChildren"> + <body> + <![CDATA[ + if (this._radioChildren) + return this._radioChildren; + + var radioChildren = []; + var doc = this.ownerDocument; + + if (this.hasChildNodes()) { + // Don't store the collected child nodes immediately, + // collecting the child nodes could trigger constructors + // which would blow away our list. + + const nsIDOMNodeFilter = Components.interfaces.nsIDOMNodeFilter; + var iterator = doc.createTreeWalker(this, + nsIDOMNodeFilter.SHOW_ELEMENT, + this._filterRadioGroup); + while (iterator.nextNode()) + radioChildren.push(iterator.currentNode); + return this._radioChildren = radioChildren; + } + + // We don't have child nodes. + const XUL_NS = "http://www.mozilla.org/keymaster/" + + "gatekeeper/there.is.only.xul"; + var elems = doc.getElementsByAttribute("group", this.id); + for (var i = 0; i < elems.length; i++) { + if ((elems[i].namespaceURI == XUL_NS) && + (elems[i].localName == "radio")) { + radioChildren.push(elems[i]); + } + } + return this._radioChildren = radioChildren; + ]]> + </body> + </method> + <method name="_filterRadioGroup"> + <parameter name="node"/> + <body> + <![CDATA[ + switch (node.localName) { + case "radio": return NodeFilter.FILTER_ACCEPT; + case "template": + case "radiogroup": return NodeFilter.FILTER_REJECT; + default: return NodeFilter.FILTER_SKIP; + } + ]]> + </body> + </method> + + <method name="getIndexOfItem"> + <parameter name="item"/> + <body> + return this._getRadioChildren().indexOf(item); + </body> + </method> + + <method name="getItemAtIndex"> + <parameter name="index"/> + <body> + <![CDATA[ + var children = this._getRadioChildren(); + return (index >= 0 && index < children.length) ? children[index] : null; + ]]> + </body> + </method> + + <method name="appendItem"> + <parameter name="label"/> + <parameter name="value"/> + <body> + <![CDATA[ + var XULNS = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"; + var radio = document.createElementNS(XULNS, "radio"); + radio.setAttribute("label", label); + radio.setAttribute("value", value); + this.appendChild(radio); + this._radioChildren = null; + return radio; + ]]> + </body> + </method> + + <method name="insertItemAt"> + <parameter name="index"/> + <parameter name="label"/> + <parameter name="value"/> + <body> + <![CDATA[ + var XULNS = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"; + var radio = document.createElementNS(XULNS, "radio"); + radio.setAttribute("label", label); + radio.setAttribute("value", value); + var before = this.getItemAtIndex(index); + if (before) + before.parentNode.insertBefore(radio, before); + else + this.appendChild(radio); + this._radioChildren = null; + return radio; + ]]> + </body> + </method> + + <method name="removeItemAt"> + <parameter name="index"/> + <body> + <![CDATA[ + var remove = this.getItemAtIndex(index); + if (remove) { + remove.parentNode.removeChild(remove); + this._radioChildren = null; + } + return remove; + ]]> + </body> + </method> + </implementation> + + <handlers> + <handler event="mousedown"> + if (this.disabled) + event.preventDefault(); + </handler> + + <!-- keyboard navigation --> + <!-- Here's how keyboard navigation works in radio groups on Windows: + The group takes 'focus' + The user is then free to navigate around inside the group + using the arrow keys. Accessing previous or following radio buttons + is done solely through the arrow keys and not the tab button. Tab + takes you to the next widget in the tab order --> + <handler event="keypress" key=" " phase="target"> + this.selectedItem = this.focusedItem; + this.selectedItem.doCommand(); + // Prevent page from scrolling on the space key. + event.preventDefault(); + </handler> + <handler event="keypress" keycode="VK_UP" phase="target"> + this.checkAdjacentElement(false); + event.stopPropagation(); + event.preventDefault(); + </handler> + <handler event="keypress" keycode="VK_LEFT" phase="target"> + // left arrow goes back when we are ltr, forward when we are rtl + this.checkAdjacentElement(document.defaultView.getComputedStyle( + this, "").direction == "rtl"); + event.stopPropagation(); + event.preventDefault(); + </handler> + <handler event="keypress" keycode="VK_DOWN" phase="target"> + this.checkAdjacentElement(true); + event.stopPropagation(); + event.preventDefault(); + </handler> + <handler event="keypress" keycode="VK_RIGHT" phase="target"> + // right arrow goes forward when we are ltr, back when we are rtl + this.checkAdjacentElement(document.defaultView.getComputedStyle( + this, "").direction == "ltr"); + event.stopPropagation(); + event.preventDefault(); + </handler> + + <!-- set a focused attribute on the selected item when the group + receives focus so that we can style it as if it were focused even though + it is not (Windows platform behaviour is for the group to receive focus, + not the item --> + <handler event="focus" phase="target"> + <![CDATA[ + this.setAttribute("focused", "true"); + if (this.focusedItem) + return; + + var val = this.selectedItem; + if (!val || val.disabled || val.hidden || val.collapsed) { + var children = this._getRadioChildren(); + for (var i = 0; i < children.length; ++i) { + if (!children[i].hidden && !children[i].collapsed && !children[i].disabled) { + val = children[i]; + break; + } + } + } + this.focusedItem = val; + ]]> + </handler> + <handler event="blur" phase="target"> + this.removeAttribute("focused"); + this.focusedItem = null; + </handler> + </handlers> + </binding> + + <binding id="radio" role="xul:radiobutton" + extends="chrome://global/content/bindings/general.xml#control-item"> + <resources> + <stylesheet src="chrome://global/skin/radio.css"/> + </resources> + + <content> + <xul:image class="radio-check" xbl:inherits="disabled,selected"/> + <xul:hbox class="radio-label-box" align="center" flex="1"> + <xul:image class="radio-icon" xbl:inherits="src"/> + <xul:label class="radio-label" xbl:inherits="xbl:text=label,accesskey,crop" flex="1"/> + </xul:hbox> + </content> + + <implementation implements="nsIDOMXULSelectControlItemElement"> + <constructor> + <![CDATA[ + // Just clear out the parent's cached list of radio children + var control = this.control; + if (control) + control._radioChildren = null; + ]]> + </constructor> + <destructor> + <![CDATA[ + if (!this.control) + return; + + var radioList = this.control._radioChildren; + if (!radioList) + return; + for (var i = 0; i < radioList.length; ++i) { + if (radioList[i] == this) { + radioList.splice(i, 1); + return; + } + } + ]]> + </destructor> + <property name="selected" readonly="true"> + <getter> + <![CDATA[ + return this.hasAttribute('selected'); + ]]> + </getter> + </property> + <property name="radioGroup" readonly="true" onget="return this.control"/> + <property name="control" readonly="true"> + <getter> + <![CDATA[ + const XUL_NS = "http://www.mozilla.org/keymaster/" + + "gatekeeper/there.is.only.xul"; + var parent = this.parentNode; + while (parent) { + if ((parent.namespaceURI == XUL_NS) && + (parent.localName == "radiogroup")) { + return parent; + } + parent = parent.parentNode; + } + + var group = this.getAttribute("group"); + if (!group) { + return null; + } + + parent = this.ownerDocument.getElementById(group); + if (!parent || + (parent.namespaceURI != XUL_NS) || + (parent.localName != "radiogroup")) { + parent = null; + } + return parent; + ]]> + </getter> + </property> + </implementation> + <handlers> + <handler event="click" button="0"> + <![CDATA[ + if (!this.disabled) + this.control.selectedItem = this; + ]]> + </handler> + + <handler event="mousedown" button="0"> + <![CDATA[ + if (!this.disabled) + this.control.focusedItem = this; + ]]> + </handler> + </handlers> + </binding> +</bindings> |