1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/toolkit/content/widgets/radio.xml Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,520 @@ 1.4 +<?xml version="1.0"?> 1.5 +<!-- This Source Code Form is subject to the terms of the Mozilla Public 1.6 + - License, v. 2.0. If a copy of the MPL was not distributed with this 1.7 + - file, You can obtain one at http://mozilla.org/MPL/2.0/. --> 1.8 + 1.9 + 1.10 +<bindings id="radioBindings" 1.11 + xmlns="http://www.mozilla.org/xbl" 1.12 + xmlns:xul="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" 1.13 + xmlns:xbl="http://www.mozilla.org/xbl"> 1.14 + 1.15 + <binding id="radiogroup" role="xul:radiogroup" 1.16 + extends="chrome://global/content/bindings/general.xml#basecontrol"> 1.17 + <resources> 1.18 + <stylesheet src="chrome://global/skin/radio.css"/> 1.19 + </resources> 1.20 + 1.21 + <implementation implements="nsIDOMXULSelectControlElement"> 1.22 + <constructor> 1.23 + <![CDATA[ 1.24 + if (this.getAttribute("disabled") == "true") 1.25 + this.disabled = true; 1.26 + 1.27 + var children = this._getRadioChildren(); 1.28 + var length = children.length; 1.29 + for (var i = 0; i < length; i++) { 1.30 + if (children[i].getAttribute("selected") == "true") { 1.31 + this.selectedIndex = i; 1.32 + return; 1.33 + } 1.34 + } 1.35 + 1.36 + var value = this.value; 1.37 + if (value) 1.38 + this.value = value; 1.39 + else 1.40 + this.selectedIndex = 0; 1.41 + ]]> 1.42 + </constructor> 1.43 + 1.44 + <property name="value" onget="return this.getAttribute('value');"> 1.45 + <setter> 1.46 + <![CDATA[ 1.47 + this.setAttribute("value", val); 1.48 + var children = this._getRadioChildren(); 1.49 + for (var i = 0; i < children.length; i++) { 1.50 + if (String(children[i].value) == String(val)) { 1.51 + this.selectedItem = children[i]; 1.52 + break; 1.53 + } 1.54 + } 1.55 + return val; 1.56 + ]]> 1.57 + </setter> 1.58 + </property> 1.59 + <property name="disabled"> 1.60 + <getter> 1.61 + <![CDATA[ 1.62 + if (this.getAttribute('disabled') == 'true') 1.63 + return true; 1.64 + var children = this._getRadioChildren(); 1.65 + for (var i = 0; i < children.length; ++i) { 1.66 + if (!children[i].hidden && !children[i].collapsed && !children[i].disabled) 1.67 + return false; 1.68 + } 1.69 + return true; 1.70 + ]]> 1.71 + </getter> 1.72 + <setter> 1.73 + <![CDATA[ 1.74 + if (val) 1.75 + this.setAttribute('disabled', 'true'); 1.76 + else 1.77 + this.removeAttribute('disabled'); 1.78 + var children = this._getRadioChildren(); 1.79 + for (var i = 0; i < children.length; ++i) { 1.80 + children[i].disabled = val; 1.81 + } 1.82 + return val; 1.83 + ]]> 1.84 + </setter> 1.85 + </property> 1.86 + 1.87 + <property name="itemCount" readonly="true" 1.88 + onget="return this._getRadioChildren().length"/> 1.89 + 1.90 + <property name="selectedIndex"> 1.91 + <getter> 1.92 + <![CDATA[ 1.93 + var children = this._getRadioChildren(); 1.94 + for (var i = 0; i < children.length; ++i) { 1.95 + if (children[i].selected) 1.96 + return i; 1.97 + } 1.98 + return -1; 1.99 + ]]> 1.100 + </getter> 1.101 + <setter> 1.102 + <![CDATA[ 1.103 + this.selectedItem = this._getRadioChildren()[val]; 1.104 + return val; 1.105 + ]]> 1.106 + </setter> 1.107 + </property> 1.108 + 1.109 + <property name="selectedItem"> 1.110 + <getter> 1.111 + <![CDATA[ 1.112 + var children = this._getRadioChildren(); 1.113 + for (var i = 0; i < children.length; ++i) { 1.114 + if (children[i].selected) 1.115 + return children[i]; 1.116 + } 1.117 + return null; 1.118 + ]]> 1.119 + </getter> 1.120 + <setter> 1.121 + <![CDATA[ 1.122 + var focused = this.getAttribute("focused") == "true"; 1.123 + var alreadySelected = false; 1.124 + 1.125 + if (val) { 1.126 + alreadySelected = val.getAttribute("selected") == "true"; 1.127 + val.setAttribute("focused", focused); 1.128 + val.setAttribute("selected", "true"); 1.129 + this.setAttribute("value", val.value); 1.130 + } 1.131 + else { 1.132 + this.removeAttribute("value"); 1.133 + } 1.134 + 1.135 + // uncheck all other group nodes 1.136 + var children = this._getRadioChildren(); 1.137 + var previousItem = null; 1.138 + for (var i = 0; i < children.length; ++i) { 1.139 + if (children[i] != val) { 1.140 + if (children[i].getAttribute("selected") == "true") 1.141 + previousItem = children[i]; 1.142 + 1.143 + children[i].removeAttribute("selected"); 1.144 + children[i].removeAttribute("focused"); 1.145 + } 1.146 + } 1.147 + 1.148 + var event = document.createEvent("Events"); 1.149 + event.initEvent("select", false, true); 1.150 + this.dispatchEvent(event); 1.151 + 1.152 + if (!alreadySelected && focused) { 1.153 + // Only report if actual change 1.154 + var myEvent; 1.155 + if (val) { 1.156 + myEvent = document.createEvent("Events"); 1.157 + myEvent.initEvent("RadioStateChange", true, true); 1.158 + val.dispatchEvent(myEvent); 1.159 + } 1.160 + 1.161 + if (previousItem) { 1.162 + myEvent = document.createEvent("Events"); 1.163 + myEvent.initEvent("RadioStateChange", true, true); 1.164 + previousItem.dispatchEvent(myEvent); 1.165 + } 1.166 + } 1.167 + 1.168 + return val; 1.169 + ]]> 1.170 + </setter> 1.171 + </property> 1.172 + 1.173 + <property name="focusedItem"> 1.174 + <getter> 1.175 + <![CDATA[ 1.176 + var children = this._getRadioChildren(); 1.177 + for (var i = 0; i < children.length; ++i) { 1.178 + if (children[i].getAttribute("focused") == "true") 1.179 + return children[i]; 1.180 + } 1.181 + return null; 1.182 + ]]> 1.183 + </getter> 1.184 + <setter> 1.185 + <![CDATA[ 1.186 + if (val) val.setAttribute("focused", "true"); 1.187 + 1.188 + // unfocus all other group nodes 1.189 + var children = this._getRadioChildren(); 1.190 + for (var i = 0; i < children.length; ++i) { 1.191 + if (children[i] != val) 1.192 + children[i].removeAttribute("focused"); 1.193 + } 1.194 + return val; 1.195 + ]]> 1.196 + </setter> 1.197 + </property> 1.198 + 1.199 + <method name="checkAdjacentElement"> 1.200 + <parameter name="aNextFlag"/> 1.201 + <body> 1.202 + <![CDATA[ 1.203 + var currentElement = this.focusedItem || this.selectedItem; 1.204 + var i; 1.205 + var children = this._getRadioChildren(); 1.206 + for (i = 0; i < children.length; ++i ) { 1.207 + if (children[i] == currentElement) 1.208 + break; 1.209 + } 1.210 + var index = i; 1.211 + 1.212 + if (aNextFlag) { 1.213 + do { 1.214 + if (++i == children.length) 1.215 + i = 0; 1.216 + if (i == index) 1.217 + break; 1.218 + } 1.219 + while (children[i].hidden || children[i].collapsed || children[i].disabled); 1.220 + // XXX check for display/visibility props too 1.221 + 1.222 + this.selectedItem = children[i]; 1.223 + children[i].doCommand(); 1.224 + } 1.225 + else { 1.226 + do { 1.227 + if (i == 0) 1.228 + i = children.length; 1.229 + if (--i == index) 1.230 + break; 1.231 + } 1.232 + while (children[i].hidden || children[i].collapsed || children[i].disabled); 1.233 + // XXX check for display/visibility props too 1.234 + 1.235 + this.selectedItem = children[i]; 1.236 + children[i].doCommand(); 1.237 + } 1.238 + ]]> 1.239 + </body> 1.240 + </method> 1.241 + <field name="_radioChildren">null</field> 1.242 + <method name="_getRadioChildren"> 1.243 + <body> 1.244 + <![CDATA[ 1.245 + if (this._radioChildren) 1.246 + return this._radioChildren; 1.247 + 1.248 + var radioChildren = []; 1.249 + var doc = this.ownerDocument; 1.250 + 1.251 + if (this.hasChildNodes()) { 1.252 + // Don't store the collected child nodes immediately, 1.253 + // collecting the child nodes could trigger constructors 1.254 + // which would blow away our list. 1.255 + 1.256 + const nsIDOMNodeFilter = Components.interfaces.nsIDOMNodeFilter; 1.257 + var iterator = doc.createTreeWalker(this, 1.258 + nsIDOMNodeFilter.SHOW_ELEMENT, 1.259 + this._filterRadioGroup); 1.260 + while (iterator.nextNode()) 1.261 + radioChildren.push(iterator.currentNode); 1.262 + return this._radioChildren = radioChildren; 1.263 + } 1.264 + 1.265 + // We don't have child nodes. 1.266 + const XUL_NS = "http://www.mozilla.org/keymaster/" 1.267 + + "gatekeeper/there.is.only.xul"; 1.268 + var elems = doc.getElementsByAttribute("group", this.id); 1.269 + for (var i = 0; i < elems.length; i++) { 1.270 + if ((elems[i].namespaceURI == XUL_NS) && 1.271 + (elems[i].localName == "radio")) { 1.272 + radioChildren.push(elems[i]); 1.273 + } 1.274 + } 1.275 + return this._radioChildren = radioChildren; 1.276 + ]]> 1.277 + </body> 1.278 + </method> 1.279 + <method name="_filterRadioGroup"> 1.280 + <parameter name="node"/> 1.281 + <body> 1.282 + <![CDATA[ 1.283 + switch (node.localName) { 1.284 + case "radio": return NodeFilter.FILTER_ACCEPT; 1.285 + case "template": 1.286 + case "radiogroup": return NodeFilter.FILTER_REJECT; 1.287 + default: return NodeFilter.FILTER_SKIP; 1.288 + } 1.289 + ]]> 1.290 + </body> 1.291 + </method> 1.292 + 1.293 + <method name="getIndexOfItem"> 1.294 + <parameter name="item"/> 1.295 + <body> 1.296 + return this._getRadioChildren().indexOf(item); 1.297 + </body> 1.298 + </method> 1.299 + 1.300 + <method name="getItemAtIndex"> 1.301 + <parameter name="index"/> 1.302 + <body> 1.303 + <![CDATA[ 1.304 + var children = this._getRadioChildren(); 1.305 + return (index >= 0 && index < children.length) ? children[index] : null; 1.306 + ]]> 1.307 + </body> 1.308 + </method> 1.309 + 1.310 + <method name="appendItem"> 1.311 + <parameter name="label"/> 1.312 + <parameter name="value"/> 1.313 + <body> 1.314 + <![CDATA[ 1.315 + var XULNS = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"; 1.316 + var radio = document.createElementNS(XULNS, "radio"); 1.317 + radio.setAttribute("label", label); 1.318 + radio.setAttribute("value", value); 1.319 + this.appendChild(radio); 1.320 + this._radioChildren = null; 1.321 + return radio; 1.322 + ]]> 1.323 + </body> 1.324 + </method> 1.325 + 1.326 + <method name="insertItemAt"> 1.327 + <parameter name="index"/> 1.328 + <parameter name="label"/> 1.329 + <parameter name="value"/> 1.330 + <body> 1.331 + <![CDATA[ 1.332 + var XULNS = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"; 1.333 + var radio = document.createElementNS(XULNS, "radio"); 1.334 + radio.setAttribute("label", label); 1.335 + radio.setAttribute("value", value); 1.336 + var before = this.getItemAtIndex(index); 1.337 + if (before) 1.338 + before.parentNode.insertBefore(radio, before); 1.339 + else 1.340 + this.appendChild(radio); 1.341 + this._radioChildren = null; 1.342 + return radio; 1.343 + ]]> 1.344 + </body> 1.345 + </method> 1.346 + 1.347 + <method name="removeItemAt"> 1.348 + <parameter name="index"/> 1.349 + <body> 1.350 + <![CDATA[ 1.351 + var remove = this.getItemAtIndex(index); 1.352 + if (remove) { 1.353 + remove.parentNode.removeChild(remove); 1.354 + this._radioChildren = null; 1.355 + } 1.356 + return remove; 1.357 + ]]> 1.358 + </body> 1.359 + </method> 1.360 + </implementation> 1.361 + 1.362 + <handlers> 1.363 + <handler event="mousedown"> 1.364 + if (this.disabled) 1.365 + event.preventDefault(); 1.366 + </handler> 1.367 + 1.368 + <!-- keyboard navigation --> 1.369 + <!-- Here's how keyboard navigation works in radio groups on Windows: 1.370 + The group takes 'focus' 1.371 + The user is then free to navigate around inside the group 1.372 + using the arrow keys. Accessing previous or following radio buttons 1.373 + is done solely through the arrow keys and not the tab button. Tab 1.374 + takes you to the next widget in the tab order --> 1.375 + <handler event="keypress" key=" " phase="target"> 1.376 + this.selectedItem = this.focusedItem; 1.377 + this.selectedItem.doCommand(); 1.378 + </handler> 1.379 + <handler event="keypress" keycode="VK_UP" phase="target"> 1.380 + this.checkAdjacentElement(false); 1.381 + event.stopPropagation(); 1.382 + </handler> 1.383 + <handler event="keypress" keycode="VK_LEFT" phase="target"> 1.384 + // left arrow goes back when we are ltr, forward when we are rtl 1.385 + this.checkAdjacentElement(document.defaultView.getComputedStyle( 1.386 + this, "").direction == "rtl"); 1.387 + event.stopPropagation(); 1.388 + </handler> 1.389 + <handler event="keypress" keycode="VK_DOWN" phase="target"> 1.390 + this.checkAdjacentElement(true); 1.391 + event.stopPropagation(); 1.392 + </handler> 1.393 + <handler event="keypress" keycode="VK_RIGHT" phase="target"> 1.394 + // right arrow goes forward when we are ltr, back when we are rtl 1.395 + this.checkAdjacentElement(document.defaultView.getComputedStyle( 1.396 + this, "").direction == "ltr"); 1.397 + event.stopPropagation(); 1.398 + </handler> 1.399 + 1.400 + <!-- set a focused attribute on the selected item when the group 1.401 + receives focus so that we can style it as if it were focused even though 1.402 + it is not (Windows platform behaviour is for the group to receive focus, 1.403 + not the item --> 1.404 + <handler event="focus" phase="target"> 1.405 + <![CDATA[ 1.406 + this.setAttribute("focused", "true"); 1.407 + if (this.focusedItem) 1.408 + return; 1.409 + 1.410 + var val = this.selectedItem; 1.411 + if (!val || val.disabled || val.hidden || val.collapsed) { 1.412 + var children = this._getRadioChildren(); 1.413 + for (var i = 0; i < children.length; ++i) { 1.414 + if (!children[i].hidden && !children[i].collapsed && !children[i].disabled) { 1.415 + val = children[i]; 1.416 + break; 1.417 + } 1.418 + } 1.419 + } 1.420 + this.focusedItem = val; 1.421 + ]]> 1.422 + </handler> 1.423 + <handler event="blur" phase="target"> 1.424 + this.removeAttribute("focused"); 1.425 + this.focusedItem = null; 1.426 + </handler> 1.427 + </handlers> 1.428 + </binding> 1.429 + 1.430 + <binding id="radio" role="xul:radiobutton" 1.431 + extends="chrome://global/content/bindings/general.xml#control-item"> 1.432 + <resources> 1.433 + <stylesheet src="chrome://global/skin/radio.css"/> 1.434 + </resources> 1.435 + 1.436 + <content> 1.437 + <xul:image class="radio-check" xbl:inherits="disabled,selected"/> 1.438 + <xul:hbox class="radio-label-box" align="center" flex="1"> 1.439 + <xul:image class="radio-icon" xbl:inherits="src"/> 1.440 + <xul:label class="radio-label" xbl:inherits="xbl:text=label,accesskey,crop" flex="1"/> 1.441 + </xul:hbox> 1.442 + </content> 1.443 + 1.444 + <implementation implements="nsIDOMXULSelectControlItemElement"> 1.445 + <constructor> 1.446 + <![CDATA[ 1.447 + // Just clear out the parent's cached list of radio children 1.448 + var control = this.control; 1.449 + if (control) 1.450 + control._radioChildren = null; 1.451 + ]]> 1.452 + </constructor> 1.453 + <destructor> 1.454 + <![CDATA[ 1.455 + if (!this.radioGroup) 1.456 + return; 1.457 + 1.458 + var radioList = this.radioGroup.mRadioChildren; 1.459 + if (!radioList) 1.460 + return; 1.461 + for (var i = 0; i < radioList.length; ++i) { 1.462 + if (radioList[i] == this) { 1.463 + radioList.splice(i, 1); 1.464 + return; 1.465 + } 1.466 + } 1.467 + ]]> 1.468 + </destructor> 1.469 + <property name="selected" readonly="true"> 1.470 + <getter> 1.471 + <![CDATA[ 1.472 + return this.hasAttribute('selected'); 1.473 + ]]> 1.474 + </getter> 1.475 + </property> 1.476 + <property name="radioGroup" readonly="true" onget="return this.control"/> 1.477 + <property name="control" readonly="true"> 1.478 + <getter> 1.479 + <![CDATA[ 1.480 + const XUL_NS = "http://www.mozilla.org/keymaster/" 1.481 + + "gatekeeper/there.is.only.xul"; 1.482 + var parent = this.parentNode; 1.483 + while (parent) { 1.484 + if ((parent.namespaceURI == XUL_NS) && 1.485 + (parent.localName == "radiogroup")) { 1.486 + return parent; 1.487 + } 1.488 + parent = parent.parentNode; 1.489 + } 1.490 + 1.491 + var group = this.getAttribute("group"); 1.492 + if (!group) { 1.493 + return null; 1.494 + } 1.495 + 1.496 + parent = this.ownerDocument.getElementById(group); 1.497 + if (!parent || 1.498 + (parent.namespaceURI != XUL_NS) || 1.499 + (parent.localName != "radiogroup")) { 1.500 + parent = null; 1.501 + } 1.502 + return parent; 1.503 + ]]> 1.504 + </getter> 1.505 + </property> 1.506 + </implementation> 1.507 + <handlers> 1.508 + <handler event="click" button="0"> 1.509 + <![CDATA[ 1.510 + if (!this.disabled) 1.511 + this.control.selectedItem = this; 1.512 + ]]> 1.513 + </handler> 1.514 + 1.515 + <handler event="mousedown" button="0"> 1.516 + <![CDATA[ 1.517 + if (!this.disabled) 1.518 + this.control.focusedItem = this; 1.519 + ]]> 1.520 + </handler> 1.521 + </handlers> 1.522 + </binding> 1.523 +</bindings>