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

1217 lines
42 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
- Olli Pettay
- Portions created by the Initial Developer are Copyright (C) 2005
- the Initial Developer. All Rights Reserved.
-
- Contributor(s):
- Olli Pettay <Olli.Pettay@helsinki.fi>
-
- 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="select1Bindings"
xmlns="http://www.mozilla.org/xbl"
xmlns:html="http://www.w3.org/1999/xhtml"
xmlns:xbl="http://www.mozilla.org/xbl"
xmlns:xul="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
xmlns:xforms="http://www.w3.org/2002/xforms"
xmlns:mozType="http://www.mozilla.org/projects/xforms/2005/type">
<!-- select1 -->
<binding id="xformswidget-select1"
extends="chrome://xforms/content/xforms.xml#xformswidget-base">
<!-- The strange indentation is because of the whitespace nodes.-->
<content>
<children includes="label|hint"/>
<html:div class="-moz-xforms-select1-popup" anonid="popup"
onmouseover="this.parentNode.shouldHandleBlur = false;
this.parentNode.mouseOver(event);"
onmouseup="this.parentNode.mouseUp(event);"
onmouseout="this.parentNode.shouldHandleBlur = true;">
<children/>
</html:div>
<html:span class="-moz-select1-container"
anonid="container"><html:input
class="-moz-xforms-select1-input xf-value"
anonid="control"
xbl:inherits="accesskey, tabindex"
onfocus="this.parentNode.parentNode.dispatchDOMUIEvent('DOMFocusIn')"
onblur="this.parentNode.parentNode.handleBlur(); this.parentNode.parentNode.dispatchDOMUIEvent('DOMFocusOut');"
onclick="this.parentNode.parentNode.handleControlClick();"
onkeypress="this.parentNode.parentNode.handleKeyPress(event);"
oninput="this.parentNode.parentNode.handleInput();"
/><html:input mozType:dropmarker="true"
type="button"
anonid="dropmarker"
tabindex="-1"
onmousedown="this.parentNode.parentNode.shouldHandleBlur = false;"
onmouseup="this.parentNode.parentNode.shouldHandleBlur = true;"
onclick="this.parentNode.parentNode.togglePopup();
this.previousSibling.focus();"
/></html:span></content>
<implementation implements="nsIXFormsUIWidget, nsIXFormsNSSelect1Element">
<!-- nsIXFormsNSSelect1Element -->
<property name="selectedItem">
<getter>
return this._selected;
</getter>
<setter>
if (this._selected == val)
return;
if (this._selected)
this._selected.setActive(false);
this._selected =
val.QueryInterface(Components.interfaces.nsIXFormsItemElement);
if (this._selected) {
this._selected.setActive(true);
this.updateInputField();
this._handleSelection(true);
}
</setter>
</property>
<!-- nsIXFormsComboboxUIWidget -->
<property name="open">
<getter>
return this.popupOpen;
</getter>
<setter>
if (val)
this.showPopup();
else
this.hidePopup();
</setter>
</property>
<field name="_inputField">null</field>
<field name="_dropMarker">null</field>
<field name="_popup">null</field>
<field name="_container">null</field>
<field name="_width">-1</field>
<!-- This is either an nsIXFormsItemElement or null. -->
<field name="_selected">null</field>
<field name="_tmpSelected">null</field>
<field name="_lastSelectedItem">null</field>
<field name="popupOpen">false</field>
<field name="shouldHandleBlur">true</field>
<field name="_outOfRange">false</field>
<property name="selectionOpen" readonly="true">
<getter>
return this.getAttribute("selection") == "open";
</getter>
</property>
<property name="inputField" readonly="true">
<getter>
if (!this._inputField) {
this._inputField =
document.getAnonymousElementByAttribute(this, "anonid", "control");
}
return this._inputField;
</getter>
</property>
<property name="dropMarker" readonly="true">
<getter>
if (!this._dropMarker) {
this._dropMarker =
document.getAnonymousElementByAttribute(this, "anonid", "dropmarker");
}
return this._dropMarker;
</getter>
</property>
<property name="popup" readonly="true">
<getter>
if (!this._popup) {
this._popup =
document.getAnonymousElementByAttribute(this, "anonid", "popup");
}
return this._popup;
</getter>
</property>
<property name="container" readonly="true">
<getter>
if (!this._container) {
this._container =
document.getAnonymousElementByAttribute(this, "anonid", "container");
}
return this._container;
</getter>
</property>
<property name="incremental" readonly="true">
<getter>
<![CDATA[
// default is true
var incremental = true;
if (this.getAttribute("incremental") == "false") {
incremental = false;
}
return incremental;
]]>
</getter>
</property>
<method name="updateInputField">
<body>
<![CDATA[
if (this._selected) {
// Remove extra white space characters from the beginning of the label.
this.inputField.value = this._selected.labelText.replace(/^[\s\n]+/, "");
}
]]>
</body>
</method>
<method name="handleControlClick">
<parameter name="aEvent"/>
<body>
<![CDATA[
if (this.selectionOpen) {
if (!this._selected && this.incremental) {
this._handleSelection(false);
return;
}
if (this.popupOpen) {
this.hidePopup();
}
return;
}
this.togglePopup();
]]>
</body>
</method>
<method name="handleKeyPress">
<parameter name="aEvent"/>
<body>
<![CDATA[
var key = aEvent.keyCode;
if (key == aEvent.DOM_VK_RETURN || key == aEvent.DOM_VK_ENTER ||
key == aEvent.DOM_VK_F4 || aEvent.altKey &&
(key == aEvent.DOM_VK_UP || key == aEvent.DOM_VK_DOWN)) {
var open = this.popupOpen;
this.togglePopup();
if (open && this._selected) {
this.updateInputField();
if (this.incremental) {
this._handleSelection(true);
} else {
this.dispatchSelectEvents();
}
}
if (aEvent.altKey && (key == aEvent.DOM_VK_UP ||
key == aEvent.DOM_VK_DOWN)) {
aEvent.preventDefault();
}
} else if (key == aEvent.DOM_VK_UP || key == aEvent.DOM_VK_DOWN) {
if (this.selectionOpen && !this.popupOpen) {
this.togglePopup();
}
this.internalScroll(aEvent.keyCode == aEvent.DOM_VK_DOWN);
if (this._selected && this.popupOpen) {
var el = this._selected.QueryInterface(Components.interfaces.nsIDOMElement);
if ("scrollIntoView" in el) {
el.scrollIntoView(false);
}
}
if (!this.popupOpen && this.incremental) {
if (this._selected) {
this.updateInputField();
}
this._handleSelection(true);
}
aEvent.preventDefault();
} else if (key == aEvent.DOM_VK_TAB) {
// Hiding popup when user uses keyboard to focus out
// from <select1>. No need to update the value of the control
// in this case.
if (this._selected && this.popupOpen) {
this.hidePopup();
this._selected.setActive(false);
this._selected = this._tmpSelected;
this._tmpSelected = null;
if (this._selected) {
this._selected.setActive(true);
}
}
} else if (aEvent.charCode && !this.selectionOpen) {
// Cache sequence of last pressed keys and search it in value of
// xforms:label inside xforms:item elements.
var currtime = new Date().valueOf();
var char = String.fromCharCode(aEvent.charCode);
if (currtime - this.searchKeypressTime <= 1000)
this.searchValue += char;
else
this.searchValue = char;
this.selectItemByLabel(this.searchValue);
this.searchKeypressTime = currtime;
}
return true;
]]>
</body>
</method>
<field name="searchValue">""</field>
<field name="searchKeypressTime">0</field>
<method name="handleInput">
<body>
<![CDATA[
if (this._selected) {
this._selected.setActive(false);
this._selected = null;
}
if (this.incremental) {
this._handleSelection(false);
}
return true;
]]>
</body>
</method>
<!-- This is used by internalScroll to find the next
selectable <item>. Returns a DOMElement or null. -->
<method name="findNextSelectable">
<parameter name="aNode"/>
<parameter name="aDown"/>
<body>
<![CDATA[
if (!aNode) {
return null;
}
var node = aNode;
var next = null;
while (node) {
if (aDown) {
next = node.nextSibling;
} else {
next = node.previousSibling;
}
if (next && next.namespaceURI == this.XFORMS_NS) {
if (next.localName == "item") {
return next;
} else if (next.localName == "choices") {
var child = null;
if (aDown) {
child = next.firstChild;
} else {
child = next.lastChild;
}
if (child.namespaceURI == this.XFORMS_NS &&
child.localName == "item") {
return child;
}
child = this.findNextSelectable(child, aDown);
if (child) {
return child;
}
} else if (next.localName == "itemset" &&
next.anonymousItemSetContent.childNodes.length > 0) {
return aDown ? next.anonymousItemSetContent.firstChild
: next.anonymousItemSetContent.lastChild;
}
}
node = next;
}
// if we are in a choices or itemset element
var parent = aNode.parentNode;
if (parent.namespaceURI == this.XFORMS_NS &&
parent.localName == "choices") {
var sibling = aDown ? parent.nextSibling : parent.previousSibling;
if (sibling.namespaceURI == this.XFORMS_NS &&
sibling.localName == "item") {
return sibling;
}
return this.findNextSelectable(sibling, aDown);
}
if (parent.parentNode.namespaceURI == this.XFORMS_NS &&
parent.parentNode.localName == "itemset") {
var sibling2 = aDown
? parent.parentNode.nextSibling
: parent.parentNode.previousSibling;
if (sibling2.namespaceURI == this.XFORMS_NS &&
sibling2.localName == "item") {
return sibling2;
}
return this.findNextSelectable(sibling2, aDown);
}
return null;
]]>
</body>
</method>
<method name="internalScroll">
<parameter name="aDown"/>
<body>
<![CDATA[
var label = null;
var next = null, nextItem = null;
var node = this.firstChild;
while (node) {
if (node.namespaceURI == this.XFORMS_NS &&
node.localName == "label") {
label = node;
break;
}
node = node.nextSibling;
}
if (this._selected) {
node = this._selected.QueryInterface(Components.interfaces.nsIDOMNode);
} else if (label) {
node = label.nextSibling;
} else {
node = this.firstChild;
}
next = this.findNextSelectable(node, aDown);
if (next) {
if (this._selected) {
this._lastSelectedItem = this._selected;
this._selected.setActive(false);
this._selected = null;
}
nextItem = next.QueryInterface(Components.interfaces.nsIXFormsItemElement);
if (nextItem) {
this._selected = nextItem;
this._selected.setActive(true);
if (!this.selectionOpen)
this.updateInputField();
}
return true;
}
]]>
</body>
</method>
<method name="mouseUp">
<parameter name="aEvent"/>
<body>
<![CDATA[
// Use original target, because <item> is possible (anonymously)
// inside <itemset>
var target = aEvent.originalTarget;
while (target && target != this) {
if (target.namespaceURI == this.XFORMS_NS &&
(target.localName == "item" || target.localName == "choices")) {
break;
}
target = target.parentNode;
}
if (target == this) {
return true;
}
this.hidePopup();
if (this._selected) {
this.updateInputField();
if (this.incremental) {
this._handleSelection(true);
} else {
this.dispatchSelectEvents();
}
}
this.inputField.focus();
]]>
</body>
</method>
<method name="mouseOver">
<parameter name="aEvent"/>
<body>
<![CDATA[
// Use original target, because <item> is possible (anonymously)
// inside <itemset>
var target = aEvent.originalTarget;
while (target && target != this) {
if (target.namespaceURI == this.XFORMS_NS && target.localName == "item") {
if (this._selected) {
this._selected.setActive(false);
this._selected = null;
}
var item =
target.QueryInterface(Components.interfaces.nsIXFormsItemElement);
if (item) {
item.setActive(true);
this._selected = item;
}
break;
}
target = target.parentNode;
}
]]>
</body>
</method>
<method name="refreshWidth">
<body>
<![CDATA[
if (!this.popupOpen) {
this.inputField.removeAttribute("style");
this.popup.style.width = "auto";
this.popup.style.height = "auto";
this.popup.style.maxHeight = "none";
this._width = -1;
var popupBox = document.getBoxObjectFor(this.popup);
var w = popupBox.width;
if (w > 0) {
w = w + 12; // Adding some 'padding' for possible scrollbar
this.inputField.setAttribute("style", "width:" + w + "px;");
this._width = w + document.getBoxObjectFor(this.dropMarker).width;
}
this.popup.style.maxHeight = "10px";
this.popup.style.left = "0px";
this.popup.style.top = "0px";
}
]]>
</body>
</method>
<method name="hidePopup">
<body>
this.popup.style.visibility = "hidden";
this.popupOpen = false;
</body>
</method>
<method name="showPopup">
<body>
<![CDATA[
if (this.popupOpen || this.accessors.isReadonly())
return;
// Calculating the size and position of the popup.
var style = "";
var containerBox = document.getBoxObjectFor(this.container);
var x;
var y;
var adjust = 0;
var absolute = false;
var absoluteOffsetY = 0;
var p = this.container.offsetParent;
var compStyle =
document.defaultView.getComputedStyle(p, null);
if (compStyle.getPropertyValue("position") != "absolute" &&
compStyle.getPropertyValue("position") != "relative") {
adjust = document.documentElement.offsetTop;
x = containerBox.x;
y = containerBox.y;
} else {
absolute = true;
x = this.container.offsetLeft;
y = this.container.offsetTop;
absoluteOffsetY = y;
while (p) {
absoluteOffsetY += p.offsetTop;
p = p.offsetParent;
}
}
var h = containerBox.height;
var w = containerBox.width;
var targetY = y + h;
this.popup.style.maxHeight = "none";
var popupBox = document.getBoxObjectFor(this.popup);
var popupHeight = popupBox.height;
var pY = window.pageYOffset;
var iH = window.innerHeight;
var belowSelect = absolute
? (iH - (absoluteOffsetY + h) - h)
: (iH - (y - pY + h - adjust) - h);
if (belowSelect < popupHeight) {
if ((y - pY) > popupHeight) {
targetY = y - popupHeight;
} else if (belowSelect < (y - pY)) {
style = style + "max-height:" + (y - pY - adjust) + "px;";
targetY = pY + adjust;
} else {
style = style + "max-height:" + belowSelect + "px;";
}
}
style = style + "left:" + x + "px;";
style = style + "top:" + targetY + "px;";
style = style + "width:";
if (this.selectionOpen) {
style = style + w + "px;";
} else if (this._width < 0) {
style = style + "auto;"
} else {
style = style + this._width + "px;";
}
style = style + "visibility:visible;";
this.popup.setAttribute("style", style);
this.popupOpen = true;
this._tmpSelected = this._selected;
if (this._selected) {
var el = this._selected.QueryInterface(Components.interfaces.nsIDOMElement);
if ("scrollIntoView" in el) {
el.scrollIntoView(false);
}
}
]]>
</body>
</method>
<method name="togglePopup">
<body>
if (!this.popupOpen)
this.showPopup();
else
this.hidePopup();
</body>
</method>
<method name="refresh">
<body>
<![CDATA[
try {
if (this._selected) {
// Verify that the selected item that we know and love is still
// valid. If it were part of an itemset, this refresh might be
// happening due to an itemset refresh which means that this item
// may have already been removed from the DOM. This is just a
// hack to work around the fact that the original patch for bug
// 360188 won't work on the branches due to an issue where
// DOMNodeRemoved isn't ever being dispatched. This will all be
// fixed when we rework the select/select1, again, to better
// handle items being dynamically inserted and removed
// (bug 372197).
if (!this._selected.parentNode) {
this._selected = null;
this._lastSelectedItem = null;
}
}
var nodeValue = null, newValue = null;
var boundNode = this.accessors.getBoundNode();
var outOfRange = false;
if (boundNode && boundNode.hasChildNodes()) {
// Since this is a select1, there should normally be just one
// child node here. But no guarantee that a select1 generated
// the value coming in. So we'll look for text node with
// non-whitespace characters to compare with an item's xf:value.
// Any other node that we encounter we look to match with an
// item's xf:copy. If more than one of either of these exists
// in the instance data, we need to generate a xforms-out-of-range
// event and style the select1 as out-of-range since by
// definition a select1 can not select more than one item.
var child = boundNode.firstChild;
while (child) {
var type = child.nodeType;
if (type == Node.TEXT_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 (newValue || nodeValue) {
// oh oh! We've already found a selectable node in the
// instance data and now we have another. That shouldn't
// happen.
outOfRange = true;
}
newValue = string;
}
} else {
// 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.
if (newValue || nodeValue) {
// oh oh! We've already found a selectable node in the
// instance data and now we have another. That shouldn't
// happen.
outOfRange = true;
}
nodeValue = child;
}
if (child == boundNode.lastChild) {
break;
}
child = child.nextSibling;
}
}
if (outOfRange) {
// 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 ((outOfRange != this._outOfRange) && boundNode) {
this._outOfRange = outOfRange;
this.accessors.setInRange(false);
}
// can't possibly work, no sense continuing.
this.inputField.value = "";
this._selected.setActive(false);
this._selected = null;
this.refreshWidth();
return false;
}
if (!this.selectionOpen || this.accessors.isReadonly()) {
this.inputField.setAttribute("readonly", "readonly");
} else {
this.inputField.removeAttribute("readonly");
}
if (this._selected && !this._selected.isCopyItem) {
var envelope = this._getSelectedValue();
if (envelope) {
var textNode = null;
if (envelope.nodeType == Node.ELEMENT_NODE) {
textNode = envelope.firstChild;
if (newValue == textNode.nodeValue) {
// Value in instance data already selected. Need to only
// refresh the width.
this.refreshWidth();
return true;
}
} else {
if (newValue == envelope.nodeValue) {
// Value in instance data already selected. Need to only
// refresh the width.
this.refreshWidth();
return true;
}
}
}
this._selected.setActive(false);
this._selected = null;
this._lastSelectedItem = null;
}
if (newValue) {
this.selectItemByValue(newValue);
} else if (nodeValue) {
this.selectItemByNode(nodeValue);
}
outOfRange = false;
if (this._selected) {
this.updateInputField();
this._lastSelectedItem = this._selected;
} else if (this.selectionOpen) {
this.inputField.value = newValue;
} else {
this.inputField.value = "";
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 ((outOfRange != this._outOfRange) && boundNode) {
this._outOfRange = outOfRange;
this.accessors.setInRange(!outOfRange);
}
this.refreshWidth();
} catch (ex) {}
return true;
]]>
</body>
</method>
<method name="focus">
<body>
this.inputField.focus();
return true;
</body>
</method>
<method name="getCurrentValue">
<body>
throw Components.results.NS_ERROR_NOT_IMPLEMENTED;
</body>
</method>
<method name="selectItemByLabel">
<parameter name="aValue"/>
<parameter name="aContextNode"/>
<body>
<![CDATA[
var item =
this.searchItemByLabel(new RegExp("^" + aValue, "i"), this);
if (this._selected) {
this._selected.setActive(false);
this._selected = null;
}
this._selected = item;
if (this._selected) {
this._selected.setActive(true);
this.updateInputField();
}
]]>
</body>
</method>
<method name="searchItemByLabel">
<parameter name="aExp"/>
<parameter name="aContextNode"/>
<body>
<![CDATA[
for (var child = aContextNode.firstChild; child;
child = child.nextSibling) {
if (child.namespaceURI != this.XFORMS_NS)
continue;
switch (child.localName) {
case "item":
child = child.
QueryInterface(Components.interfaces.nsIXFormsItemElement);
if (child.labelText.search(aExp) != -1) {
return child;
}
break;
case "choices":
var item = this.searchItemByLabel(aExp, child);
if (item)
return item;
break;
case "itemset":
var item =
this.searchItemByLabel(aExp, child.anonymousItemSetContent);
if (item)
return item;
break;
}
}
]]>
</body>
</method>
<method name="selectItemByValue">
<parameter name="aValue"/>
<body>
<![CDATA[
var node = this.firstChild;
var item;
while (node) {
item = null;
try {
if (node.nodeType == document.ELEMENT_NODE &&
node.namespaceURI == this.XFORMS_NS &&
node.localName != "label") {
item = node.QueryInterface(Components.interfaces.nsIXFormsSelectChild);
if (item) {
item = item.selectItemByValue(aValue);
if (item) {
if (this._selected) {
this._selected.setActive(false);
this._selected = null;
}
this._selected = item.QueryInterface(Components.interfaces.nsIXFormsItemElement);
if (this._selected) {
this._selected.setActive(true);
return;
}
}
}
}
} catch (ex) {}
node = node.nextSibling;
}
]]>
</body>
</method>
<method name="selectItemByNode">
<parameter name="aNode"/>
<body>
<![CDATA[
// select the copyItem in this select1 whose copyNode equals aNode
var node = this.firstChild;
var item;
while (node) {
item = null;
try {
if (node.nodeType == document.ELEMENT_NODE &&
node.namespaceURI == this.XFORMS_NS &&
node.localName != "label") {
item = node.QueryInterface(Components.interfaces.nsIXFormsSelectChild);
if (item) {
item = item.selectItemByNode(aNode);
if (item) {
if (this._selected) {
this._selected.setActive(false);
this._selected = null;
}
this._selected = item.QueryInterface(Components.interfaces.nsIXFormsItemElement);
if (this._selected) {
this._selected.setActive(true);
return;
}
}
}
}
} catch (ex) {}
node = node.nextSibling;
}
]]>
</body>
</method>
<method name="handleBlur">
<body>
<![CDATA[
if (this.shouldHandleBlur) {
var open = this.popupOpen;
this.hidePopup();
if (open) {
if (this._selected) {
this._selected.setActive(false);
this._selected = this._tmpSelected;
this._tmpSelected = null;
if (this._selected) {
this._selected.setActive(true);
}
}
return;
}
if (this._selected) {
this.updateInputField();
}
this._handleSelection(false, true);
}
]]>
</body>
</method>
<!-- _handleSelection updates the bound node with the value from the
currently selected item's value element or copy element. -->
<method name="_handleSelection">
<parameter name="aDispatchSelectEvents"/>
<parameter name="aInBlur"/>
<body>
<![CDATA[
// if aDispatchSelectEvents is true, then we need to make sure to
// dispatch the xforms-deselect and xforms-select events before we
// change the value of the bound node otherwise we'll get the
// event ordering wrong. Similar with setting out of/in range (must
// happen after dispatch select/deselect). aDispatchSelectEvents is
// a REQUIRED parameter, whether it be true or false.
// aInBlur is not a required parameter. It is true if handleSelection
// was called from the blur handler.
var boundNode = this.accessors.getBoundNode();
if (!boundNode) {
return;
}
if (this.selectionOpen && !this._selected) {
if (aDispatchSelectEvents == true) {
this.dispatchSelectEvents();
}
this.accessors.setValue(this.inputField.value);
// open selection can't be out of range
this._outOfRange = false;
this.accessors.setInRange(true);
return;
}
if (!this._selected) {
// no reason to continue
return;
}
if (aInBlur && aInBlur == true) {
// if _handleSelection is called due to a blur, we only really care
// about making sure the bound node is in sync if @incremental is
// false. Otherwise the bound node is already up to date so might
// as well return.
if (this.incremental) {
return;
}
}
if (boundNode.nodeType != boundNode.ELEMENT_NODE) {
// if the boundNode type isn't an ELEMENT_NODE, then contentEnvelope
// isn't an ELEMENT_NODE (since it is a clone of the bound node).
// So if contentEnvelope has a value, it will be the nodeValue.
var envelope = this._getSelectedValue();
if (envelope) {
if (aDispatchSelectEvents == true) {
this.dispatchSelectEvents();
}
this.accessors.setValue(envelope.nodeValue);
this._outOfRange = false;
this.accessors.setInRange(true);
return;
}
// not allowed to copy a node under a non ELEMENT node, so
// generating a binding exception per spec.
var ev = document.createEvent("Events");
ev.initEvent("xforms-binding-exception", true, false);
this.dispatchEvent(ev);
// well, whatever we had selected isn't going to cut it. But the
// user did choose to deselect the previous item in favor of this
// this item, so we really shouldn't go back to what
// was there before. So we'll go to nothing. Make sure bound
// node reflects this. Seems to be consistent with what Novell
// and formsPlayer does, too.
this._selected.setActive(false);
this._selected = null;
this.inputField.value = "";
if (aDispatchSelectEvents == true) {
this.dispatchSelectEvents();
}
this.accessors.setValue("");
this._outOfRange = true;
this.accessors.setInRange(false);
return;
}
var contentEnvelope = this._getSelectedValue();
var copyInvolved = this._selected.isCopyItem;
if (!copyInvolved && this._lastSelectedItem) {
copyInvolved = this._lastSelectedItem.isCopyItem;
}
if (!copyInvolved) {
// Since we aren't selecting a copyItem nor causing a copyItem to
// be deselected, no sense using setContent. Too expensive.
if (aDispatchSelectEvents == true) {
this.dispatchSelectEvents();
}
this.accessors.setValue(contentEnvelope.textContent);
this._outOfRange = false;
this.accessors.setInRange(true);
} else {
if (aDispatchSelectEvents == true) {
this.dispatchSelectEvents();
}
this.accessors.setContent(contentEnvelope, true);
this._outOfRange = false;
this.accessors.setInRange(true);
}
]]>
</body>
</method>
<method name="dispatchSelectEvents">
<body>
<![CDATA[
if (this._lastSelectedItem != this._selected) {
if (this._lastSelectedItem) {
this.dispatchSelectEvent(this._lastSelectedItem, "xforms-deselect");
}
if (this._selected) {
this.dispatchSelectEvent(this._selected, "xforms-select");
}
this._lastSelectedItem = this._selected;
}
]]>
</body>
</method>
<method name="dispatchSelectEvent">
<parameter name="aElement"/>
<parameter name="aName"/>
<body>
<![CDATA[
var ev = document.createEvent("Events");
ev.initEvent(aName, true, false);
var elm = aElement;
// per errata for XForms 1.0 second edition, we send the event to the
// item, even if it is contained in an itemset
elm.dispatchEvent(ev);
return true;
]]>
</body>
</method>
<method name="_getSelectedValue">
<body>
<![CDATA[
// The purpose of this function is to return the select1's currently
// selected item's value in a contentEnvelope. It achieves this by
// cloning the bound node to create the contentEnvelope which will be
// returned. If the contentEnvelope is an element node, the contents
// of the selected item's value will be inserted as a child of the
// contentEnvelope. If it is a textnode, the selected item value will
// be put in the contentEnvelope.nodeValue.
if (!this._selected) {
// this will probably only happen if there was an exception
// somewhere else first. But no sense continuing below and adding
// more exceptions to the console.
return null;
}
var boundNode = this.accessors.getBoundNode();
if (!boundNode) {
return null;
}
var contentEnvelope = boundNode.cloneNode(false);
if (!contentEnvelope) {
return null;
}
var contentDocument = contentEnvelope.ownerDocument;
if (contentEnvelope.nodeType == Node.ELEMENT_NODE) {
var contentNode = null;
if (this._selected.isCopyItem) {
var copyNode = this._selected.copyNode;
if (copyNode) {
contentNode = contentDocument.importNode(copyNode, true);
}
} else {
contentNode =
contentDocument.createTextNode(this._selected.value);
}
contentEnvelope.appendChild(contentNode);
} else {
// if the selected item is not a copyItem, then we'll just put the
// item's value in the nodeValue for the contentEnvelope. Otherwise
// we are stuck trying to stick an element node under a non-element
// node and that ainna gonna work.
if (!this._selected.isCopyItem) {
contentEnvelope.nodeValue = this._selected.value;
} else {
contentEnvelope = null;
}
}
return contentEnvelope;
]]>
</body>
</method>
</implementation>
<handlers>
<handler event="keypress" keycode="VK_ESCAPE">
if (this.popupOpen) {
this.hidePopup();
if (this._selected)
this._selected.setActive(false);
this._selected = this._tmpSelected;
this._tmpSelected = null;
if (this._selected)
this._selected.setActive(true);
}
}
</handler>
</handlers>
</binding>
<!-- The binding for <item> is needed only because of the
scrollIntoView method. -->
<binding id="xformswidget-select1-item">
<content>
<html:div anonid="content">
<children/>
</html:div>
</content>
<implementation>
<field name="_content">null</field>
<property name="content" readonly="true">
<getter>
if (!this._content) {
this._content =
document.getAnonymousElementByAttribute(this, "anonid", "content");
}
return this._content;
</getter>
</property>
<!-- Only (X)HTML elements have method 'scrollIntoView',
so we need to forward this method call to the anonymous content
of this element. -->
<method name="scrollIntoView">
<parameter name="aTop"/>
<body>
// this.content is null if anonymous content hasn't been created yet.
var content = this.content;
if (content) {
content.scrollIntoView(aTop);
}
</body>
</method>
</implementation>
</binding>
</bindings>