RetroZilla/extensions/xforms/resources/content/select.xml
2015-10-20 23:03:22 -04:00

1208 lines
43 KiB
XML

<?xml version="1.0"?>
<!-- ***** BEGIN LICENSE BLOCK *****
- Version: MPL 1.1/GPL 2.0/LGPL 2.1
-
- The contents of this file are subject to the Mozilla Public License Version
- 1.1 (the "License"); you may not use this file except in compliance with
- the License. You may obtain a copy of the License at
- http://www.mozilla.org/MPL/
-
- Software distributed under the License is distributed on an "AS IS" basis,
- WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
- for the specific language governing rights and limitations under the
- License.
-
- The Original Code is Mozilla XForms support.
-
- The Initial Developer of the Original Code is
- IBM Corporation.
- Portions created by the Initial Developer are Copyright (C) 2005
- the Initial Developer. All Rights Reserved.
-
- Contributor(s):
- Doron Rosenberg <doronr@us.ibm.com>
- Olli Pettay <Olli.Pettay@helsinki.fi>
- Alexander Surkov <surkov.alexander@gmail.com>
-
- Alternatively, the contents of this file may be used under the terms of
- either the GNU General Public License Version 2 or later (the "GPL"), or
- the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
- in which case the provisions of the GPL or the LGPL are applicable instead
- of those above. If you wish to allow use of your version of this file only
- under the terms of either the GPL or the LGPL, and not to allow others to
- use your version of this file under the terms of the MPL, indicate your
- decision by deleting the provisions above and replace them with the notice
- and other provisions required by the GPL or the LGPL. If you do not delete
- the provisions above, a recipient may use your version of this file under
- the terms of any one of the MPL, the GPL or the LGPL.
-
- ***** END LICENSE BLOCK ***** -->
<bindings id="xformsSelectBindings"
xmlns="http://www.mozilla.org/xbl"
xmlns:html="http://www.w3.org/1999/xhtml"
xmlns:xbl="http://www.mozilla.org/xbl">
<!--
This file implements the "abstract" UI class for XForms select controls. It
have "pure virtual" functions that it expect to be implemented by concrete
application and returned in the getElementControl() call. An example is the
controls for XHTML in select-xhtml.xml.
The "abstact" UI class parses the child nodes of the xforms:select and then
constructs the anonymous content programmatically.
The interface of object returned by getElementControl() method is:
readonly - set/get readonly state
appendItem(label, value, group) - appends item, returns item control
@param label - label control
@param value - item's value
@param group - parent group
appendGroup(label, group) - appends group, returns group control
@param label - label control
@param group - parent group
removeAllItems() - remove all items
addItemToSelection(item) - selects item
@param item - item control returned by appendItem()
removeItemFromSelection(item) - unselects item
@param item - item control returned by appendItem()
isItemSelected(item) - return true if item is selected
@param item - item control returned by appendItem()
getFreeEntryValues() - return space delimited list of free entry items values
allowFreeEntry(allowed) - enable/disable free entry
@param allowed - boolean value
appendFreeEntryItem(value) - append and select free entry item
@param value - item value
-->
<!-- BASE for select/select1 elements. -->
<binding id="xformswidget-selectcontrols-base"
extends="chrome://xforms/content/xforms.xml#xformswidget-base">
<implementation implements="nsIXFormsUIWidget">
<!-- Make sure we don't refresh while we are refreshing (race condition).
This happens when we are inside a repeat for example. We use the
_refreshing field to store if we are refreshing or not. -->
<field name="_refreshing">false</field>
<method name="refresh">
<body>
<![CDATA[
if (this._refreshing)
return true;
this.control.readonly = this.accessors.isReadonly();
this._refreshing = true;
// if this node contains a non TEXT node, then we have to throw
// the 'just string values' logic out the window
var boundNode = this.accessors.getBoundNode();
var containsNonText = false;
if (boundNode && boundNode.hasChildNodes()) {
var child = boundNode.firstChild;
while (child) {
var type = child.nodeType;
if (type != Node.TEXT_NODE && type != Node.CDATA_SECTION_NODE) {
containsNonText = true;
this._accessorValueCache = null;
break;
}
child = child.nextSibling;
}
}
// We detect if the instance data we bind to has changed. If it has,
// changed, we simply update the selection. If it hasn't, that means
// we rebuild the select UI. We also rebuild if the accessor cache is
// null (first load).
if (this._accessorValueCache == null ||
this._accessorValueCache == this.accessors.getValue() ||
containsNonText) {
// if we reached here and the instance data only contains text
// nodes, then we need to rebuild the control since we know it
// wasn't due to a simple instance data changing scenario. But if
// the bound node contains non TEXT nodes, then it is too expensive
// to figure out if this was because a child node changed somewhere
// along the way. We'd basically have to cache the whole bound node
// subtree to compare against. To avoid this we'll just rebuild the
// control from scratch.
// XXX at a future time we need to figure out which will be more
// efficient give the most probable use cases.
// builds select UI
this._buildDefaultValues(containsNonText);
this._buildSelect();
} else if (!containsNonText) {
// update selection
this._updateDefaultValues();
this._updateSelection();
// store the accessor value
this._accessorValueCache = this.accessors.getValue();
}
this._refreshing = false;
return true;
]]>
</body>
</method>
<method name="focus">
<body>
<![CDATA[
this.control.focus();
return true;
]]>
</body>
</method>
<method name="getCurrentValue">
<body>
throw Components.results.NS_ERROR_NOT_IMPLEMENTED;
</body>
</method>
<property name="incremental">
<getter>
<![CDATA[
// default is true
var incremental = true;
if (this.hasAttribute("incremental")) {
if (this.getAttribute("incremental") == "false")
incremental = false;
}
return incremental;
]]>
</getter>
</property>
<property name="selection">
<getter>
return this.getAttribute("selection");
</getter>
<setter>
this.setAttribute("selection", val);
if (val == "open")
this.control.allowFreeEntry(true);
else
this.control.allowFreeEntry(false);
</setter>
</property>
<method name="_buildSelect">
<body>
<![CDATA[
// select builds its own UI by parsing it's children.
// clear the UI children
this.control.removeAllItems();
// create children
var child, option;
var childNodes = this.childNodes;
// these hold an array of generated HTML controls
this._controlArray = new Array();
for (var i = 0; i < childNodes.length; i++) {
child = childNodes[i];
// we only care about element nodes in the XForms namespace.
if (child.nodeType != child.ELEMENT_NODE ||
child.namespaceURI != this.XFORMS_NS) {
continue;
}
switch (child.localName) {
case "item":
this._buildItem(child);
break;
case "choices":
this._buildChoices(child);
break;
case "itemset":
this._buildItemset(child);
break;
}
}
var outOfRange = false;
var selectionOpen = this.selection == 'open';
// check if any default values were not found
for (var index in this._defaultHash) {
if (this._defaultHash[index].hits == 0) {
if (selectionOpen) {
this.control.appendFreeEntryItem(index);
} else {
// some of default values not found, we need to throw an
// xforms-out-of-range event, but only if the select is 'closed'.
outOfRange = true;
}
}
}
// check if any default elements were not found
for (var j = 0; j < this._selectedElementArray.length; j++) {
if (this._selectedElementArray[j].hits == 0) {
if (selectionOpen) {
// XXX: If the select is open, the missing elements should be added
// and selected per 8.1.10 in the spec.
} else {
// some of default values not found, we need to throw an
// xforms-out-of-range event, but only if the select is 'closed'.
outOfRange = true;
}
}
}
// a control can't be out of range (or in range for that matter) if
// there is no selected value, so make sure that there is a bound
// node first.
if (!selectionOpen && this.accessors.hasBoundNode() &&
(outOfRange != this._outOfRange)) {
this._outOfRange = outOfRange;
this.accessors.setInRange(!outOfRange);
}
return true;
]]>
</body>
</method>
<method name="_buildDefaultValues">
<parameter name="aContainsNonText"/>
<body><![CDATA[
// builds default values hash
// if accessors.value has something, then only text node(s) should
// exist under the bound node
// create a hash from the default values so we can store how often
// we encountered them. This allows us to figure out later if any
// were not hit, which requires us to send an event.
this._defaultHash = new Object();
// holds an array of DOMElements that exist under bound node,
this._selectedElementArray = new Array();
if (!aContainsNonText) {
var selectedArray = this._getValuesArray();
for (var run = 0; run < selectedArray.length; run++) {
this._defaultHash[selectedArray[run]] = {hits: 0}
}
return;
}
var boundNode = this.accessors.getBoundNode();
var child = boundNode ? boundNode.firstChild : null;
var childcount = 0;
for (; child; child = child.nextSibling) {
var type = child.nodeType;
if (type == Node.TEXT_NODE || type == Node.CDATA_SECTION_NODE) {
// if child is a text node completely filled with
// whitespace let's ignore it and get the next node
var string = child.nodeValue;
var nonWhitespace = false;
if (string) {
// this regexp tests whether only whitespace is contained
// between the beginning and ending of the string.
nonWhitespace = !(/^\s*$/.test(string));
}
if (nonWhitespace) {
if (++childcount > 1 && this.localName == "select1")
break;
// When the string is empty, the method returns an array
// containing one empty string, rather than an empty array.
// We'll allow that into the defaultHash so that
// 'xforms-out-of-range' will be correctly generated if none of
// the items in the select have an empty string as a value.
var selectedArray = this._getValuesArray(string);
for (var run = 0; run < selectedArray.length; run++) {
this._defaultHash[selectedArray[run]] = {hits: 0}
}
}
} else {
if (++childcount > 1 && this.localName == "select1")
break;
// if it's not a text node, we'll assume that we are looking at
// a node worth comparing. As such, look for an
// item with a copy element that might match this node.
this._selectedElementArray.push({element: child, hits: 0});
}
}
if (this.localName == "select1") {
var outOfRange = childcount > 1;
if (outOfRange != this._outOfRange) {
this._outOfRange = outOfRange;
this.accessors.setInRange(!outOfRange);
if (outOfRange)
return;
}
}
]]></body>
</method>
<method name="_buildItem">
<parameter name="aItem"/>
<parameter name="aParentGroup"/>
<body>
// cache the item's label/value
aItem = aItem.QueryInterface(Components.interfaces.nsIXFormsItemElement);
var labels = aItem.getElementsByTagNameNS(this.XFORMS_NS, "label");
var controlitem;
if (labels[0]) {
controlitem = this.control.
appendItem(labels[0].nodeValue, aItem.value, aParentGroup);
} else {
controlitem = this.control.
appendItem(null, aItem.value, aParentGroup);
}
var selected = false;
if (aItem.value in this._defaultHash) {
selected = true;
this.control.addItemToSelection(controlitem);
this._defaultHash[aItem.value].hits++;
}
this._controlArray.push(
{control: aItem, option: controlitem, wasSelected: selected}
);
</body>
</method>
<method name="_buildChoices">
<parameter name="aChoice"/>
<parameter name="aParentGroup"/>
<body>
<![CDATA[
// finds label for a choice
for (var i = 0; i < aChoice.childNodes.length; i++) {
var item = aChoice.childNodes[i];
if (item.nodeType == item.ELEMENT_NODE &&
item.namespaceURI == this.XFORMS_NS &&
item.localName == "label") {
break;
}
}
// creates group for a choice
var group;
if (i != aChoice.childNodes.length) {
// XXX: If xf:label is bounded to instance data then since xf:label
// gets refresh after xf:choices refresh then label.nodeValue returns
// incorrect value. Threafore we use xf:label node instead of
// node returned by label.nodeValue. It is workaround and we should
// use label.nodeValue when the problem related with it will be fixed.
var label = aChoice.childNodes[i];
group = this.control.appendGroup(label, aParentGroup);
} else {
group = this.control.appendGroup(null, aParentGroup);
}
// creates the items for a choice and it's children
for (var i = 0; i < aChoice.childNodes.length; i++) {
var item = aChoice.childNodes[i];
if (item.nodeType != item.ELEMENT_NODE)
continue;
switch (item.localName) {
case "item":
this._buildItem(item, group);
break;
case "choices":
this._buildChoices(item, group);
break;
}
}
]]>
</body>
</method>
<method name="_buildItemset">
<parameter name="aItemset"/>
<body>
<![CDATA[
aItemset = aItemset.
QueryInterface(Components.interfaces.nsIXFormsItemSetUIElement);
var containers = aItemset.anonymousItemSetContent.childNodes;
// go through each item in the itemset and add it to the
// html:select. Select any of the items that contain a value
// that also appears under the bound node.
for (var y = 0; y < containers.length; y++) {
if (containers[y].nodeType != Node.ELEMENT_NODE)
continue;
var item = containers[y].
QueryInterface(Components.interfaces.nsIXFormsItemElement);
var copyItem = item.isCopyItem;
var valueText = copyItem ? "" : item.value;
var labelText = item.labelText;
var textNode = this.ownerDocument.createTextNode(labelText);
var controlitem =
this.control.appendItem(textNode, valueText);
var selected = false;
// if this item contains a copy element AND if the bound node contains
// non-text elements, then see if any of these non-text elements match
// this copyItem's node.
if (copyItem && this._selectedElementArray.length > 0) {
item = item.QueryInterface(Components.interfaces.nsIXFormsSelectChild);
for (var j = 0; j < this._selectedElementArray.length; j++ ) {
var selectedItem =
item.selectItemByNode(this._selectedElementArray[j].element);
if (selectedItem) {
this.control.addItemToSelection(controlitem);
selected = true;
this._selectedElementArray[j].hits++;
// XXX It is possible that two identical elements are under the
// bound node. I guess we shouldn't mark one and not the other
// if there is an item in the select that matches it. So we'll
// go through the whole list. But this is quite an edge case
// and will cause more inefficiency just to prevent an errant
// xforms-out-of-range.
}
}
}
if (!copyItem) {
if (item.value in this._defaultHash) {
this.control.addItemToSelection(controlitem);
selected = true;
this._defaultHash[item.value].hits++;
}
}
this._controlArray.push(
{control: item, option: controlitem, wasSelected: selected}
);
}
]]>
</body>
</method>
<method name="_updateSelection">
<body>
<![CDATA[
// select if found, unselect if not
var options = this._controlArray;
for (var i = 0; i < options.length; i++) {
var item = options[i].control.
QueryInterface(Components.interfaces.nsIXFormsSelectChild);
if (item.isCopyItem)
// _updateSelection is used only to select items when _defaultHash
// contains only text nodes. So we can ignore copyItems.
continue;
var value = item.value;
var selectionValue = value in this._defaultHash;
if (selectionValue) {
this.control.addItemToSelection(options[i].option);
options[i].wasSelected = true;
this._defaultHash[value].hits++;
}
else {
this.control.removeItemFromSelection(options[i].option);
options[i].wasSelected = false;
}
}
// see if any of default values are not found. If so, we need to
// throw an xforms-out-of-range event and style the control as
// being out of range. But only if the select is 'closed'.
if (this.selection == 'open') {
return;
}
var outOfRange = false;
var length = 0;
for (var index in this._defaultHash) {
length++;
if (this._defaultHash[index].hits == 0) {
outOfRange = true;
}
}
if (length == 0) {
// this covers the scenario where some items were previously
// selected and the user selected a copyItem, yet because the
// bound node wasn't an element node the copyItem was deselected
// and the bound node was set to empty. If there is no selected
// item, then we must be out of range.
outOfRange = true;
}
if (this._outOfRange != outOfRange) {
this._outOfRange = outOfRange;
this.accessors.setInRange(!outOfRange);
}
]]>
</body>
</method>
<method name="_updateDefaultValues">
<body>
<![CDATA[
var selectedArray = this._getValuesArray();
// store the values in a hash for quick access
this._defaultHash = new Object();
for (var run = 0; run < selectedArray.length; run++) {
this._defaultHash[selectedArray[run]] = {hits: 0}
}
]]>
</body>
</method>
<!-- Return an array of values to be selected for the given string or of
the bound node if string argument is omitted. -->
<method name="_getValuesArray">
<parameter name="aValue"/>
<body>
// When the instance value is empty, then we return an array
// containing one empty string, rather than an empty array. We'll
// allow that into the defaultHash so that 'xforms-out-of-range' event
// will be correctly generated if none of the items in the select have
// an empty string as a value.
if (!aValue) {
aValue = this.accessors.getValue();
if (!aValue) {
aValue = "";
}
}
if (this.localName == "select1")
return [aValue];
// A limitation of the XML Schema list datatypes is that white space
// characters in the storage values (the value element) are always
// interpreted as separators between individual data values.
// Therefore, authors should avoid using white space characters within
// storage values with list simpleContent.
// Replace new line (\n), tabs (\t) and carriage returns (\r) with
// space (" ").
aValue = aValue.replace(/\n|\t|\r/g, " ");
return aValue.split(/\s/);
</body>
</method>
<!-- Updates the instance data bound to this control.
@param aIncremental - if true then it means the update is additional and
instance data will be updated only if @incremental attribute value of
this control is not false.
-->
<method name="updateInstanceData">
<parameter name="aIncremental"/>
<body>
<![CDATA[
// Fire 'xfroms-select'/'xforms-deselect' events and unselect illegaly
// selected items.
var copySelectedOrDeselected = new Boolean();
copySelectedOrDeselected.value = false;
var contentEnvelope =
this._processSelectedValues(copySelectedOrDeselected);
if (!aIncremental || this.incremental) {
if (contentEnvelope) {
this._setBoundValue(contentEnvelope,
copySelectedOrDeselected.value);
}
}
]]>
</body>
</method>
<method name="_setBoundValue">
<parameter name="aContentEnvelope"/>
<parameter name="aCopySelectedOrDeselected"/>
<body>
<![CDATA[
var boundNode = this.accessors.getBoundNode();
if (!boundNode)
return;
// if a copy item is selected or deselected, then we need to replace
// ALL of the current content with the newly selected content. Which
// means calling setContent. setValue only messes with the first
// textnode under the bound node.
if (boundNode.nodeType == Node.ELEMENT_NODE) {
// we shouldn't call setContent if we haven't selected any
// copyItems. We can't just test for a single text node under
// the bound node because this could still have been the result
// of a copyItem being selected. And if a copyItem is selected,
// then we need to do the whole rebuild, recalculate, revalidate,
// refresh process according to the spec...whether we really need
// to or not.
if (!aCopySelectedOrDeselected) {
// well, no copyItems are selected, so need to find the value
// to set. Will be in the first child.
var firstChild = aContentEnvelope.firstChild;
var value = null;
if (firstChild) {
value = firstChild.nodeValue;
}
this.accessors.setValue(value);
} else {
this.accessors.setContent(aContentEnvelope, true);
}
} else {
// if some copyItems were selected by the user prior to the call
// to _processSelectedValues, then we would not have set up
// _accessorValueCache. Since the node we are bound to can't
// be set by copyItems (its not an ELEMENT_NODE), any copyItems
// in this select would have been deselected during
// _processSelectedValues. Thus, anything in the contentEnvelope at
// this point should just be strings and so we can set
// delegate.value directly and use _accessorValueCache after all.
this.accessors.setValue(aContentEnvelope.nodeValue);
this._accessorValueCache = aContentEnvelope.nodeValue;
}
]]>
</body>
</method>
<!--
The method serves to
1) unselect illegally selected items
2) fire "xforms-select"/"xforms-deselect" events
3) return list of selected values
-->
<method name="_processSelectedValues">
<parameter name="aIsACopyItemSelectedOrDeselected"/>
<body>
<![CDATA[
if (aIsACopyItemSelectedOrDeselected) {
aIsACopyItemSelectedOrDeselected.value = false;
}
var boundNode = this.accessors.getBoundNode();
if (!boundNode) {
this._dispatchSelectEvents();
return null;
}
// we are cloning boundNode to create a node that we will return.
// By the end of this function, assuming all went well,
// contentEnvelope will contain the values and copyNodes that are
// represented by the selected items in this xf:select. Cloning
// the boundNode to use as the envelope so that the caller could
// just pass the results straight into accessors.setContent().
var contentEnvelope = null;
contentEnvelope = boundNode.cloneNode(false);
if (!contentEnvelope) {
this._dispatchSelectEvents();
return null;
}
var contentDocument = contentEnvelope.ownerDocument;
var selectedValues = "";
if (this.selection == "open") {
selectedValues = this.control.getFreeEntryValues();
}
var options = this._controlArray;
var boundType = boundNode.nodeType;
var copyNode;
// keep in mind, to maintain compatibility with XSmiles and Novell, we
// ultimately need to end up with all 'value' elements contained in a
// text node and this text node needs to be the first child of the
// bound node.
for (var i = 0; i < options.length; i++) {
var isSelected = this.control.isItemSelected(options[i].option);
if (isSelected) {
// space delimited list
if (selectedValues.length > 0) {
selectedValues += " ";
}
var item = options[i].control.
QueryInterface(Components.interfaces.nsIXFormsItemElement);
if (item.isCopyItem) {
if (boundType && (boundType != Node.ELEMENT_NODE)) {
// if we are trying to do a copy without being bound to an
// element node, then we need to throw a binding exception
// per spec.
var bindingException = document.createEvent("Events");
bindingException.initEvent("xforms-binding-exception", true, false);
this.dispatchEvent(bindingException);
// we should probably un-select the option so that the list
// of selected data is accurate. This WON'T cause a
// xforms-select/deselect to fire. Since the user just
// selected this item and we are automatically deselecting
// it from underneath the user, we'll treat it like nothing
// happened.
this.control.removeItemFromSelection(options[i].option);
} else {
copyNode = item.copyNode;
if (copyNode) {
var clone = contentDocument.importNode(copyNode, true);
contentEnvelope.appendChild(clone);
if (!options[i].wasSelected) {
if (aIsACopyItemSelectedOrDeselected &&
aIsACopyItemSelectedOrDeselected.value != true) {
aIsACopyItemSelectedOrDeselected.value = true;
}
}
}
}
} else {
// not a copyItem, so grab the item's value and append it to our
// space seperated list.
selectedValues += options[i].control.
QueryInterface(Components.interfaces.nsIXFormsSelectChild).value;
}
} else {
// it was selected before, but now unselected
if (options[i].wasSelected) {
// if a copyItem was deselected, we need to make sure to do a
// rebuild. By setting aIsACopyItemSelectedOrDeselected, this
// should tell _handleSelection to use setContent with
// aForceUpdate = true
var item = options[i].control.
QueryInterface(Components.interfaces.nsIXFormsItemElement);
if (item.isCopyItem) {
if (aIsACopyItemSelectedOrDeselected &&
aIsACopyItemSelectedOrDeselected.value != true) {
aIsACopyItemSelectedOrDeselected.value = true;
}
}
}
}
}
// write out the text nodes before we handle copy
if (boundType == Node.ELEMENT_NODE) {
if (selectedValues.length > 0) {
var textNode = contentDocument.createTextNode(selectedValues);
if (copyNode) {
// making sure all selected 'values' are in the first text node
// under the bound node.
var firstChild = contentEnvelope.firstChild;
contentEnvelope.insertBefore(textNode, firstChild);
} else {
contentEnvelope.appendChild(textNode);
}
}
} else {
contentEnvelope.nodeValue = selectedValues;
}
this._dispatchSelectEvents();
return contentEnvelope;
]]>
</body>
</method>
<method name="_dispatchSelectEvents">
<body>
<![CDATA[
var options = this._controlArray;
for (var i = 0; i < options.length; i++) {
var selected = this.control.isItemSelected(options[i].option);
if (options[i].wasSelected && !selected) {
options[i].wasSelected = false;
this.dispatchSelectEvent(options[i].control, "xforms-deselect");
}
}
for (var i = 0; i < options.length; i++) {
var selected = this.control.isItemSelected(options[i].option);
if (!options[i].wasSelected && selected) {
options[i].wasSelected = true;
this.dispatchSelectEvent(options[i].control, "xforms-select");
}
}
]]>
</body>
</method>
<method name="dispatchSelectEvent">
<parameter name="aElement"/>
<parameter name="aName"/>
<body>
<![CDATA[
// per errata for XForms 1.0 second edition, we send the event to
// the item element even if it is contained insided an itemset
return this.dispatchXFormsNotificationEvent(aName, aElement);
]]>
</body>
</method>
<!-- Array of objects that contains objects for all items of this control,
serves to keep xforms items and native items together.
Object has next properties:
control - xforms:item element
option - native widget for xforms:item
wasSelected - flag specifies whether native item was selected or not.
-->
<field name="_controlArray">new Array()</field>
<field name="_selectedElementArray">new Array()</field>
<field name="_defaultHash">null</field>
<field name="_accessorValueCache">null</field>
<field name="_outOfRange">false</field>
<method name="getControlElement">
<body>
return document.getAnonymousElementByAttribute(this, "anonid", "control");
</body>
</method>
</implementation>
</binding>
<!-- SELECT BASE
Implements nsIXFormsNSSelectElement interface.
-->
<binding id="xformswidget-select-base"
extends="#xformswidget-selectcontrols-base">
<implementation implements="nsIXFormsNSSelectElement">
<!-- nsIXFormsNSSelectElement -->
<property name="selectedItems" readonly="true">
<getter>
<![CDATA[
var items = [];
for (var i = 0; i < this._controlArray.length; i++) {
var nativeitem = this._controlArray[i].option;
if (this.control.isItemSelected(nativeitem)) {
var item = this._controlArray[i].control;
items.push(item);
}
}
return items;
]]>
</getter>
</property>
<method name="addItemToSelection">
<parameter name="aItem"/>
<body>
var nativeitem = this.getNativeItem(aItem);
if (!this.control.isItemSelected(nativeitem)) {
this.control.addItemToSelection(nativeitem);
this.updateInstanceData();
}
</body>
</method>
<method name="removeItemFromSelection">
<parameter name="aItem"/>
<body>
var nativeitem = this.getNativeItem(aItem);
if (this.control.isItemSelected(nativeitem)) {
this.control.removeItemFromSelection(nativeitem);
this.updateInstanceData();
}
</body>
</method>
<method name="clearSelection">
<body>
<![CDATA[
changed = false;
for (var i = 0; i < this._controlArray.length; i++) {
var nativeitem = this._controlArray[i].option;
if (this.control.isItemSelected(nativeitem)) {
this.control.removeItemFromSelection(nativeitem);
changed = true;
}
}
if (changed)
this.updateInstanceData();
]]>
</body>
</method>
<method name="selectAll">
<body>
<![CDATA[
var changed = false;
for (var i = 0; i < this._controlArray.length; i++) {
var nativeitem = this._controlArray[i].option;
if (!this.control.isItemSelected(nativeitem)) {
this.control.addItemToSelection(nativeitem);
changed = true;
}
}
if (changed)
this.updateInstanceData();
]]>
</body>
</method>
<method name="isItemSelected">
<parameter name="aItem"/>
<body>
return this.control.isItemSelected(this.getNativeItem(aItem));
</body>
</method>
<!-- private -->
<method name="getNativeItem">
<parameter name="aItem"/>
<body>
<![CDATA[
for (var i = 0; i < this._controlArray.length; i++) {
if (this._controlArray[i].control == aItem)
return this._controlArray[i].option;
}
return null;
]]>
</body>
</method>
</implementation>
</binding>
<!-- SELECT1 BASE
Implements nsIXFormsNSSelect1Element interface.
-->
<binding id="xformswidget-select1-base"
extends="#xformswidget-selectcontrols-base">
<implementation implements="nsIXFormsNSSelect1Element">
<property name="selectedItem">
<getter>
<![CDATA[
for (var i = 0; i < this._controlArray.length; i++) {
var nativeitem = this._controlArray[i].option;
var item = this._controlArray[i].control;
if (this.control.isItemSelected(nativeitem)) {
return item;
}
}
return null;
]]>
</getter>
<setter>
<![CDATA[
var changed = false;
for (var i = 0; i < this._controlArray.length; i++) {
var nativeitem = this._controlArray[i].option;
var item = this._controlArray[i].control;
if (!this.control.isItemSelected(nativeitem)) {
if (item == val) {
this.control.addItemToSelection(nativeitem);
if (changed)
break;
changed = true;
}
} else {
if (item != val) {
this.control.removeItemFromSelection(nativeitem);
if (changed)
break;
changed = true;
}
}
}
if (changed)
this.updateInstanceData();
]]>
</setter>
</property>
</implementation>
</binding>
<!-- BASE CONTROL WIDGET FOR XFORMS SELECTS
The 'controlwidget-base' is the base binding of underlying controls. Thease
underlying controls implement functionality asked for by object returned by
getControlElement() method of xforms select widgets. Examples of the widget
using you can find in 'select-xhtml.xml' file.
All inherited widgets from this one should be also capable of being bound to
directly rather than just through xforms select widgets. We are supporting
the use of a readonly attribute on elements that bind this way.
-->
<binding id="controlwidget-base">
<implementation>
<!-- Return parent xf:select/xf:select1 control. -->
<property name="parentControl" readonly="true">
<getter>
<![CDATA[
// returns 'select' or 'select1' parent control
if (!this._parentControl) {
var root = this.ownerDocument.documentElement;
for (var node = this.parentNode; node != root; node = node.parentNode) {
if (node.namespaceURI == this.XFORMS_NS &&
(node.localName == "select" || node.localName == "select1")) {
this._parentControl = node;
break;
}
}
}
return this._parentControl;
]]>
</getter>
</property>
<field name="_parentControl">null</field>
<!-- Return native control widget. -->
<property name="control" readonly="true">
<getter>
if (!this._control) {
this._control =
document.getAnonymousElementByAttribute(this, "anonid", "control");
}
return this._control;
</getter>
</property>
<field name="_control">null</field>
<!-- Set readonly state. The state is used to enable/disable native
control widget during select items adding.
-->
<property name="readonly">
<getter>
return this.getAttribute("readonly") == "true" ? true : false;
</getter>
<setter>
if (val) {
this.setAttribute("readonly", "true");
} else {
this.removeAttribute("readonly");
}
</setter>
</property>
<!-- Advance focus to native widget. -->
<method name="focus">
<body>
this.control.focus();
</body>
</method>
<!-- Update instance data to native control widget value.
@param aIncremental - will be true if this function is called because
the user selected a new value in the native control widget. It will be
false if this function is called due to focus leaving the native
control widget.
-->
<method name="updateInstanceData">
<parameter name="aIncremental"/>
<body>
<![CDATA[
if (!this.parentControl)
return;
// No need to update the bound value if the control is incremental and
// we are losing focus. It should already be up to date.
if (this.parentControl.incremental && !aIncremental)
return;
this.parentControl.updateInstanceData(aIncremental);
]]>
</body>
</method>
<!-- Dispatch event to parent control. -->
<method name="dispatchDOMUIEvent">
<parameter name="aType"/>
<body>
if (this.parentControl)
this.parentControl.dispatchDOMUIEvent(aType);
</body>
</method>
<!-- Return values of free entry. The method is a stub and it should
be removed when all native controls will support @selection="open".
-->
<method name="getFreeEntryValues">
<body>
return "";
</body>
</method>
<!-- Allow free entry. The method is a stub and it should be removed when
all native controls will support @selection="open".
-->
<method name="allowFreeEntry">
<parameter name="aAllowed"/>
<body>
</body>
</method>
<!-- Append and select free entry item. The method is a stub and it should
be removed when all native controls will support @selection="open".
-->
<method name="appendFreeEntryItem">
<parameter name="aValue"/>
<body>
</body>
</method>
<property name="XFORMS_NS" readonly="true"
onget="return 'http://www.w3.org/2002/xforms';"/>
<property name="XHTML_NS" readonly="true"
onget="return 'http://www.w3.org/1999/xhtml';"/>
<property name="XUL_NS" readonly="true"
onget="return 'http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul';"/>
<property name="XBL_NS" readonly="true"
onget="return 'http://www.mozilla.org/xbl';"/>
<constructor>
if (this.parentControl.selection == "open")
this.allowFreeEntry(true);
else
this.allowFreeEntry(false);
</constructor>
</implementation>
</binding>
</bindings>