summaryrefslogtreecommitdiffstats
path: root/toolkit/content/widgets/radio.xml
diff options
context:
space:
mode:
Diffstat (limited to 'toolkit/content/widgets/radio.xml')
-rw-r--r--toolkit/content/widgets/radio.xml526
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>