XForms Bug 279063 - implement copy element. Patch by aaronr, r=me,smaug,allan

This commit is contained in:
doronr%us.ibm.com 2005-11-17 22:00:27 +00:00
Родитель 9f4fc80b93
Коммит 9d771bbe20
20 изменённых файлов: 1682 добавлений и 82 удалений

Просмотреть файл

@ -133,6 +133,7 @@ XPIDLSRCS = \
nsIXFormsRangeAccessors.idl \
nsIXFormsUploadElement.idl \
nsIXFormsUploadUIElement.idl \
nsIXFormsCopyElement.idl \
$(NULL)
# XForms source files
@ -191,6 +192,7 @@ CPPSRCS = \
nsXFormsRangeElement.cpp \
nsXFormsAccessors.cpp \
nsXFormsRangeAccessors.cpp \
nsXFormsCopyElement.cpp \
$(NULL)
# Standard Mozilla make rules

Просмотреть файл

@ -47,7 +47,7 @@ interface nsIDOMNode;
* Private interface implemented by the model element for other
* elements to use.
*/
[uuid(8c5e2b7d-043e-4278-bc3e-1519bd9c62ec)]
[uuid(ccba3717-ef05-41cd-b831-f7f5ab3b921c)]
interface nsIModelElementPrivate : nsIXFormsModelElement
{
/**
@ -99,6 +99,13 @@ interface nsIModelElementPrivate : nsIXFormsModelElement
void getNodeValue(in nsIDOMNode contextNode,
out AString nodeValue);
/**
* Insert a node under an instance node
*/
void setNodeContent(in nsIDOMNode contextNode,
in nsIDOMNode nodeContent,
out boolean nodeChanged);
/**
* Validates the instance node against the schemas loaded by the model.
*/

Просмотреть файл

@ -37,6 +37,7 @@
* ***** END LICENSE BLOCK ***** */
#include "nsISupports.idl"
#include "nsIDOMNode.idl"
/**
* Interface exposing the states of an XForms control.
@ -44,7 +45,7 @@
* For more information on this interface please see
* http://developer.mozilla.org/en/docs/XForms:Custom_Controls
*/
[scriptable, uuid(8e8c5022-a61d-42cf-9551-74215dc611ad)]
[scriptable, uuid(74992960-42a9-4479-a1ff-f7f1b37e187a)]
interface nsIXFormsAccessors : nsISupports
{
/**
@ -81,4 +82,22 @@ interface nsIXFormsAccessors : nsISupports
* true, if XForms control is bound to a node in a data model.
*/
boolean hasBoundNode();
/**
* Node that the control is bound to in its data model.
*/
nsIDOMNode getBoundNode();
/**
* Used to set the complete contents of the bound node. This function is
* meant to be used like setValue() except that it can be used to set more
* than just the first textnode contained under the bound node. If there
* is nothing contained under aNode, then all children of the bound node
* will be eliminated.
*
* @param aNode aNode should be a copy of the bound node. setContent
* will take the contents of aNode and move them under
* bound node.
*/
void setContent(in nsIDOMNode aNode);
};

Просмотреть файл

@ -0,0 +1,54 @@
/* -*- Mode: IDL; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* ***** 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):
* Aaron Reed <aaronr@us.ibm.com>
*
* Alternatively, the contents of this file may be used under the terms of
* either of 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 ***** */
/**
* This interface is implemented by XForms \<copy\> elements.
*/
#include "nsISupports.idl"
#include "nsIDOMNode.idl"
[uuid(4af9ad8e-5266-44ac-b9f7-ef2cb9fe2637)]
interface nsIXFormsCopyElement : nsISupports
{
/**
* Returns the node that will be deep copied into the instance data if the
* item element which contains this copy element is selected.
*/
readonly attribute nsIDOMNode copyNode;
};

Просмотреть файл

@ -37,11 +37,13 @@
* ***** END LICENSE BLOCK ***** */
#include "nsISupports.idl"
#include "nsIDOMNode.idl"
#include "nsIXFormsDelegate.idl"
/**
* Interface implemented by the item element.
*/
[scriptable, uuid(796a2e26-a40b-4ebf-be2c-42faf5fea6c4)]
[scriptable, uuid(ec8d3556-8ed2-4143-88d1-6b7b2c8b0b3b)]
interface nsIXFormsItemElement : nsISupports
{
/**
@ -67,4 +69,18 @@ interface nsIXFormsItemElement : nsISupports
* \<select1\> element, which can then refresh its UI.
*/
void labelRefreshed();
/**
* Indicates whether the item element contains a value child or a copy
* child. We'll assume that if the item is NOT a copy item, then it must
* be a value item. Which means that it must contain a XForms value element
* child.
*/
attribute boolean isCopyItem;
/*
* returns the node that the contained copy element is bound to
*/
readonly attribute nsIDOMNode copyNode;
};

Просмотреть файл

@ -52,7 +52,7 @@ class nsStringArray;
* of an XForms select element (choices, item, itemset).
*/
[scriptable, uuid(a29ac2bd-f36a-451e-99e1-0f3bd94ffbef)]
[scriptable, uuid(9fac2f59-4ec8-456f-ad06-4e28cd7d5b2c)]
interface nsIXFormsSelectChild : nsISupports
{
/*
@ -61,4 +61,11 @@ interface nsIXFormsSelectChild : nsISupports
* this method returns that \<item\>.
*/
nsIDOMNode selectItemByValue(in AString value);
/*
* selectItemByNode is used in \<select1\> and \<select\>. If the
* XFormsSelectChild is or contains an \<item\>, which has a node 'equal' the
* parameter |aNode|, this method returns the first \<item\> that matches.
*/
nsIDOMNode selectItemByNode(in nsIDOMNode aNode);
};

Просмотреть файл

@ -44,6 +44,7 @@
#include "nsDOMString.h"
#include "nsIEventStateManager.h"
#include "nsIContent.h"
#include "nsIXFormsControl.h"
NS_IMPL_ISUPPORTS2(nsXFormsAccessors, nsIXFormsAccessors, nsIClassInfo)
@ -113,6 +114,48 @@ nsXFormsAccessors::IsValid(PRBool *aStateVal)
return GetState(NS_EVENT_STATE_VALID, aStateVal);
}
NS_IMETHODIMP
nsXFormsAccessors::SetContent(nsIDOMNode *aNode)
{
NS_ENSURE_STATE(mElement);
nsCOMPtr<nsIDOMNode> boundNode;
nsresult rv = GetBoundNode(getter_AddRefs(boundNode));
NS_ENSURE_STATE(boundNode);
nsCOMPtr<nsIModelElementPrivate> modelPriv = nsXFormsUtils::GetModel(mElement);
NS_ENSURE_STATE(modelPriv);
PRBool changed;
rv = modelPriv->SetNodeContent(boundNode, aNode, &changed);
NS_ENSURE_SUCCESS(rv, rv);
if (changed) {
nsCOMPtr<nsIDOMNode> model = do_QueryInterface(modelPriv);
if (model) {
rv = nsXFormsUtils::DispatchEvent(model, eEvent_Recalculate);
NS_ENSURE_SUCCESS(rv, rv);
rv = nsXFormsUtils::DispatchEvent(model, eEvent_Revalidate);
NS_ENSURE_SUCCESS(rv, rv);
rv = nsXFormsUtils::DispatchEvent(model, eEvent_Refresh);
NS_ENSURE_SUCCESS(rv, rv);
}
}
return NS_OK;
}
NS_IMETHODIMP
nsXFormsAccessors::GetBoundNode(nsIDOMNode **aBoundNode)
{
NS_ENSURE_ARG_POINTER(aBoundNode);
if (mDelegate) {
nsCOMPtr<nsIXFormsControl> control = do_QueryInterface(mDelegate);
return control->GetBoundNode(aBoundNode);
}
return NS_OK;
}
// nsIClassInfo implementation
static const nsIID sScriptingIIDs[] = {

Просмотреть файл

@ -219,6 +219,36 @@ nsXFormsChoicesElement::SelectItemByValue(const nsAString &aValue, nsIDOMNode **
return NS_OK;
}
NS_IMETHODIMP
nsXFormsChoicesElement::SelectItemByNode(nsIDOMNode *aNode,
nsIDOMNode **aSelected)
{
NS_ENSURE_ARG_POINTER(aSelected);
NS_ENSURE_STATE(mElement);
*aSelected = nsnull;
nsCOMPtr<nsIDOMNodeList> children;
nsresult rv = mElement->GetChildNodes(getter_AddRefs(children));
NS_ENSURE_SUCCESS(rv, rv);
PRUint32 childCount = 0;
children->GetLength(&childCount);
nsCOMPtr<nsIDOMNode> childNode;
nsCOMPtr<nsIXFormsSelectChild> childItem;
for (PRUint32 i = 0; i < childCount; ++i) {
children->Item(i, getter_AddRefs(childNode));
childItem = do_QueryInterface(childNode);
if (childItem) {
childItem->SelectItemByNode(aNode, aSelected);
if (*aSelected)
return NS_OK;
}
}
return NS_OK;
}
// internal methods
void

Просмотреть файл

@ -0,0 +1,191 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* ***** 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):
* Aaron Reed <aaronr@us.ibm.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 ***** */
#include "nsIXFormsCopyElement.h"
#include "nsXFormsStubElement.h"
#include "nsIXTFGenericElementWrapper.h"
#include "nsXFormsUtils.h"
#include "nsIDOMElement.h"
#include "nsString.h"
#include "nsIXFormsItemElement.h"
/**
* Implementation of the XForms \<copy\> element.
*
* @note The copy element does not display any content, it simply provides
* a node to be copied to instance data when the containing XForms item
* element is selected.
*/
class nsXFormsCopyElement : public nsXFormsStubElement,
public nsIXFormsCopyElement
{
public:
nsXFormsCopyElement() : mElement(nsnull) {}
NS_DECL_ISUPPORTS_INHERITED
// nsIXTFGenericElement overrides
NS_IMETHOD OnCreated(nsIXTFGenericElementWrapper *aWrapper);
// nsIXTFElement overrides
NS_IMETHOD ParentChanged(nsIDOMElement *aNewParent);
NS_IMETHOD DocumentChanged(nsIDOMDocument *aNewParent);
// nsIXFormsCopyElement
NS_DECL_NSIXFORMSCOPYELEMENT
private:
nsIDOMElement *mElement;
};
NS_IMPL_ISUPPORTS_INHERITED1(nsXFormsCopyElement,
nsXFormsStubElement,
nsIXFormsCopyElement)
NS_IMETHODIMP
nsXFormsCopyElement::OnCreated(nsIXTFGenericElementWrapper *aWrapper)
{
aWrapper->SetNotificationMask(nsIXTFElement::NOTIFY_PARENT_CHANGED |
nsIXTFElement::NOTIFY_DOCUMENT_CHANGED);
nsCOMPtr<nsIDOMElement> node;
aWrapper->GetElementNode(getter_AddRefs(node));
// It's ok to keep pointer to mElement. mElement will have an
// owning reference to this object, so as long as we null out mElement in
// OnDestroyed, it will always be valid.
mElement = node;
NS_ASSERTION(mElement, "Wrapper is not an nsIDOMElement, we'll crash soon");
return NS_OK;
}
// nsIXTFElement
NS_IMETHODIMP
nsXFormsCopyElement::ParentChanged(nsIDOMElement *aNewParent)
{
if (aNewParent) {
if (!nsXFormsUtils::IsXFormsElement(aNewParent,
NS_LITERAL_STRING("itemset")) &&
!nsXFormsUtils::IsXFormsElement(aNewParent,
NS_LITERAL_STRING("contextcontainer"))) {
// parent of a copy element must always be an itemset. We really can't
// enforce this all that well until we have full schema support but for
// now we'll at least warn the author. We are also checking for
// contextcontainer because under Mozilla, the children of an itemset
// element are cloned underneath a contextcontainer which is in turn
// contained in a nsXFormsItemElement. Each such item element is then
// appended as anonymous content of the itemset.
nsXFormsUtils::ReportError(NS_LITERAL_STRING("copyError"), mElement);
}
}
return NS_OK;
}
NS_IMETHODIMP
nsXFormsCopyElement::DocumentChanged(nsIDOMDocument* aNewDocument)
{
if (!aNewDocument)
return NS_OK;
// tell grandparent (xf:item) that it contains a xf:copy element and
// not a xf:value element.
nsCOMPtr<nsIDOMNode> contextContainer;
nsresult rv = mElement->GetParentNode(getter_AddRefs(contextContainer));
NS_ENSURE_TRUE(contextContainer, rv);
nsCOMPtr<nsIDOMNode> itemNode;
rv = contextContainer->GetParentNode(getter_AddRefs(itemNode));
NS_ENSURE_TRUE(itemNode, rv);
nsCOMPtr<nsIXFormsItemElement> item = do_QueryInterface(itemNode);
// It is possible that the grandparent ISN'T an xf:item, if this is the
// original template copy element whose parent is the xf:itemset and
// grandparent is the xf:select. We'll ignore a copy element in that case
// since it really isn't in play.
if (item) {
item->SetIsCopyItem(PR_TRUE);
}
return NS_OK;
}
// nsIXFormsCopyElement
NS_IMETHODIMP
nsXFormsCopyElement::GetCopyNode(nsIDOMNode **aNode)
{
NS_ENSURE_ARG_POINTER(aNode);
*aNode = nsnull;
nsCOMPtr<nsIModelElementPrivate> model;
nsCOMPtr<nsIDOMXPathResult> result;
nsresult rv =
nsXFormsUtils::EvaluateNodeBinding(mElement,
nsXFormsUtils::ELEMENT_WITH_MODEL_ATTR,
NS_LITERAL_STRING("ref"), EmptyString(),
nsIDOMXPathResult::FIRST_ORDERED_NODE_TYPE,
getter_AddRefs(model),
getter_AddRefs(result));
NS_ENSURE_SUCCESS(rv, rv);
if (result) {
nsCOMPtr<nsIDOMNode> singleNode;
result->GetSingleNodeValue(getter_AddRefs(singleNode));
NS_IF_ADDREF(*aNode = singleNode);
}
return NS_OK;
}
NS_HIDDEN_(nsresult)
NS_NewXFormsCopyElement(nsIXTFElement **aResult)
{
*aResult = new nsXFormsCopyElement();
if (!*aResult)
return NS_ERROR_OUT_OF_MEMORY;
NS_ADDREF(*aResult);
return NS_OK;
}

Просмотреть файл

@ -73,6 +73,7 @@ NS_HIDDEN_(nsresult) NS_NewXFormsValueElement(nsIXTFElement **aElement);
NS_HIDDEN_(nsresult) NS_NewXFormsChoicesElement(nsIXTFElement **aElement);
NS_HIDDEN_(nsresult) NS_NewXFormsItemSetElement(nsIXTFElement **aElement);
NS_HIDDEN_(nsresult) NS_NewXFormsRangeElement(nsIXTFElement **aElement);
NS_HIDDEN_(nsresult) NS_NewXFormsCopyElement(nsIXTFElement **aElement);
//Action Module Elements
NS_HIDDEN_(nsresult) NS_NewXFormsDispatchElement(nsIXTFElement **aResult);
@ -191,6 +192,8 @@ nsXFormsElementFactory::CreateElement(const nsAString& aTagName,
return NS_NewXFormsUploadElement(aElement);
if (aTagName.EqualsLiteral("range"))
return NS_NewXFormsRangeElement(aElement);
if (aTagName.EqualsLiteral("copy"))
return NS_NewXFormsCopyElement(aElement);
*aElement = nsnull;
return NS_ERROR_FAILURE;

Просмотреть файл

@ -56,6 +56,8 @@
#include "nsIXFormsLabelElement.h"
#include "nsIDocument.h"
#include "nsXFormsModelElement.h"
#include "nsIXFormsCopyElement.h"
#include "nsIDOMEventTarget.h"
/**
* nsXFormsItemElement implements the XForms \<item\> element.
@ -69,7 +71,8 @@ class nsXFormsItemElement : public nsXFormsBindableStub,
public nsIXFormsItemElement
{
public:
nsXFormsItemElement() : mElement(nsnull), mDoneAddingChildren(PR_FALSE)
nsXFormsItemElement() : mElement(nsnull), mDoneAddingChildren(PR_FALSE),
mIsCopyItem(PR_FALSE)
{
}
@ -91,9 +94,14 @@ public:
// nsIXFormsSelectChild
NS_DECL_NSIXFORMSSELECTCHILD
private:
nsIDOMElement* mElement;
PRBool mDoneAddingChildren;
// If true, indicates that this item contains a xf:copy element (via
// xf:itemset) rather than a xf:value element
PRBool mIsCopyItem;
};
NS_IMPL_ISUPPORTS_INHERITED2(nsXFormsItemElement,
@ -219,12 +227,50 @@ nsXFormsItemElement::SelectItemByValue(const nsAString &aValue, nsIDOMNode **aSe
{
NS_ENSURE_ARG_POINTER(aSelected);
NS_ENSURE_STATE(mElement);
nsAutoString value;
GetValue(value);
if (aValue.Equals(value)) {
NS_ADDREF(*aSelected = mElement);
} else {
*aSelected = nsnull;
if (mIsCopyItem) {
// copy items are selected by node, not by value
return NS_OK;
}
nsAutoString value;
nsresult rv = GetValue(value);
if (NS_SUCCEEDED(rv) && aValue.Equals(value)) {
NS_ADDREF(*aSelected = mElement);
}
return rv;
}
NS_IMETHODIMP
nsXFormsItemElement::SelectItemByNode(nsIDOMNode *aNode, nsIDOMNode **aSelected)
{
NS_ENSURE_ARG_POINTER(aSelected);
NS_ENSURE_STATE(mElement);
PRBool isCopyItem;
*aSelected = nsnull;
// If this item doesn't contain a copy element but instead has a value
// element, then there is no sense testing further.
GetIsCopyItem(&isCopyItem);
if (!isCopyItem) {
return NS_ERROR_FAILURE;
}
nsCOMPtr<nsIDOMNode> copyNode;
nsresult rv = GetCopyNode(getter_AddRefs(copyNode));
NS_ENSURE_STATE(copyNode);
PRUint16 nodeType;
copyNode->GetNodeType(&nodeType);
// copy elements are only allowed to bind to ELEMENT_NODEs per spec. But
// test first before doing all of this work.
if ((nodeType == nsIDOMNode::ELEMENT_NODE) &&
(nsXFormsUtils::AreNodesEqual(copyNode, aNode))) {
NS_ADDREF(*aSelected = mElement);
}
return NS_OK;
@ -233,6 +279,15 @@ nsXFormsItemElement::SelectItemByValue(const nsAString &aValue, nsIDOMNode **aSe
NS_IMETHODIMP
nsXFormsItemElement::GetValue(nsAString &aValue)
{
PRBool isCopyItem;
GetIsCopyItem(&isCopyItem);
if (isCopyItem) {
// if this item was built by an itemset and the itemset's template used
// a copy element, then there is no value element to be had. No sense
// continuing.
aValue.Truncate(0);
return NS_ERROR_FAILURE;
}
nsCOMPtr<nsIDOMNode> firstChild, container;
mElement->GetFirstChild(getter_AddRefs(firstChild));
@ -271,6 +326,52 @@ nsXFormsItemElement::GetValue(nsAString &aValue)
return NS_OK;
}
NS_IMETHODIMP
nsXFormsItemElement::GetCopyNode(nsIDOMNode **aNode)
{
NS_ENSURE_ARG_POINTER(aNode);
PRBool isCopyItem;
GetIsCopyItem(&isCopyItem);
if (!isCopyItem) {
// If this item doesn't contain a copy element but instead has a value
// element, then there is no sense continuing.
*aNode = nsnull;
return NS_ERROR_FAILURE;
}
// Since this item really contains a copy element, then firstChild MUST be
// a contextcontainer since copy elements can only exist as a child of an
// itemset.
nsCOMPtr<nsIDOMNode> container;
mElement->GetFirstChild(getter_AddRefs(container));
// Find the copy element contained by this item and get the copyNode from it.
nsCOMPtr<nsIDOMNodeList> children;
nsresult rv = container->GetChildNodes(getter_AddRefs(children));
NS_ENSURE_SUCCESS(rv, rv);
PRUint32 childCount;
children->GetLength(&childCount);
nsCOMPtr<nsIDOMNode> child;
nsAutoString value;
for (PRUint32 i = 0; i < childCount; ++i) {
children->Item(i, getter_AddRefs(child));
nsCOMPtr<nsIXFormsCopyElement> copyElement = do_QueryInterface(child);
if (copyElement) {
return copyElement->GetCopyNode(aNode);
}
}
// No copy element as a child. Set return node to null and set the copyitem
// boolean to false so we don't go through this unnecessary pain again.
aNode = nsnull;
SetIsCopyItem(PR_FALSE);
return NS_OK;
}
void
nsXFormsItemElement::Refresh()
{
@ -368,6 +469,21 @@ nsXFormsItemElement::LabelRefreshed()
return NS_OK;
}
NS_IMETHODIMP
nsXFormsItemElement::GetIsCopyItem(PRBool *aIsCopyItem)
{
NS_ENSURE_ARG(aIsCopyItem);
*aIsCopyItem = mIsCopyItem;
return NS_OK;
}
NS_IMETHODIMP
nsXFormsItemElement::SetIsCopyItem(PRBool aIsCopyItem)
{
mIsCopyItem = aIsCopyItem;
return NS_OK;
}
NS_HIDDEN_(nsresult)
NS_NewXFormsItemElement(nsIXTFElement **aResult)
{

Просмотреть файл

@ -190,6 +190,38 @@ nsXFormsItemSetElement::SelectItemByValue(const nsAString &aValue, nsIDOMNode **
return NS_OK;
}
NS_IMETHODIMP
nsXFormsItemSetElement::SelectItemByNode(nsIDOMNode *aNode,
nsIDOMNode **aSelected)
{
NS_ENSURE_ARG_POINTER(aSelected);
NS_ENSURE_STATE(mElement);
*aSelected = nsnull;
// nsIXFormsItemSetUIElement is implemented by the XBL binding.
nsCOMPtr<nsIXFormsItemSetUIElement> uiItemSet(do_QueryInterface(mElement));
NS_ENSURE_STATE(uiItemSet);
nsCOMPtr<nsIDOMElement> anonContent;
uiItemSet->GetAnonymousItemSetContent(getter_AddRefs(anonContent));
NS_ENSURE_STATE(anonContent);
nsCOMPtr<nsIDOMNode> child, tmp;
anonContent->GetFirstChild(getter_AddRefs(child));
// Trying to select the first possible (generated) \<item\> element.
while (child) {
nsCOMPtr<nsIXFormsSelectChild> selectChild(do_QueryInterface(child));
if (selectChild) {
selectChild->SelectItemByNode(aNode, aSelected);
if (*aSelected) {
return NS_OK;
}
}
tmp.swap(child);
tmp->GetNextSibling(getter_AddRefs(child));
}
return NS_OK;
}
NS_IMETHODIMP
nsXFormsItemSetElement::Bind()
{

Просмотреть файл

@ -47,6 +47,7 @@
#include "nsDeque.h"
#include "nsIModelElementPrivate.h"
#include "nsXFormsUtils.h"
#include "nsDOMError.h"
#ifdef DEBUG
//# define DEBUG_XF_MDG
@ -743,6 +744,101 @@ nsXFormsMDGEngine::SetNodeValueInternal(nsIDOMNode *aContextNode,
return NS_OK;
}
nsresult
nsXFormsMDGEngine::SetNodeContent(nsIDOMNode *aContextNode,
nsIDOMNode *aContentEnvelope,
PRBool *aNodeChanged)
{
NS_ENSURE_ARG(aContextNode);
NS_ENSURE_ARG(aContentEnvelope);
// ok, this is tricky. This function will REPLACE the contents of
// aContextNode with the CONTENTS of aContentEnvelope. No, not a clone of
// the contents, but the contents themselves. If aContentEnvelope has no
// contents, then any contents that aContextNode has will still be removed.
// In order to determine whether the incoming node content is the same as what
// is already contained in aContextNode, aContentEnvelope MUST be a clone (not
// deep) of aContextNode, otherwise aNodeChanged will always be returned as
// being PR_TRUE. I took this approach because I think it is much more
// efficient for the caller to build a complete list of what goes in the
// contents in one go rather than allowing any number of appends to existing
// content one node at a time. There are quite a few links in the call chain
// to go from nsXFormsDelegateStub to here.
if (aNodeChanged) {
*aNodeChanged = PR_FALSE;
}
const nsXFormsNodeState* ns = GetNodeState(aContextNode);
NS_ENSURE_TRUE(ns, NS_ERROR_FAILURE);
// If the node is read-only and not set by a @calculate MIP,
// ignore the call
if (ns->IsReadonly()) {
///
/// @todo Better feedback for readonly nodes? (XXX)
return NS_OK;
}
PRUint16 nodeType;
nsresult rv = aContextNode->GetNodeType(&nodeType);
NS_ENSURE_SUCCESS(rv, rv);
if (nodeType != nsIDOMNode::ELEMENT_NODE) {
// got to return something pretty unique that we can check down the road in
// order to dispatch any error events
return NS_ERROR_DOM_WRONG_TYPE_ERR;
}
PRBool nodesEqual = nsXFormsUtils::AreNodesEqual(aContextNode,
aContentEnvelope,
PR_FALSE);
if (nodesEqual) {
return NS_OK;
}
// remove any child nodes that aContextNode already contains
nsCOMPtr<nsIDOMNode> resultNode;
nsCOMPtr<nsIDOMNodeList> childList;
rv = aContextNode->GetChildNodes(getter_AddRefs(childList));
NS_ENSURE_SUCCESS(rv, rv);
if (childList) {
PRUint32 length;
rv = childList->GetLength(&length);
NS_ENSURE_SUCCESS(rv, rv);
for (PRInt32 i = length-1; i >= 0; i--) {
nsCOMPtr<nsIDOMNode> childNode;
rv = childList->Item(i, getter_AddRefs(childNode));
NS_ENSURE_SUCCESS(rv, rv);
rv = aContextNode->RemoveChild(childNode, getter_AddRefs(resultNode));
NS_ENSURE_SUCCESS(rv, rv);
}
}
// add contents of the envelope under aContextNode
nsCOMPtr<nsIDOMNode> childNode;
rv = aContentEnvelope->GetFirstChild(getter_AddRefs(childNode));
NS_ENSURE_SUCCESS(rv, rv);
while (childNode) {
rv = aContextNode->AppendChild(childNode, getter_AddRefs(resultNode));
NS_ENSURE_SUCCESS(rv, rv);
rv = aContentEnvelope->GetFirstChild(getter_AddRefs(childNode));
NS_ENSURE_SUCCESS(rv, rv);
}
// NB: Never reached for Readonly nodes.
if (aNodeChanged) {
*aNodeChanged = PR_TRUE;
}
// Not calling MarkNodeAsChanged since caller will do a full rebuild
return NS_OK;
}
const nsXFormsNodeState*
nsXFormsMDGEngine::GetNodeState(nsIDOMNode *aContextNode)
{

Просмотреть файл

@ -326,6 +326,7 @@ protected:
PRBool aMarkNode = PR_TRUE,
PRBool aIsCalculate = PR_FALSE,
PRBool *aNodeChanged = nsnull);
public:
/**
* Constructor
@ -419,6 +420,18 @@ public:
nsresult GetNodeValue(nsIDOMNode *aContextNode,
nsAString &aNodeValue);
/**
* Set the contents of a node
*
* @param aContextNode The node to set the contents of
* @param aContentEnvelope The container of the contents that need to be
* moved under aContextNode
* @param aNodeChanged Was node changed?
*/
nsresult SetNodeContent(nsIDOMNode *aContextNode,
nsIDOMNode *aContentEnvelope,
PRBool *aNodeChanged = nsnull);
/**
* External interface of GetNCNodeState(), returns const pointer to the node
* state.

Просмотреть файл

@ -1138,6 +1138,16 @@ nsXFormsModelElement::GetNodeValue(nsIDOMNode *aContextNode,
aNodeValue);
}
NS_IMETHODIMP
nsXFormsModelElement::SetNodeContent(nsIDOMNode *aContextNode,
nsIDOMNode *aNodeContent,
PRBool *aNodeChanged)
{
return mMDG.SetNodeContent(aContextNode,
aNodeContent,
aNodeChanged);
}
NS_IMETHODIMP
nsXFormsModelElement::ValidateNode(nsIDOMNode *aInstanceNode, PRBool *aResult)
{

Просмотреть файл

@ -95,6 +95,10 @@
#include "nsIDOMAbstractView.h"
#include "nsPIDOMWindow.h"
#include "nsIDOMDocumentType.h"
#include "nsIDOMEntity.h"
#include "nsIDOMNotation.h"
#define CANCELABLE 0x01
#define BUBBLES 0x02
@ -1625,3 +1629,365 @@ nsXFormsUtils::HandleBindingException(nsIDOMElement *aElement)
nsnull, getter_AddRefs(messageWindow));
return NS_SUCCEEDED(rv);
}
/* static */ PRBool
nsXFormsUtils::AreEntitiesEqual(nsIDOMNamedNodeMap *aEntities1,
nsIDOMNamedNodeMap *aEntities2)
{
if (!aEntities1 && !aEntities2) {
return PR_TRUE;
}
if (!aEntities1 || !aEntities2) {
return PR_FALSE;
}
PRUint32 entLength1, entLength2;
nsresult rv1 = aEntities1->GetLength(&entLength1);
nsresult rv2 = aEntities2->GetLength(&entLength2);
if (NS_FAILED(rv1) || NS_FAILED(rv2) || entLength1 != entLength2) {
return PR_FALSE;
}
nsAutoString buffer1, buffer2;
for (PRUint32 i = 0; i < entLength1; ++i) {
nsCOMPtr<nsIDOMNode> entNode1, entNode2;
rv1 = aEntities1->Item(i, getter_AddRefs(entNode1));
rv2 = aEntities2->Item(i, getter_AddRefs(entNode2));
if (NS_FAILED(rv1) || NS_FAILED(rv2) || !entNode1 || !entNode2) {
return PR_FALSE;
}
nsCOMPtr<nsIDOMEntity> ent1, ent2;
ent1 = do_QueryInterface(entNode1);
ent2 = do_QueryInterface(entNode2);
if (!ent1 || !ent2) {
return PR_FALSE;
}
rv1 = ent1->GetPublicId(buffer1);
rv2 = ent2->GetPublicId(buffer2);
if (NS_FAILED(rv1) || NS_FAILED(rv2) || !buffer1.Equals(buffer2)) {
return PR_FALSE;
}
rv1 = ent1->GetSystemId(buffer1);
rv2 = ent2->GetSystemId(buffer2);
if (NS_FAILED(rv1) || NS_FAILED(rv2) || !buffer1.Equals(buffer2)) {
return PR_FALSE;
}
rv1 = ent1->GetNotationName(buffer1);
rv2 = ent2->GetNotationName(buffer2);
if (NS_FAILED(rv1) || NS_FAILED(rv2) || !buffer1.Equals(buffer2)) {
return PR_FALSE;
}
// XXX: These will need to be uncommented when Mozilla supports these from
// DOM3
#if 0
rv1 = ent1->GetInputEncoding(buffer1);
rv2 = ent2->GetInputEncoding(buffer2);
if (NS_FAILED(rv1) || NS_FAILED(rv2) || !buffer1.Equals(buffer2)) {
return PR_FALSE;
}
rv1 = ent1->GetXmlEncoding(buffer1);
rv2 = ent2->GetXmlEncoding(buffer2);
if (NS_FAILED(rv1) || NS_FAILED(rv2) || !buffer1.Equals(buffer2)) {
return PR_FALSE;
}
rv1 = ent1->GetXmlVersion(buffer1);
rv2 = ent2->GetXmlVersion(buffer2);
if (NS_FAILED(rv1) || NS_FAILED(rv2) || !buffer1.Equals(buffer2)) {
return PR_FALSE;
}
#endif
}
return PR_TRUE;
}
/* static */ PRBool
nsXFormsUtils::AreNotationsEqual(nsIDOMNamedNodeMap *aNotations1,
nsIDOMNamedNodeMap *aNotations2)
{
if (!aNotations1 && !aNotations2) {
return PR_TRUE;
}
if (!aNotations1 || !aNotations2) {
return PR_FALSE;
}
PRUint32 notLength1, notLength2;
nsresult rv1 = aNotations1->GetLength(&notLength1);
nsresult rv2 = aNotations2->GetLength(&notLength2);
if (NS_FAILED(rv1) || NS_FAILED(rv2) || notLength1 != notLength2) {
return PR_FALSE;
}
nsAutoString buffer1, buffer2;
for (PRUint32 j = 0; j < notLength1; ++j) {
nsCOMPtr<nsIDOMNode> notNode1, notNode2;
rv1 = aNotations1->Item(j, getter_AddRefs(notNode1));
rv2 = aNotations2->Item(j, getter_AddRefs(notNode2));
if (NS_FAILED(rv1) || NS_FAILED(rv2) || !notNode1 || !notNode2) {
return PR_FALSE;
}
nsCOMPtr<nsIDOMNotation> notation1, notation2;
notation1 = do_QueryInterface(notNode1);
notation2 = do_QueryInterface(notNode2);
if (!notation1 || !notation2) {
return PR_FALSE;
}
rv1 = notation1->GetPublicId(buffer1);
rv2 = notation2->GetPublicId(buffer2);
if (NS_FAILED(rv1) || NS_FAILED(rv2) || !buffer1.Equals(buffer2)) {
return PR_FALSE;
}
rv1 = notation1->GetSystemId(buffer1);
rv2 = notation2->GetSystemId(buffer2);
if (NS_FAILED(rv1) || NS_FAILED(rv2) || !buffer1.Equals(buffer2)) {
return PR_FALSE;
}
}
return PR_TRUE;
}
/* static */ PRBool
nsXFormsUtils::AreNodesEqual(nsIDOMNode *aFirstNode, nsIDOMNode *aSecondNode,
PRBool aAlreadyNormalized)
{
if (!aFirstNode || !aSecondNode) {
return PR_FALSE;
}
nsresult rv1, rv2;
PRUint16 firstType, secondType;
rv1 = aFirstNode->GetNodeType(&firstType);
rv2 = aSecondNode->GetNodeType(&secondType);
if (NS_FAILED(rv1) || NS_FAILED(rv2) || firstType != secondType) {
return PR_FALSE;
}
nsAutoString buffer1, buffer2;
if (firstType == nsIDOMNode::DOCUMENT_TYPE_NODE) {
nsCOMPtr<nsIDOMDocumentType> doc1 = do_QueryInterface(aFirstNode);
nsCOMPtr<nsIDOMDocumentType> doc2 = do_QueryInterface(aSecondNode);
if (!doc1 || !doc2) {
return PR_FALSE;
}
rv1 = doc1->GetName(buffer1);
rv2 = doc2->GetName(buffer2);
if (NS_FAILED(rv1) || NS_FAILED(rv2) || !buffer1.Equals(buffer2)) {
return PR_FALSE;
}
rv1 = doc1->GetPublicId(buffer1);
rv2 = doc2->GetPublicId(buffer2);
if (NS_FAILED(rv1) || NS_FAILED(rv2) || !buffer1.Equals(buffer2)) {
return PR_FALSE;
}
rv1 = doc1->GetSystemId(buffer1);
rv2 = doc2->GetSystemId(buffer2);
if (NS_FAILED(rv1) || NS_FAILED(rv2) || !buffer1.Equals(buffer2)) {
return PR_FALSE;
}
rv1 = doc1->GetInternalSubset(buffer1);
rv2 = doc2->GetInternalSubset(buffer2);
if (NS_FAILED(rv1) || NS_FAILED(rv2) || !buffer1.Equals(buffer2)) {
return PR_FALSE;
}
nsCOMPtr<nsIDOMNamedNodeMap> map1, map2;
rv1 = doc1->GetEntities(getter_AddRefs(map1));
rv2 = doc2->GetEntities(getter_AddRefs(map2));
// XXX need to handle the case where neither has entities?
if (NS_FAILED(rv1) || NS_FAILED(rv2)) {
return PR_FALSE;
}
PRBool equal = nsXFormsUtils::AreEntitiesEqual(map1, map2);
if (!equal) {
return PR_FALSE;
}
rv1 = doc1->GetNotations(getter_AddRefs(map1));
rv2 = doc2->GetNotations(getter_AddRefs(map2));
if (NS_FAILED(rv1) || NS_FAILED(rv2)) {
return PR_FALSE;
}
equal = nsXFormsUtils::AreNotationsEqual(map1, map2);
if (!equal) {
return PR_FALSE;
}
}
rv1 = aFirstNode->GetNodeName(buffer1);
rv2 = aSecondNode->GetNodeName(buffer2);
if (NS_FAILED(rv1) || NS_FAILED(rv2) || !buffer1.Equals(buffer2)) {
return PR_FALSE;
}
rv1 = aFirstNode->GetLocalName(buffer1);
rv2 = aSecondNode->GetLocalName(buffer2);
if (NS_FAILED(rv1) || NS_FAILED(rv2) || !buffer1.Equals(buffer2)) {
return PR_FALSE;
}
rv1 = aFirstNode->GetNamespaceURI(buffer1);
rv2 = aSecondNode->GetNamespaceURI(buffer2);
if (NS_FAILED(rv1) || NS_FAILED(rv2) || !buffer1.Equals(buffer2)) {
return PR_FALSE;
}
rv1 = aFirstNode->GetPrefix(buffer1);
rv2 = aSecondNode->GetPrefix(buffer2);
if (NS_FAILED(rv1) || NS_FAILED(rv2) || !buffer1.Equals(buffer2)) {
return PR_FALSE;
}
rv1 = aFirstNode->GetNodeValue(buffer1);
rv2 = aSecondNode->GetNodeValue(buffer2);
if (NS_FAILED(rv1) || NS_FAILED(rv2) || !buffer1.Equals(buffer2)) {
return PR_FALSE;
}
PRBool hasAttr1, hasAttr2;
rv1 = aFirstNode->HasAttributes(&hasAttr1);
rv2 = aSecondNode->HasAttributes(&hasAttr2);
if (NS_FAILED(rv1) || NS_FAILED(rv2) || hasAttr1 != hasAttr2) {
return PR_FALSE;
}
if (hasAttr1) {
nsCOMPtr<nsIDOMNamedNodeMap> attrs1, attrs2;
PRUint32 attrLength1, attrLength2;
rv1 = aFirstNode->GetAttributes(getter_AddRefs(attrs1));
rv2 = aSecondNode->GetAttributes(getter_AddRefs(attrs2));
if (NS_FAILED(rv1) || NS_FAILED(rv2)) {
return PR_FALSE;
}
rv1 = attrs1->GetLength(&attrLength1);
rv2 = attrs2->GetLength(&attrLength2);
if (NS_FAILED(rv1) || NS_FAILED(rv2) || attrLength1 != attrLength2) {
return PR_FALSE;
}
// the order of the attributes on the two nodes doesn't matter. But
// every attribute on node1 must exist on node2 (and no more)
for (PRUint32 i = 0; i < attrLength1; ++i) {
nsCOMPtr<nsIDOMNode> attr1, attr2;
rv1 = attrs1->Item(i, getter_AddRefs(attr1));
if (!attr1) {
return PR_FALSE;
}
attr1->GetLocalName(buffer1);
attr1->GetNamespaceURI(buffer2);
attrs2->GetNamedItemNS(buffer2, buffer1, getter_AddRefs(attr2));
if (!attr2) {
return PR_FALSE;
}
rv1 = attr1->GetNodeValue(buffer1);
rv2 = attr2->GetNodeValue(buffer2);
if (NS_FAILED(rv1) || NS_FAILED(rv2) || !buffer1.Equals(buffer2)) {
return PR_FALSE;
}
}
}
// now looking at the child nodes. They have to be 'equal' and at the same
// index inside each of the parent nodes.
PRBool hasChildren1, hasChildren2;
rv1 = aFirstNode->HasChildNodes(&hasChildren1);
rv2 = aSecondNode->HasChildNodes(&hasChildren2);
if (NS_FAILED(rv1) || NS_FAILED(rv2) || hasChildren1 != hasChildren2) {
return PR_FALSE;
}
if (hasChildren1) {
nsCOMPtr<nsIDOMNodeList> children1, children2;
PRUint32 childrenLength1, childrenLength2;
rv1 = aFirstNode->GetChildNodes(getter_AddRefs(children1));
rv2 = aSecondNode->GetChildNodes(getter_AddRefs(children2));
if (NS_FAILED(rv1) || NS_FAILED(rv2) || !children1 || !children2) {
return PR_FALSE;
}
rv1 = children1->GetLength(&childrenLength1);
rv2 = children2->GetLength(&childrenLength2);
if (NS_FAILED(rv1) || NS_FAILED(rv2) || childrenLength1 != childrenLength2) {
return PR_FALSE;
}
nsCOMPtr<nsIDOMNode> clone1, clone2;
if (!aAlreadyNormalized) {
// well we avoided this as long as we can. If we haven't already
// normalized all children, now is the time to do it. We'll have to clone
// nodes since the normalization process actually changes the DOM.
rv1 = aFirstNode->CloneNode(PR_TRUE, getter_AddRefs(clone1));
if (NS_FAILED(rv1) || !clone1) {
return PR_FALSE;
}
rv2 = aSecondNode->CloneNode(PR_TRUE, getter_AddRefs(clone2));
if (NS_FAILED(rv2) || !clone2) {
return PR_FALSE;
}
rv1 = clone1->Normalize();
rv2 = clone2->Normalize();
if (NS_FAILED(rv1) || NS_FAILED(rv2)) {
return PR_FALSE;
}
// since this already worked once on the original nodes, won't bother
// checking the results for the clones
clone1->GetChildNodes(getter_AddRefs(children1));
clone2->GetChildNodes(getter_AddRefs(children2));
// get length again since normalizing may have eliminated some text nodes
rv1 = children1->GetLength(&childrenLength1);
rv2 = children2->GetLength(&childrenLength2);
if (NS_FAILED(rv1) || NS_FAILED(rv2) || childrenLength1 != childrenLength2) {
return PR_FALSE;
}
}
for (PRUint32 i = 0; i < childrenLength1; ++i) {
nsCOMPtr<nsIDOMNode> child1, child2;
rv1 = children1->Item(i, getter_AddRefs(child1));
rv2 = children2->Item(i, getter_AddRefs(child2));
if (NS_FAILED(rv1) || NS_FAILED(rv2)) {
return PR_FALSE;
}
PRBool areEqual = nsXFormsUtils::AreNodesEqual(child1, child2, PR_TRUE);
if (!areEqual) {
return PR_FALSE;
}
}
}
return PR_TRUE;
}

Просмотреть файл

@ -465,6 +465,39 @@ public:
* @return Whether handling was successful
*/
static PRBool HandleBindingException(nsIDOMElement *aElement);
/**
* Returns whether the given NamedNodeMaps of Entities are equal
*
*/
static NS_HIDDEN_(PRBool) AreEntitiesEqual(nsIDOMNamedNodeMap *aEntities1,
nsIDOMNamedNodeMap *aEntities2);
/**
* Returns whether the given NamedNodeMaps of Notations are equal
*
*/
static NS_HIDDEN_(PRBool) AreNotationsEqual(nsIDOMNamedNodeMap *aNotations1,
nsIDOMNamedNodeMap *aNotations2);
/**
* Returns whether the given nodes are equal as described in the isEqualNode
* function defined in the DOM Level 3 Core spec.
* http://www.w3.org/TR/2004/REC-DOM-Level-3-Core-20040407/DOM3-Core.html#core-Node3-isEqualNode
*
* XXX: this is just temporary until isEqualNode is implemented in Mozilla
* (https://bugzilla.mozilla.org/show_bug.cgi?id=159167)
*
* @param aFirstNode The first node to compare
* @param aSecondNode The second node to compare
* @param aAlreadyNormalized Whether the two nodes and their children, etc.
* have already been normalized to allow for
* more accurate child node comparisons, as
* recommended in the DOM Level 3 Core spec.
*/
static NS_HIDDEN_(PRBool) AreNodesEqual(nsIDOMNode *aFirstNode,
nsIDOMNode *aSecondNode,
PRBool aAlreadyNormalized = PR_FALSE);
};
#endif

Просмотреть файл

@ -138,22 +138,49 @@
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 delegate cache is
// null (first load).
if (this._accessorValueCache == null ||
this._accessorValueCache == this.accessors.getValue()) {
// refresh was not called due to instance data changing, so build the
// UI.
this._buildSelect();
} else {
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.
this._buildSelect(containsNonText);
} else if (!containsNonText) {
// update selection
this._updateSelection();
}
// store the delegate value
this._accessorValueCache = this.accessors.getValue();
}
this._refreshing = false;
@ -164,29 +191,93 @@
<field name="_controlArraySize">0</field>
<field name="_controlArray">new Array()</field>
<field name="_selectedElementSize">0</field>
<field name="_selectedElementArray">new Array()</field>
<field name="_defaultHash">null</field>
<method name="_buildSelect">
<parameter name="aContainsNonText"/>
<body>
<![CDATA[
// select builds its own UI by parsing it's children.
// replace new line (\n), tabs (\t) and carriage returns (\r) with "".
var value = "";
// if delegate.value has something, then only text node(s) should
// exist under the bound node
if (this.accessors.getValue())
value = this.accessors.getValue().replace(/\n|\t|\r/g, " ");
// holds an array of DOMElements that exist under bound node,
this._selectedElementSize = 0;
this._selectedElementArray = new Array();
if (!aContainsNonText) {
// replace new line (\n), tabs (\t) and carriage returns (\r) with
// "".
var value = "";
var accessValue = this.accessors.getValue();
if (accessValue)
value = accessValue.replace(/\n|\t|\r/g, " ");
// get an array of values selected in the bound node
var selectedArray = value.split(" ");
// 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.
// 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();
for (var run = 0; run < selectedArray.length; run++) {
this._defaultHash[selectedArray[run]] = {hits: 0}
}
} else {
var boundNode = this.accessors.getBoundNode();
var child = boundNode ? boundNode.firstChild : null;
this._defaultHash = null;
while (child) {
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) {
// replace new line (\n), tabs (\t) and carriage returns (\r)
// with " ".
var value = string.replace(/\n|\t|\r/g, " ");
// get an array of values selected in the bound node
var selectedArray = value.split(" ");
// 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.
if (!this._defaultHash) {
this._defaultHash = new Object();
}
for (var run = 0; run < selectedArray.length; run++) {
this._defaultHash[selectedArray[run]] = {hits: 0}
}
}
} 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.
this._selectedElementArray[this._selectedElementSize] =
{element: child, hits: 0}
this._selectedElementSize++;
}
if (child == boundNode.lastChild) {
break;
}
child = child.nextSibling;
}
}
// clear the UI children
for (var i = this.uiElement.childNodes.length; i > 0; i--) {
@ -225,12 +316,13 @@
var uiElement =
child.QueryInterface(Components.interfaces.nsIXFormsItemSetUIElement);
var containers = uiElement.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 == containers[y].ELEMENT_NODE) {
var value = this._addItemSetItem(containers[y]);
// check if we should pre-select this option
this.preselectItem(value);
this._addItemSetItem(containers[y]);
}
}
}
@ -247,6 +339,18 @@
}
}
// check if any default elements were not found
for (var j = 0; j < this._selectedElementSize; j++) {
if (this._selectedElementArray[j].hits == 0) {
// XXX: some of default values not found, we need to throw an
// xforms-out-of-range event, but only if the select is 'closed'.
// If the select is open, the missing elements should be added
// and selected per 8.1.10 in the spec.
}
}
return true;
]]>
</body>
@ -365,10 +469,33 @@
<body>
<![CDATA[
var itemElm = aItemElement.QueryInterface(Components.interfaces.nsIXFormsItemElement);
var itemValue = itemElm.value;
var copyItem = itemElm.isCopyItem;
var itemValue = copyItem ? "" : itemElm.value;
var itemLabel = itemElm.labelText;
var option = this._buildSelectItem(itemLabel, itemValue, aItemElement, true);
var option = this._buildSelectItem(itemLabel, itemValue, aItemElement,
true);
// 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._selectedElementSize > 0) {
var item = aItemElement.QueryInterface(Components.interfaces.nsIXFormsSelectChild);
for (var j = 0; j < this._selectedElementSize; j++ ) {
var selectedItem =
item.selectItemByNode(this._selectedElementArray[j].element);
if (selectedItem) {
this._selectedElementArray[j].hits++;
option.selected = true;
// 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.
}
}
}
// add to the control array
this._controlArray[this._controlArraySize] =
@ -377,6 +504,12 @@
this.uiElement.appendChild(option);
if (!copyItem) {
// if this item contains a value element, then make sure to select
// this item if its value exists under the bound node.
this.preselectItem(itemValue);
}
return itemValue;
]]>
</body>
@ -388,7 +521,7 @@
<![CDATA[
// if incremental, change instance data and send the value-changed event
if (this.incremental) {
this._setBoundValue();
this._handleSelection();
} else {
// per the spec, if not incremental, we still need to send the
// deselect/select events. which _getSelectedValues() does for us
@ -398,11 +531,32 @@
</body>
</method>
<method name="_setBoundValue">
<method name="_handleSelection">
<body>
<![CDATA[
if (this.accessors.hasBoundNode)
this.accessors.setValue(this._getSelectedValues());
var boundNode = this.accessors.getBoundNode();
if (!boundNode) {
return;
}
var contentEnvelope = this._getSelectedValues();
if (contentEnvelope) {
if (boundNode.nodeType == Node.ELEMENT_NODE) {
this.accessors.setContent(contentEnvelope);
} else {
// if some copyItems were selected by the user prior to the call
// to _getSelectedValues, then we would not have set up
// _delegateValueCache. 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
// _getSelectedValues. Thus, anything in the contentEnvelope at
// this point should just be strings and so we can set
// delegate.value directly and use _delegateValueCache after all.
this.accessors.setValue(contentEnvelope.nodeValue);
this._accessorValueCache = contentEnvelope.nodeValue;
}
}
]]>
</body>
</method>
@ -416,6 +570,29 @@
// select if found, unselect if not
var options = this._controlArray;
var boundNode = this.accessors.getBoundNode();
if (!boundNode) {
return;
}
// 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) {
return;
}
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 =
options[i].option ? options[i].option.selected : options[i].checkbox.checked;
@ -426,31 +603,94 @@
selectedValues += " ";
}
selectedValues +=
options[i].control.QueryInterface(Components.interfaces.nsIXFormsSelectChild).value;
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.
bindingException = document.createEvent("Events");
bindingException.initEvent("xforms-binding-exception", true, false);
this.dispatchEvent(bindingException);
// if it wasn't selected before add to the list of newly selected items
// 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.
if (options[i].option) {
options[i].option.selected = false;
} else {
options[i].checkbox.checked = false;
}
} else {
copyNode = item.copyNode;
if (copyNode) {
var clone = copyNode.cloneNode(true);
contentEnvelope.appendChild(clone);
}
// if it wasn't selected before add to the list of newly
// selected items
if (!options[i].wasSelected) {
newSelectedControls.push(options[i].control);
}
options[i].wasSelected = 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;
// if it wasn't selected before add to the list of newly
// selected items
if (!options[i].wasSelected) {
newSelectedControls.push(options[i].control);
}
options[i].wasSelected = true;
}
} else {
// it was selected before, but now unselected
if (options[i].wasSelected) {
this.dispatchSelectEvent(options[i].control, "xforms-deselect");
// XXX if this is a copyItem, we'll need to rebuild the model
// per spec.
}
options[i].wasSelected = false;
}
}
// write out the text nodes before we handle copy
if (boundType == Node.ELEMENT_NODE) {
if (selectedValues.length > 0) {
var textNode = document.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;
}
selectedValues = "";
// we send xforms-select after all deselect events are thrown
for (var i = 0; i < newSelectedControls.length; i++) {
this.dispatchSelectEvent(newSelectedControls[i], "xforms-select");
}
return selectedValues;
return contentEnvelope;
]]>
</body>
</method>
@ -458,6 +698,11 @@
<method name="_updateSelection">
<body>
<![CDATA[
// this function only looks through the text values that are stored
// under the bound node and selects the xf:items that have
// corresponding values to that list. As such, copyItems will
// be ignored.
// get an array of values selected in the bound node
var selectedArray = new Array();
if (this.accessors.getValue())
@ -473,8 +718,11 @@
var options = this._controlArray;
for (var i = 0; i < options.length; i++) {
var value =
options[i].control.QueryInterface(Components.interfaces.nsIXFormsSelectChild).value;
var item = options[i].control.QueryInterface(Components.interfaces.nsIXFormsSelectChild);
if (item.isCopyItem) {
break;
}
var value = item.value;
var selectionValue = (this._defaultHash[value] != null);
// either a checkbox or an option
@ -520,7 +768,7 @@
<![CDATA[
// update instance data if we are not incremental
if (!this.incremental) {
this._setBoundValue();
this._handleSelection();
}
]]>
</body>
@ -531,7 +779,7 @@
<body>
<![CDATA[
// check if we should pre-select this option
if (this._defaultHash[aValue] != null) {
if (this._defaultHash && this._defaultHash[aValue] != null) {
var control = this._controlArray[this._controlArraySize - 1]
this._setItemSelection(control, true);
@ -726,10 +974,12 @@
<body>
<![CDATA[
var itemElm = aItemElement.QueryInterface(Components.interfaces.nsIXFormsItemElement);
var copyItem = itemElm.isCopyItem;
var itemValue = itemElm.value;
var itemLabel = itemElm.labelText;
var itemLabel = copyItem ? "" : itemElm.labelText;
var item = this._buildSelectItem(itemLabel, itemValue, aItemElement, true);
var item = this._buildSelectItem(itemLabel, itemValue, aItemElement,
true);
// add to the control array
this._controlArray[this._controlArraySize] =
@ -738,6 +988,31 @@
this.uiElement.appendChild(item);
// 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._selectedElementSize > 0) {
var item = aItemElement.QueryInterface(Components.interfaces.nsIXFormsSelectChild);
for (var j = 0; j < this._selectedElementSize; j++ ) {
var selectedItem =
item.selectItemByNode(this._selectedElementArray[j].element);
if (selectedItem) {
this._selectedElementArray[j].hits++;
item.firstChild.checked = true;
// 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.
}
}
} else if (!copyItem) {
// if this item contains a value element, then make sure to select
// this item if its value exists under the bound node.
this.preselectItem(itemValue);
}
return itemValue;
]]>
</body>

Просмотреть файл

@ -154,7 +154,7 @@
<![CDATA[
if (this.selectionOpen && !this._selected) {
if (this.getAttribute("incremental") != "false") {
this.accessors.setValue(this.inputField.value);
this._handleSelection();
}
} else {
this.togglePopup();
@ -173,7 +173,28 @@
this.togglePopup();
if (open && this._selected) {
this.updateInputField();
this.accessors.setValue(this._selected.value);
this._handleSelection();
// need to keep the xforms-select and xforms-deselect event
// dispatching after _handleSelection. That function might
// deselect this._selected if this user selected item is a
// copyItem bound to a non-element node. We don't want to
// dispatch a xforms-select/deselect in that case since it
// really isn't considered to be a valid selection to begin with
// (causes a xforms-binding-exception).
if (this._lastSelectedItem != this._selected) {
if (this._lastSelectedItem) {
// XXX if this is a copyItem, we'll need to rebuild the
// model per spec.
this.dispatchSelectEvent(this._lastSelectedItem, "xforms-deselect");
}
if (this._selected) {
this.dispatchSelectEvent(this._selected, "xforms-select");
}
this._lastSelectedItem = this._selected;
}
}
} else if (key == aEvent.DOM_VK_UP ||
key == aEvent.DOM_VK_DOWN) {
@ -188,9 +209,28 @@
if (!this.popupOpen && this.getAttribute("incremental") != "false") {
if (this._selected) {
this.updateInputField();
this.accessors.setValue(this._selected.value);
} else if (this.selectionOpen) {
this.accessors.setValue(this.inputField.value);
}
this._handleSelection();
// need to keep the xforms-select and xforms-deselect event
// dispatching after _handleSelection. That function might
// deselect this._selected if the selected item is a copyItem
// bound to a non-element node. We don't want to dispatch a
// xforms-select/deselect in that case since it really isn't
// considered to be a valid selection to begin with (causes
// a xforms-binding-exception).
if (this._lastSelectedItem != this._selected) {
if (this._lastSelectedItem) {
// XXX if this is a copyItem, we'll need to rebuild the
// model per spec.
this.dispatchSelectEvent(this._lastSelectedItem, "xforms-deselect");
}
if (this._selected) {
this.dispatchSelectEvent(this._selected, "xforms-select");
}
this._lastSelectedItem = this._selected;
}
}
} else if (key == aEvent.DOM_VK_TAB) {
@ -232,7 +272,7 @@
this._selected = null;
}
if (this.getAttribute("incremental") != "false") {
this.accessors.setValue(this.inputField.value);
this._handleSelection();
}
}
@ -346,16 +386,14 @@
next = this.findNextSelectable(node, aDown);
if (next) {
if (this._selected) {
this.dispatchSelectEvent(this._selected, "xforms-deselect");
this._lastSelected = this._selected;
this._selected.setActive(false);
this._selected = null;
}
nextItem = next.QueryInterface(Components.interfaces.nsIXFormsItemElement);
if (nextItem) {
this.dispatchSelectEvent(nextItem, "xforms-select");
this._selected = nextItem;
this._lastSelectedItem = nextItem;
this._selected.setActive(true);
this.updateInputField();
}
@ -395,16 +433,29 @@
this.hidePopup();
if (this._selected) {
if (this._lastSelectedItem != this._selected) {
if (this._lastSelectedItem)
this.dispatchSelectEvent(this._lastSelectedItem, "xforms-deselect");
this.updateInputField();
this._handleSelection();
this.dispatchSelectEvent(this._selected, "xforms-select");
this._lastSelectedItem = this._selected;
// need to keep the xforms-select and xforms-deselect event
// dispatching after _handleSelection. That function might
// deselect this._selected if the selected item is a copyItem
// bound to a non-element node. We don't want to dispatch a
// xforms-select/deselect in that case since it really isn't
// considered to be a valid selection to begin with (causes
// a xforms-binding-exception).
if (this._lastSelectedItem != this._selected) {
if (this._lastSelectedItem) {
// XXX if this is a copyItem, we'll need to rebuild the
// model per spec.
this.dispatchSelectEvent(this._lastSelectedItem, "xforms-deselect");
}
this.updateInputField();
this.accessors.setValue(this._selected.value);
if (this._selected) {
this.dispatchSelectEvent(this._selected, "xforms-select");
}
this._lastSelectedItem = this._selected;
}
}
this.inputField.focus();
]]>
@ -557,7 +608,72 @@
<body>
<![CDATA[
try {
var newValue = this.stringValue;
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.
// XXX generate xforms-out-of-range exception and style
// control as out-of-range
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.
// XXX generate xforms-out-of-range exception and style
// control as out-of-range
outOfRange = true;
}
nodeValue = child;
}
if (child == boundNode.lastChild) {
break;
}
child = child.nextSibling;
}
}
if (outOfRange) {
// can't possibly work, no sense continuing.
this.inputField.value = "";
this._selected.setActive(false);
this._selected = null;
return false;
}
if (!this.selectionOpen || this.accessors.isReadonly()) {
this.inputField.setAttribute("readonly", "readonly");
@ -565,17 +681,35 @@
this.inputField.removeAttribute("readonly");
}
if (this._selected) {
if (newValue ==
this._selected.value) {
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. Nothin' left
// to do.
return true;
}
} else {
if (newValue == contentEnvelope.nodeValue) {
// value in instance data already selected. Nothin' left
// to do.
return true;
}
}
}
this._selected.setActive(false);
this._selected = null;
this._lastSelectedItem = null;
}
if (newValue) {
this.selectItemByValue(newValue);
} else if (nodeValue) {
this.selectItemByNode(nodeValue);
}
if (this._selected) {
this.updateInputField();
@ -610,7 +744,8 @@
item = null;
try {
if (node.nodeType == document.ELEMENT_NODE &&
node.namespaceURI == this.XFORMS_NS && node.localName != "label") {
node.namespaceURI == this.XFORMS_NS &&
node.localName != "label") {
item = node.QueryInterface(Components.interfaces.nsIXFormsSelectChild);
if (item) {
@ -635,6 +770,45 @@
</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[
@ -654,12 +828,67 @@
return;
}
if (this._selected) {
this.updateInputField();
}
this._handleSelection();
}
]]>
</body>
</method>
// _handleSelection updates the bound node with the value from the
// currently selected item's value element or copy element.
<method name="_handleSelection">
<body>
<![CDATA[
var boundNode = this.accessors.getBoundNode();
if (!boundNode) {
return;
}
if (this.selectionOpen && !this._selected) {
this.accessors.setValue(this.inputField.value);
} else if (this._selected) {
this.updateInputField();
this.accessors.setValue(this._selected.value);
return;
}
if (!this._selected) {
// no reason to continue
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) {
this.accessors.setValue(envelope.nodeValue);
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 = "";
this.accessors.setValue("");
return;
}
var contentEnvelope = this._getSelectedValue();
this.accessors.setContent(contentEnvelope);
}
]]>
</body>
@ -683,6 +912,63 @@
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;
}
var contentEnvelope = boundNode.cloneNode(false);
if (!contentEnvelope) {
return null;
}
if (contentEnvelope.nodeType == Node.ELEMENT_NODE) {
var contentNode = null;
if (this._selected.isCopyItem) {
var copyNode = this._selected.copyNode;
if (copyNode) {
contentNode = copyNode.cloneNode(true);
}
} else {
contentNode = document.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>
</binding>

Просмотреть файл

@ -64,6 +64,7 @@ rangeNullInit = XForms Error (25): One or more init() parameters is NaN
rangeBeginEndError = XForms Error (26): Begin is higher than end?
encodingMemoryError = XForms Error (23): Not enough available memory to encode file %S, size = %S.
uploadBoundTypeError = XForms Error (24): Upload element not bound to valid datatype. Must be bound to datatype 'xsd:anyURI', 'xsd:base64Binary', or 'xsd:hexBinary'.
copyError = XForms Error (25): A copy element was found whose parent is not an itemset element
# Warning Messages:
warnSOAP = XForms Warning (1): You are using the SOAP post feature, which is an experimental feature! Beware that the functionality might change, and forms may stop working at any time.