fixed some basic xforms bug (too many setXFormsValue requests for textfields, multiselect lists not behaving)

- partial fix for repeat 0 to n bug.  still needs much work.

quasi functional nested repeats.  
- refactored the way triggers are generated to use nodesets rather than simple binding.
- reimplemented the way triggers are located in the xform to not rely on ids.
- reimplemented handling of prototype-cloned.  need to actually keep track of prototype ids, and different mechanism for locating the prototype node

what doesn't work:
- it's not usable for many reasons, among them no identation in the ui.
- setRepeatIndex calls aren't bubbling properly so occasionally things get added to the wrong repeat.
- constraints remain unenforced.

git-svn-id: https://svn.alfresco.com/repos/alfresco-enterprise/alfresco/HEAD/root@4490 c4b6b30b-aa2e-2d43-bbcb-ca4b014f7261
This commit is contained in:
Ariel Backenroth
2006-12-02 04:46:23 +00:00
parent ce1ab1a6d0
commit 8d2eb8ff45
3 changed files with 432 additions and 269 deletions

View File

@@ -121,6 +121,11 @@ public class SchemaFormBuilder
this.maximum = maximum; this.maximum = maximum;
} }
public boolean isRepeated()
{
return this.isUnbounded() || this.maximum > 1;
}
public boolean isUnbounded() public boolean isUnbounded()
{ {
return this.maximum == UNBOUNDED; return this.maximum == UNBOUNDED;
@@ -428,14 +433,12 @@ public class SchemaFormBuilder
this.counter = new HashMap(); this.counter = new HashMap();
final Document xForm = createFormTemplate(rootElementName); final Document xForm = createFormTemplate(rootElementName);
final Element envelopeElement = xForm.getDocumentElement();
//find form element: last element created //find form element: last element created
final NodeList children = xForm.getDocumentElement().getChildNodes(); final Element formSection = (Element)
xForm.getDocumentElement().getLastChild();
final Element formSection = (Element)children.item(children.getLength() - 1);
final Element modelSection = (Element) final Element modelSection = (Element)
envelopeElement.getElementsByTagNameNS(XFORMS_NS, "model").item(0); xForm.getDocumentElement().getElementsByTagNameNS(XFORMS_NS, "model").item(0);
//add XMLSchema if we use schema types //add XMLSchema if we use schema types
modelSection.setAttributeNS(XFORMS_NS, "schema", "#schema-1"); modelSection.setAttributeNS(XFORMS_NS, "schema", "#schema-1");
@@ -463,12 +466,6 @@ public class SchemaFormBuilder
// this.targetNamespace); // this.targetNamespace);
final Comment comment =
xForm.createComment("This XForm was automatically generated by " + this.getClass().getName() +
" on " + (new Date()) + " from the '" + rootElementName +
"' element of the '" + this.targetNamespace + "' XML Schema.");
xForm.insertBefore(comment, envelopeElement);
//TODO: WARNING: in Xerces 2.6.1, parameters are switched !!! (name, namespace) //TODO: WARNING: in Xerces 2.6.1, parameters are switched !!! (name, namespace)
//XSElementDeclaration rootElementDecl =schema.getElementDeclaration(this.targetNamespace, _rootElementName); //XSElementDeclaration rootElementDecl =schema.getElementDeclaration(this.targetNamespace, _rootElementName);
XSElementDeclaration rootElementDecl = XSElementDeclaration rootElementDecl =
@@ -564,6 +561,14 @@ public class SchemaFormBuilder
submitButton.appendChild(submitButtonCaption); submitButton.appendChild(submitButtonCaption);
submitButtonCaption.appendChild(xForm.createTextNode("Submit")); submitButtonCaption.appendChild(xForm.createTextNode("Submit"));
this.setXFormsId(submitButtonCaption); this.setXFormsId(submitButtonCaption);
this.createTriggersForRepeats(xForm);
final Comment comment =
xForm.createComment("This XForm was automatically generated by " + this.getClass().getName() +
" on " + (new Date()) + " from the '" + rootElementName +
"' element of the '" + this.targetNamespace + "' XML Schema.");
xForm.getDocumentElement().insertBefore(comment,
xForm.getDocumentElement().getFirstChild());
return xForm; return xForm;
} }
@@ -2132,9 +2137,14 @@ public class SchemaFormBuilder
element, element,
element.getTypeDefinition(), element.getTypeDefinition(),
path); path);
for (int i = 1; i < this.getOccurance(element).minimum; i++)
// update the default instance
if (this.getOccurance(element).isRepeated())
{ {
defaultInstanceElement.appendChild(newDefaultInstanceElement.cloneNode(true)); for (int i = 0; i < this.getOccurance(element).minimum; i++)
{
defaultInstanceElement.appendChild(newDefaultInstanceElement.cloneNode(true));
}
} }
} }
} }
@@ -2207,13 +2217,6 @@ public class SchemaFormBuilder
SchemaFormBuilder.XFORMS_NS_PREFIX + "appearance", SchemaFormBuilder.XFORMS_NS_PREFIX + "appearance",
"full"); "full");
//triggers
this.addTriggersForRepeat(xForm,
formSection,
repeatSection,
o,
bindId);
final Element controlWrapper = final Element controlWrapper =
this.wrapper.createControlsWrapper(repeatSection); this.wrapper.createControlsWrapper(repeatSection);
formSection.appendChild(controlWrapper); formSection.appendChild(controlWrapper);
@@ -2256,18 +2259,17 @@ public class SchemaFormBuilder
bindElement = this.startBindElement(bindElement, schema, controlType, owner, pathToRoot, o); bindElement = this.startBindElement(bindElement, schema, controlType, owner, pathToRoot, o);
// add a group if a repeat ! // add a group if a repeat !
// if (owner instanceof XSElementDeclaration && o.maximum != 1) if (owner instanceof XSElementDeclaration && o.maximum != 1)
// { {
// Element groupElement = this.createGroup(xForm, final Element groupElement = this.createGroup(xForm,
// modelSection, modelSection,
// formSection, formSection,
// (XSElementDeclaration) owner); (XSElementDeclaration)owner);
// //set content //set content
// Element groupWrapper = groupElement; formSection = (groupElement == modelSection
// if (groupElement != modelSection) ? groupElement
// groupWrapper = this.wrapper.createGroupContentWrapper(groupElement); : this.wrapper.createGroupContentWrapper(groupElement));
// formSection = groupWrapper; }
// }
//eventual repeat //eventual repeat
final Element repeatSection = this.addRepeatIfNecessary(xForm, final Element repeatSection = this.addRepeatIfNecessary(xForm,
@@ -2370,160 +2372,8 @@ public class SchemaFormBuilder
pathToRoot, pathToRoot,
new Occurs(owningAttribute.getRequired() ? 1 : 0, 1)); new Occurs(owningAttribute.getRequired() ? 1 : 0, 1));
} }
private Element createTriggerForRepeat(final Document xForm,
final String id,
final String label,
final Element action)
{
final Element trigger =
xForm.createElementNS(SchemaFormBuilder.XFORMS_NS,
SchemaFormBuilder.XFORMS_NS_PREFIX + "trigger");
this.setXFormsId(trigger, id != null ? id : null);
//label insert
final Element triggerLabel =
xForm.createElementNS(SchemaFormBuilder.XFORMS_NS,
SchemaFormBuilder.XFORMS_NS_PREFIX + "label");
this.setXFormsId(triggerLabel);
trigger.appendChild(triggerLabel);
triggerLabel.appendChild(xForm.createTextNode(label));
//hint insert
//Element hint_insert =
// xForm.createElementNS(SchemaFormBuilder.XFORMS_NS,
// SchemaFormBuilder.XFORMS_NS_PREFIX + "hint");
//this.setXFormsId(hint_insert);
//Text hint_insert_text =
// xForm.createTextNode("inserts a new entry in this collection");
//hint_insert.appendChild(hint_insert_text);
//trigger_insert.appendChild(hint_insert);
//insert action
final Element actionWrapper =
xForm.createElementNS(SchemaFormBuilder.XFORMS_NS,
SchemaFormBuilder.XFORMS_NS_PREFIX + "action");
actionWrapper.appendChild(action);
trigger.appendChild(action);
this.setXFormsId(action);
return trigger;
}
/**
* add triggers to use the repeat elements (allow to add an element, ...)
*/
private void addTriggersForRepeat(final Document xForm,
final Element formSection,
final Element repeatSection,
final Occurs o,
final String bindId) {
//xforms:at = xforms:index from the "id" attribute on the repeat element
final String repeatId =
repeatSection.getAttributeNS(SchemaFormBuilder.XFORMS_NS, "id");
///////////// insert //////////////////
//trigger insert
Element action =
xForm.createElementNS(SchemaFormBuilder.XFORMS_NS,
SchemaFormBuilder.XFORMS_NS_PREFIX + "insert");
//insert: bind & other attributes
if (bindId != null)
action.setAttributeNS(SchemaFormBuilder.XFORMS_NS,
SchemaFormBuilder.XFORMS_NS_PREFIX + "bind",
bindId);
action.setAttributeNS(SchemaFormBuilder.XFORMS_NS,
SchemaFormBuilder.XFORMS_NS_PREFIX + "position",
"before");
if (repeatId != null)
action.setAttributeNS(SchemaFormBuilder.XFORMS_NS,
SchemaFormBuilder.XFORMS_NS_PREFIX + "at",
"1");
final Element trigger_insert_before =
this.createTriggerForRepeat(xForm,
repeatId != null ? repeatId + "-insert_before" : null,
"insert at beginning",
action);
action = xForm.createElementNS(SchemaFormBuilder.XFORMS_NS,
SchemaFormBuilder.XFORMS_NS_PREFIX + "insert");
//insert: bind & other attributes
if (bindId != null)
action.setAttributeNS(SchemaFormBuilder.XFORMS_NS,
SchemaFormBuilder.XFORMS_NS_PREFIX + "bind",
bindId);
action.setAttributeNS(SchemaFormBuilder.XFORMS_NS,
SchemaFormBuilder.XFORMS_NS_PREFIX + "position",
"after");
if (repeatId != null)
action.setAttributeNS(SchemaFormBuilder.XFORMS_NS,
SchemaFormBuilder.XFORMS_NS_PREFIX + "at",
SchemaFormBuilder.XFORMS_NS_PREFIX + "index('" + repeatId + "')");
final Element trigger_insert_after =
this.createTriggerForRepeat(xForm,
repeatId != null ? repeatId + "-insert_after" : null,
"insert after selected",
action);
///////////// delete //////////////////
//trigger delete
action = xForm.createElementNS(SchemaFormBuilder.XFORMS_NS,
SchemaFormBuilder.XFORMS_NS_PREFIX + "delete");
//delete: bind & other attributes
if (bindId != null)
action.setAttributeNS(SchemaFormBuilder.XFORMS_NS,
SchemaFormBuilder.XFORMS_NS_PREFIX + "bind",
bindId);
//xforms:at = xforms:index from the "id" attribute on the repeat element
if (repeatId != null)
action.setAttributeNS(SchemaFormBuilder.XFORMS_NS,
SchemaFormBuilder.XFORMS_NS_PREFIX + "at",
SchemaFormBuilder.XFORMS_NS_PREFIX + "index('" + repeatId + "')");
final Element trigger_delete =
this.createTriggerForRepeat(xForm,
repeatId != null ? repeatId + "-delete" : null,
"delete selected",
action);
//add the triggers
final Element wrapper_triggers =
this.wrapper.createControlsWrapper(trigger_insert_before);
if (wrapper_triggers == trigger_insert_before)
{
//no wrapper
formSection.appendChild(trigger_insert_before);
formSection.appendChild(trigger_insert_after);
formSection.appendChild(trigger_delete);
}
else
{
formSection.appendChild(wrapper_triggers);
final Element insert_parent = (Element)trigger_insert_before.getParentNode();
if (insert_parent != null)
{
insert_parent.appendChild(trigger_insert_after);
insert_parent.appendChild(trigger_delete);
}
}
}
private void buildTypeTree(final XSTypeDefinition type, private void buildTypeTree(final XSTypeDefinition type,
final TreeSet descendents) final TreeSet descendents)
{ {
@@ -2691,7 +2541,6 @@ public class SchemaFormBuilder
formControl.appendChild(alertElement); formControl.appendChild(alertElement);
this.setXFormsId(alertElement); this.setXFormsId(alertElement);
final Element envelope = xForm.getDocumentElement();
String alert = this.extractPropertyFromAnnotation(ALFRESCO_NS, String alert = this.extractPropertyFromAnnotation(ALFRESCO_NS,
"alert", "alert",
this.getAnnotation(owner)); this.getAnnotation(owner));
@@ -2699,7 +2548,7 @@ public class SchemaFormBuilder
alert = ("Please provide a valid value for '" + caption + "'." + alert = ("Please provide a valid value for '" + caption + "'." +
" '" + caption + " '" + caption +
"' is " + (o.minimum == 0 ? "an optional" : "a required") + " '" + "' is " + (o.minimum == 0 ? "an optional" : "a required") + " '" +
createCaption(this.getXFormsTypeName(envelope, schema, controlType)) + createCaption(this.getXFormsTypeName(xForm, schema, controlType)) +
"' value."); "' value.");
alertElement.appendChild(xForm.createTextNode(alert)); alertElement.appendChild(xForm.createTextNode(alert));
return formControl; return formControl;
@@ -2712,10 +2561,11 @@ public class SchemaFormBuilder
* @param controlType the type from which we want the name * @param controlType the type from which we want the name
* @return the complete type name (with namespace prefix) of the type in the XForms doc * @return the complete type name (with namespace prefix) of the type in the XForms doc
*/ */
protected String getXFormsTypeName(final Element context, protected String getXFormsTypeName(final Document xformsDocument,
final XSModel schema, final XSModel schema,
final XSTypeDefinition controlType) final XSTypeDefinition controlType)
{ {
final Element context = xformsDocument.getDocumentElement();
final String typeName = controlType.getName(); final String typeName = controlType.getName();
final String typeNS = controlType.getNamespace(); final String typeNS = controlType.getNamespace();
@@ -3325,7 +3175,7 @@ public class SchemaFormBuilder
// static string (see bug #1172541 on sf.net) // static string (see bug #1172541 on sf.net)
String nodeset = pathToRoot; String nodeset = pathToRoot;
// if (o.maximum > 1 || o.isUnbounded()) // if (o.isRepeated())
// nodeset = pathToRoot + "[position() != last()]"; // nodeset = pathToRoot + "[position() != last()]";
bindElement.setAttributeNS(XFORMS_NS, bindElement.setAttributeNS(XFORMS_NS,
@@ -3334,8 +3184,9 @@ public class SchemaFormBuilder
if (!"anyType".equals(controlType.getName())) if (!"anyType".equals(controlType.getName()))
{ {
Element enveloppe = bindElement.getOwnerDocument().getDocumentElement(); String typeName = this.getXFormsTypeName(bindElement.getOwnerDocument(),
String typeName = this.getXFormsTypeName(enveloppe, schema, controlType); schema,
controlType);
if (typeName != null && typeName.length() != 0) if (typeName != null && typeName.length() != 0)
bindElement.setAttributeNS(XFORMS_NS, bindElement.setAttributeNS(XFORMS_NS,
SchemaFormBuilder.XFORMS_NS_PREFIX + "type", SchemaFormBuilder.XFORMS_NS_PREFIX + "type",
@@ -3447,8 +3298,9 @@ public class SchemaFormBuilder
prefix = basePrefix + (i++); prefix = basePrefix + (i++);
} }
namespacePrefixes.put(namespace, prefix); namespacePrefixes.put(namespace, prefix);
Element envelope = xForm.getDocumentElement(); xForm.getDocumentElement().setAttributeNS(XMLNS_NAMESPACE_URI,
envelope.setAttributeNS(XMLNS_NAMESPACE_URI, "xmlns:" + prefix, namespace); "xmlns:" + prefix,
namespace);
} }
elementName = prefix + ":" + elementName; elementName = prefix + ":" + elementName;
} }
@@ -3506,4 +3358,182 @@ public class SchemaFormBuilder
if (!e.hasAttributeNS(XMLNS_NAMESPACE_URI, p)) if (!e.hasAttributeNS(XMLNS_NAMESPACE_URI, p))
e.setAttributeNS(XMLNS_NAMESPACE_URI, "xmlns:" + p, ns); e.setAttributeNS(XMLNS_NAMESPACE_URI, "xmlns:" + p, ns);
} }
private void createTriggersForRepeats(final Document xformsDocument)
{
LOGGER.debug("creating triggers for repeats");
final HashMap<String, Element> bindIdToBind = new HashMap<String, Element>();
final NodeList binds = xformsDocument.getElementsByTagNameNS(XFORMS_NS, "bind");
for (int i = 0; i < binds.getLength(); i++)
{
final Element b = (Element)binds.item(i);
LOGGER.debug("adding bind " + b.getAttributeNS(XFORMS_NS, "id"));
bindIdToBind.put(b.getAttributeNS(XFORMS_NS, "id"), b);
}
final NodeList repeats = xformsDocument.getElementsByTagNameNS(XFORMS_NS, "repeat");
final HashMap<Element, Element> bindToRepeat = new HashMap<Element, Element>();
for (int i = 0; i < repeats.getLength(); i++)
{
Element r = (Element)repeats.item(i);
LOGGER.debug("processing repeat " + r.getAttributeNS(XFORMS_NS, "id"));
Element bind = bindIdToBind.get(r.getAttributeNS(XFORMS_NS, "bind"));
bindToRepeat.put(bind, r);
String xpath = "";
do
{
if (xpath.length() != 0)
{
xpath = '/' + xpath;
}
LOGGER.debug("walking bind " + bind.getAttributeNS(XFORMS_NS, "id"));
String s = bind.getAttributeNS(XFORMS_NS, "nodeset");
if (bindToRepeat.containsKey(bind) && !r.equals(bindToRepeat.get(bind)))
{
s += "[index(\'" + bindToRepeat.get(bind).getAttributeNS(XFORMS_NS, "id") + "\')]";
}
xpath = s + xpath;
bind = ((XFORMS_NS_PREFIX + "bind").equals(bind.getParentNode().getNodeName())
? (Element)bind.getParentNode()
: null);
}
while (bind != null);
this.createTriggersForRepeat(xformsDocument,
r.getAttributeNS(XFORMS_NS, "id"),
xpath,
r.getAttributeNS(XFORMS_NS, "bind"));
}
}
private Element createTriggerForRepeat(final Document xForm,
final String id,
final String bindId,
final String label,
final Element action)
{
final Element trigger =
xForm.createElementNS(SchemaFormBuilder.XFORMS_NS,
SchemaFormBuilder.XFORMS_NS_PREFIX + "trigger");
this.setXFormsId(trigger, id != null ? id : null);
//copy the bind attribute
trigger.setAttributeNS(SchemaFormBuilder.XFORMS_NS,
SchemaFormBuilder.XFORMS_NS_PREFIX + "bind",
bindId);
//label insert
final Element triggerLabel =
xForm.createElementNS(SchemaFormBuilder.XFORMS_NS,
SchemaFormBuilder.XFORMS_NS_PREFIX + "label");
this.setXFormsId(triggerLabel);
trigger.appendChild(triggerLabel);
triggerLabel.appendChild(xForm.createTextNode(label));
//insert action
final Element actionWrapper =
xForm.createElementNS(SchemaFormBuilder.XFORMS_NS,
SchemaFormBuilder.XFORMS_NS_PREFIX + "action");
actionWrapper.appendChild(action);
trigger.appendChild(action);
this.setXFormsId(action);
return trigger;
}
/**
* add triggers to use the repeat elements (allow to add an element, ...)
*/
private void createTriggersForRepeat(final Document xformsDocument,
final String repeatId,
final String nodeset,
final String bindId)
{
//xforms:at = xforms:index from the "id" attribute on the repeat element
///////////// insert //////////////////
//trigger insert
Element action =
xformsDocument.createElementNS(SchemaFormBuilder.XFORMS_NS,
SchemaFormBuilder.XFORMS_NS_PREFIX + "insert");
action.setAttributeNS(SchemaFormBuilder.XFORMS_NS,
SchemaFormBuilder.XFORMS_NS_PREFIX + "nodeset",
nodeset);
action.setAttributeNS(SchemaFormBuilder.XFORMS_NS,
SchemaFormBuilder.XFORMS_NS_PREFIX + "position",
"before");
action.setAttributeNS(SchemaFormBuilder.XFORMS_NS,
SchemaFormBuilder.XFORMS_NS_PREFIX + "at",
"1");
final Element trigger_insert_before =
this.createTriggerForRepeat(xformsDocument,
repeatId + "-insert_before",
bindId,
"insert at beginning",
action);
action = xformsDocument.createElementNS(SchemaFormBuilder.XFORMS_NS,
SchemaFormBuilder.XFORMS_NS_PREFIX + "insert");
action.setAttributeNS(SchemaFormBuilder.XFORMS_NS,
SchemaFormBuilder.XFORMS_NS_PREFIX + "nodeset",
nodeset);
action.setAttributeNS(SchemaFormBuilder.XFORMS_NS,
SchemaFormBuilder.XFORMS_NS_PREFIX + "position",
"after");
action.setAttributeNS(SchemaFormBuilder.XFORMS_NS,
SchemaFormBuilder.XFORMS_NS_PREFIX + "at",
SchemaFormBuilder.XFORMS_NS_PREFIX + "index('" + repeatId + "')");
final Element trigger_insert_after =
this.createTriggerForRepeat(xformsDocument,
repeatId + "-insert_after",
bindId,
"insert after selected",
action);
//trigger delete
action = xformsDocument.createElementNS(SchemaFormBuilder.XFORMS_NS,
SchemaFormBuilder.XFORMS_NS_PREFIX + "delete");
action.setAttributeNS(SchemaFormBuilder.XFORMS_NS,
SchemaFormBuilder.XFORMS_NS_PREFIX + "nodeset",
nodeset);
action.setAttributeNS(SchemaFormBuilder.XFORMS_NS,
SchemaFormBuilder.XFORMS_NS_PREFIX + "at",
SchemaFormBuilder.XFORMS_NS_PREFIX + "index('" + repeatId + "')");
final Element trigger_delete =
this.createTriggerForRepeat(xformsDocument,
repeatId != null ? repeatId + "-delete" : null,
bindId,
"delete selected",
action);
final Element formSection = (Element)xformsDocument.getDocumentElement().getLastChild();
//add the triggers
final Element wrapper_triggers =
this.wrapper.createControlsWrapper(trigger_insert_before);
if (wrapper_triggers == trigger_insert_before)
{
//no wrapper
formSection.appendChild(trigger_insert_before);
formSection.appendChild(trigger_insert_after);
formSection.appendChild(trigger_delete);
}
else
{
formSection.appendChild(wrapper_triggers);
final Element insert_parent = (Element)trigger_insert_before.getParentNode();
if (insert_parent != null)
{
insert_parent.appendChild(trigger_insert_after);
insert_parent.appendChild(trigger_delete);
}
}
}
} }

View File

@@ -0,0 +1,18 @@
<?xml version="1.0"?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"
elementFormDefault="qualified">
<xs:complexType name="repeat-simple">
<xs:sequence>
<xs:element name="one-to-one" type="xs:string" minOccurs="1" maxOccurs="1"/>
<xs:element name="one-to-inf" type="xs:string" minOccurs="1" maxOccurs="unbounded"/>
</xs:sequence>
</xs:complexType>
<xs:element name="repeat-nested">
<xs:complexType>
<xs:sequence>
<xs:element name="repeat-simple" type="repeat-simple" minOccurs="0" maxOccurs="unbounded"/>
<xs:element name="not-repeated" type="xs:string" minOccurs="1" maxOccurs="1"/>
</xs:sequence>
</xs:complexType>
</xs:element>
</xs:schema>

View File

@@ -47,6 +47,16 @@ dojo.declare("alfresco.xforms.Widget",
this.id = this.xformsNode.getAttribute("id"); this.id = this.xformsNode.getAttribute("id");
this.modified = false; this.modified = false;
this.valid = true; this.valid = true;
var b = this.xform.getBinding(this.xformsNode);
if (b)
{
dojo.debug("adding " + this.id + " to binding " + b.id);
b.widgets[this.id] = this;
}
else
{
dojo.debug("no binding found for " + this.id);
}
}, },
xformsNode: null, xformsNode: null,
labelNode: null, labelNode: null,
@@ -110,7 +120,7 @@ dojo.declare("alfresco.xforms.Widget",
return null; return null;
chibaData = chibaData[chibaData.length - 1]; chibaData = chibaData[chibaData.length - 1];
var xpath = chibaData.getAttribute("chiba:xpath"); var xpath = chibaData.getAttribute(CHIBA_NS_PREFIX + ":xpath");
var d = this.xformsNode.ownerDocument; var d = this.xformsNode.ownerDocument;
var contextNode = this.xform.getInstance(); var contextNode = this.xform.getInstance();
dojo.debug("locating " + xpath + dojo.debug("locating " + xpath +
@@ -130,8 +140,6 @@ dojo.declare("alfresco.xforms.Widget",
var labels = _getElementsByTagNameNS(this.xformsNode, XFORMS_NS, XFORMS_NS_PREFIX, "label"); var labels = _getElementsByTagNameNS(this.xformsNode, XFORMS_NS, XFORMS_NS_PREFIX, "label");
for (var i = 0; i < labels.length; i++) for (var i = 0; i < labels.length; i++)
{ {
dojo.debug("parent " + labels[i].parentNode.nodeName +
" o " + this.xformsNode.nodeName);
if (labels[i].parentNode == this.xformsNode) if (labels[i].parentNode == this.xformsNode)
return labels[i]; return labels[i];
} }
@@ -142,8 +150,6 @@ dojo.declare("alfresco.xforms.Widget",
var labels = _getElementsByTagNameNS(this.xformsNode, XFORMS_NS, XFORMS_NS_PREFIX, "alert"); var labels = _getElementsByTagNameNS(this.xformsNode, XFORMS_NS, XFORMS_NS_PREFIX, "alert");
for (var i = 0; i < labels.length; i++) for (var i = 0; i < labels.length; i++)
{ {
dojo.debug("parent " + labels[i].parentNode.nodeName +
" o " + this.xformsNode.nodeName);
if (labels[i].parentNode == this.xformsNode) if (labels[i].parentNode == this.xformsNode)
return labels[i]; return labels[i];
} }
@@ -152,7 +158,10 @@ dojo.declare("alfresco.xforms.Widget",
getLabel: function() getLabel: function()
{ {
var node = this._getLabelNode(); var node = this._getLabelNode();
return node ? dojo.dom.textContent(node) : ""; var result = node ? dojo.dom.textContent(node) : "";
if (djConfig.isDebug)
result += " [" + this.id + "]";
return result;
}, },
getAlert: function() getAlert: function()
{ {
@@ -186,7 +195,7 @@ dojo.declare("alfresco.xforms.DatePicker",
{ {
initializer: function(xform, xformsNode) initializer: function(xform, xformsNode)
{ {
this.inherited("initializer", [ xform, xformsNode ]); // this.inherited("initializer", [ xform, xformsNode ]);
}, },
render: function(attach_point) render: function(attach_point)
{ {
@@ -248,13 +257,12 @@ dojo.declare("alfresco.xforms.TextField",
{ {
initializer: function(xform, xformsNode) initializer: function(xform, xformsNode)
{ {
this.inherited("initializer", [ xform, xformsNode ]); // this.inherited("initializer", [ xform, xformsNode ]);
}, },
render: function(attach_point) render: function(attach_point)
{ {
var initial_value = this.getInitialValue() || ""; var initial_value = this.getInitialValue() || "";
this.domNode = document.createElement("div"); this.domNode = document.createElement("div");
this.domNode.setAttribute("id", this.id + "-domNode");
attach_point.appendChild(this.domNode); attach_point.appendChild(this.domNode);
this.widget = document.createElement("input"); this.widget = document.createElement("input");
@@ -279,7 +287,7 @@ dojo.declare("alfresco.xforms.TextField",
} }
else else
{ {
dojo.event.connect(this.widget, "onkeyup", this, this._widget_keyUpHandler); dojo.event.connect(this.widget, "onblur", this, this._widget_changeHandler);
} }
}, },
getValue: function() getValue: function()
@@ -289,7 +297,7 @@ dojo.declare("alfresco.xforms.TextField",
result = null; result = null;
return result; return result;
}, },
_widget_keyUpHandler: function(event) _widget_changeHandler: function(event)
{ {
this.xform.setXFormsValue(this.id, this.getValue()); this.xform.setXFormsValue(this.id, this.getValue());
} }
@@ -300,7 +308,7 @@ dojo.declare("alfresco.xforms.TextArea",
{ {
initializer: function(xform, xformsNode) initializer: function(xform, xformsNode)
{ {
this.inherited("initializer", [ xform, xformsNode ]); // this.inherited("initializer", [ xform, xformsNode ]);
}, },
render: function(attach_point) render: function(attach_point)
{ {
@@ -329,7 +337,7 @@ dojo.declare("alfresco.xforms.AbstractSelectWidget",
{ {
initializer: function(xform, xformsNode) initializer: function(xform, xformsNode)
{ {
this.inherited("initializer", [ xform, xformsNode ]); // this.inherited("initializer", [ xform, xformsNode ]);
}, },
getValues: function() getValues: function()
{ {
@@ -366,7 +374,7 @@ dojo.declare("alfresco.xforms.Select",
{ {
initializer: function(xform, xformsNode) initializer: function(xform, xformsNode)
{ {
this.inherited("initializer", [ xform, xformsNode ]); // this.inherited("initializer", [ xform, xformsNode ]);
}, },
render: function(attach_point) render: function(attach_point)
{ {
@@ -418,7 +426,7 @@ dojo.declare("alfresco.xforms.Select",
} }
this.widget.appendChild(option); this.widget.appendChild(option);
} }
dojo.event.connect(this.widget, "onchange", this, this._list_changeHandler); dojo.event.connect(this.widget, "onblur", this, this._list_changeHandler);
} }
}, },
getValue: function() getValue: function()
@@ -453,7 +461,7 @@ dojo.declare("alfresco.xforms.Select1",
{ {
initializer: function(xform, xformsNode) initializer: function(xform, xformsNode)
{ {
this.inherited("initializer", [ xform, xformsNode ]); // this.inherited("initializer", [ xform, xformsNode ]);
}, },
render: function(attach_point) render: function(attach_point)
{ {
@@ -541,7 +549,7 @@ dojo.declare("alfresco.xforms.Checkbox",
{ {
initializer: function(xform, xformsNode) initializer: function(xform, xformsNode)
{ {
this.inherited("initializer", [ xform, xformsNode ]); // this.inherited("initializer", [ xform, xformsNode ]);
}, },
render: function(attach_point) render: function(attach_point)
{ {
@@ -570,7 +578,7 @@ dojo.declare("alfresco.xforms.Group",
{ {
initializer: function(xform, xformsNode) initializer: function(xform, xformsNode)
{ {
this.inherited("initializer", [ xform, xformsNode ]); // this.inherited("initializer", [ xform, xformsNode ]);
this.children = []; this.children = [];
}, },
children: null, children: null,
@@ -645,12 +653,12 @@ dojo.declare("alfresco.xforms.Group",
if (!child.isRequired()) if (!child.isRequired())
requiredImage.style.visibility = "hidden"; requiredImage.style.visibility = "hidden";
var label = child._getLabelNode(); var label = child.getLabel();
if (label) if (label)
{ {
child.labelNode = document.createElement("span"); child.labelNode = document.createElement("span");
child.domContainer.appendChild(child.labelNode); child.domContainer.appendChild(child.labelNode);
child.labelNode.appendChild(document.createTextNode(dojo.dom.textContent(label))); child.labelNode.appendChild(document.createTextNode(label));
} }
} }
var contentDiv = document.createElement("div"); var contentDiv = document.createElement("div");
@@ -712,7 +720,7 @@ dojo.declare("alfresco.xforms.Group",
render: function(attach_point) render: function(attach_point)
{ {
this.domNode = document.createElement("div"); this.domNode = document.createElement("div");
this.domNode.setAttribute("id", this.id + "-domNode"); this.domNode.setAttribute("id", this.id + "-content");
this.domNode.widget = this; this.domNode.widget = this;
attach_point.appendChild(this.domNode); attach_point.appendChild(this.domNode);
@@ -753,7 +761,7 @@ dojo.declare("alfresco.xforms.Repeat",
{ {
initializer: function(xform, xformsNode) initializer: function(xform, xformsNode)
{ {
this.inherited("initializer", [ xform, xformsNode ]); // this.inherited("initializer", [ xform, xformsNode ]);
}, },
_selectedIndex: -1, _selectedIndex: -1,
insertChildAt: function(child, position) insertChildAt: function(child, position)
@@ -810,32 +818,54 @@ dojo.declare("alfresco.xforms.Repeat",
i % 2 ? "#f0f0ee" : "#ffffff"; i % 2 ? "#f0f0ee" : "#ffffff";
} }
}, },
_getRepeatItemTrigger: function(type, properties)
{
var bw = this.xform.getBinding(this.xformsNode).widgets;
for (var i in bw)
{
if (! (bw[i] instanceof alfresco.xforms.Trigger))
continue;
var action = bw[i].getAction();
if (action.getType() != type)
continue;
var propertiesEqual = true;
for (var p in properties)
{
if (!(p in action.properties) ||
action.properties[p] != properties[p])
{
propertiesEqual = false;
break;
}
}
if (propertiesEqual)
return bw[i];
}
throw new Error("unable to find trigger " + type +
", properties " + properties +
" for " + this.id);
},
_insertRepeatItemAfter_handler: function(event) _insertRepeatItemAfter_handler: function(event)
{ {
dojo.event.browser.stopEvent(event); dojo.event.browser.stopEvent(event);
this.setFocusedChild(event.target.repeatItem); this.setFocusedChild(event.target.repeatItem);
if (!this.insertRepeatItemAfterTrigger) var trigger = this._getRepeatItemTrigger("insert", { position: "after" });
this.insertRepeatItemAfterTrigger = this.xform.fireAction(trigger.id);
_findElementById(this.xformsNode.parentNode, this.id + "-insert_after");
this.xform.fireAction(this.insertRepeatItemAfterTrigger.getAttribute("id"));
}, },
_insertRepeatItemBefore_handler: function(event) _insertRepeatItemBefore_handler: function(event)
{ {
dojo.event.browser.stopEvent(event); dojo.event.browser.stopEvent(event);
this.setFocusedChild(event.target.repeatItem); this.setFocusedChild(event.target.repeatItem);
if (!this.insertRepeatItemBeforeTrigger) var trigger = this._getRepeatItemTrigger("insert", { position: "before" });
this.insertRepeatItemBeforeTrigger = this.xform.fireAction(trigger.id);
_findElementById(this.xformsNode.parentNode, this.id + "-insert_before");
this.xform.fireAction(this.insertRepeatItemBeforeTrigger.getAttribute("id"));
}, },
_removeRepeatItem_handler: function(event) _removeRepeatItem_handler: function(event)
{ {
dojo.event.browser.stopEvent(event); dojo.event.browser.stopEvent(event);
this.setFocusedChild(event.target.repeatItem); this.setFocusedChild(event.target.repeatItem);
if (!this.removeRepeatItemTrigger) var trigger = this._getRepeatItemTrigger("delete", {});
this.removeRepeatItemTrigger = _findElementById(this.xformsNode.parentNode, this.xform.fireAction(trigger.id);
this.id + "-delete");
this.xform.fireAction(this.removeRepeatItemTrigger.getAttribute("id"));
}, },
_moveRepeatItemUp_handler: function(event) _moveRepeatItemUp_handler: function(event)
{ {
@@ -980,7 +1010,7 @@ dojo.declare("alfresco.xforms.Repeat",
dojo.debug(this.id + ".handlePrototypeCloned("+ prototypeId +")"); dojo.debug(this.id + ".handlePrototypeCloned("+ prototypeId +")");
var chibaData = _getElementsByTagNameNS(this.xformsNode, CHIBA_NS, CHIBA_NS_PREFIX, "data"); var chibaData = _getElementsByTagNameNS(this.xformsNode, CHIBA_NS, CHIBA_NS_PREFIX, "data");
chibaData = chibaData[chibaData.length - 1]; chibaData = chibaData[chibaData.length - 1];
dojo.debug("chiba:data == " + dojo.dom.innerXML(chibaData)); dojo.debug(CHIBA_NS_PREFIX + ":data == " + dojo.dom.innerXML(chibaData));
var prototypeToClone = dojo.dom.firstElement(chibaData); var prototypeToClone = dojo.dom.firstElement(chibaData);
if (prototypeToClone.getAttribute("id") != prototypeId) if (prototypeToClone.getAttribute("id") != prototypeId)
throw new Error("unable to locate " + prototypeId + throw new Error("unable to locate " + prototypeId +
@@ -1007,7 +1037,7 @@ dojo.declare("alfresco.xforms.Trigger",
{ {
initializer: function(xform, xformsNode) initializer: function(xform, xformsNode)
{ {
this.inherited("initializer", [ xform, xformsNode ]); // this.inherited("initializer", [ xform, xformsNode ]);
}, },
render: function(attach_point) render: function(attach_point)
{ {
@@ -1022,6 +1052,20 @@ dojo.declare("alfresco.xforms.Trigger",
dojo.event.connect(this.widget, "onClick", this, this._clickHandler); dojo.event.connect(this.widget, "onClick", this, this._clickHandler);
this.domContainer.style.display = "none"; this.domContainer.style.display = "none";
}, },
getAction: function()
{
for (var i = 0; i < this.xformsNode.childNodes.length; i++)
{
var c = this.xformsNode.childNodes[i];
if (c.nodeType != dojo.dom.ELEMENT_NODE)
continue;
if (c.nodeName == XFORMS_NS_PREFIX + ":label" ||
c.nodeName == XFORMS_NS_PREFIX + ":alert")
continue;
return new alfresco.xforms.XFormsAction(this.xform, c);
}
throw new Error("unable to find action node for " + this.id);
},
_clickHandler: function(event) _clickHandler: function(event)
{ {
this.xform.fireAction(this.id); this.xform.fireAction(this.id);
@@ -1033,7 +1077,7 @@ dojo.declare("alfresco.xforms.Submit",
{ {
initializer: function(xform, xformsNode) initializer: function(xform, xformsNode)
{ {
this.inherited("initializer", [ xform, xformsNode ]); // this.inherited("initializer", [ xform, xformsNode ]);
var submit_buttons = _xforms_getSubmitButtons(); var submit_buttons = _xforms_getSubmitButtons();
for (var i = 0; i < submit_buttons.length; i++) for (var i = 0; i < submit_buttons.length; i++)
{ {
@@ -1074,6 +1118,30 @@ dojo.declare("alfresco.xforms.Submit",
} }
}); });
dojo.declare("alfresco.xforms.XFormsAction",
null,
{
initializer: function(xform, xformsNode)
{
this.xform = xform;
this.xformsNode = xformsNode;
this.properties = [];
for (var i = 0; i < this.xformsNode.attributes.length; i++)
{
var attr = this.xformsNode.attributes[i];
if (attr.nodeName.match(/^xforms:/))
{
this.properties[attr.nodeName.substring((XFORMS_NS_PREFIX + ":").length)] =
attr.nodeValue;
}
}
},
getType: function()
{
return this.xformsNode.nodeName.substring((XFORMS_NS_PREFIX + ":").length);
},
});
dojo.declare("alfresco.xforms.XFormsEvent", dojo.declare("alfresco.xforms.XFormsEvent",
null, null,
{ {
@@ -1093,7 +1161,7 @@ dojo.declare("alfresco.xforms.XFormsEvent",
getTarget: function() getTarget: function()
{ {
var targetDomNode = document.getElementById(this.targetId + "-content"); var targetDomNode = document.getElementById(this.targetId + "-content");
if (!targetDomNode) if (!targetDomNode)
throw new Error("unable to find node " + this.targetId + "-content"); throw new Error("unable to find node " + this.targetId + "-content");
return targetDomNode.widget; return targetDomNode.widget;
} }
@@ -1138,13 +1206,13 @@ dojo.declare("alfresco.xforms.XForm",
dojo.debug("creating node for " + node.nodeName.toLowerCase()); dojo.debug("creating node for " + node.nodeName.toLowerCase());
switch (node.nodeName.toLowerCase()) switch (node.nodeName.toLowerCase())
{ {
case "xforms:group": case XFORMS_NS_PREFIX + ":group":
return new alfresco.xforms.Group(this, node); return new alfresco.xforms.Group(this, node);
case "xforms:repeat": case XFORMS_NS_PREFIX + ":repeat":
return new alfresco.xforms.Repeat(this, node); return new alfresco.xforms.Repeat(this, node);
case "xforms:textarea": case XFORMS_NS_PREFIX + ":textarea":
return new alfresco.xforms.TextArea(this, node); return new alfresco.xforms.TextArea(this, node);
case "xforms:input": case XFORMS_NS_PREFIX + ":input":
var type = this.getType(node); var type = this.getType(node);
switch (type) switch (type)
{ {
@@ -1169,19 +1237,20 @@ dojo.declare("alfresco.xforms.XForm",
default: default:
return new alfresco.xforms.TextField(this, node); return new alfresco.xforms.TextField(this, node);
} }
case "xforms:select": case XFORMS_NS_PREFIX + ":select":
return new alfresco.xforms.Select(this, node); return new alfresco.xforms.Select(this, node);
case "xforms:select1": case XFORMS_NS_PREFIX + ":select1":
return (this.getType(node) == "boolean" return (this.getType(node) == "boolean"
? new alfresco.xforms.Checkbox(this, node) ? new alfresco.xforms.Checkbox(this, node)
: new alfresco.xforms.Select1(this, node)); : new alfresco.xforms.Select1(this, node));
case "xforms:submit": case XFORMS_NS_PREFIX + ":submit":
return new alfresco.xforms.Submit(this, node); return new alfresco.xforms.Submit(this, node);
case "xforms:trigger": case XFORMS_NS_PREFIX + ":trigger":
return new alfresco.xforms.Trigger(this, node); return new alfresco.xforms.Trigger(this, node);
case "chiba:data": case CHIBA_NS_PREFIX + ":data":
case "xforms:label": case XFORMS_NS_PREFIX + ":label":
case "xforms:alert": case XFORMS_NS_PREFIX + ":alert":
dojo.debug("ignoring " + node.nodeName);
return null; return null;
default: default:
throw new Error("unknown type " + node.nodeName); throw new Error("unknown type " + node.nodeName);
@@ -1193,8 +1262,7 @@ dojo.declare("alfresco.xforms.XForm",
{ {
if (xformsNode.childNodes[i].nodeType == dojo.dom.ELEMENT_NODE) if (xformsNode.childNodes[i].nodeType == dojo.dom.ELEMENT_NODE)
{ {
dojo.debug("loading " + xformsNode.childNodes[i] + dojo.debug("loading " + xformsNode.childNodes[i].nodeName +
" nodeName " + xformsNode.childNodes[i].nodeName +
" into " + parentWidget.id); " into " + parentWidget.id);
var w = this.createWidget(xformsNode.childNodes[i]); var w = this.createWidget(xformsNode.childNodes[i]);
if (w != null) if (w != null)
@@ -1236,7 +1304,7 @@ dojo.declare("alfresco.xforms.XForm",
}, },
getBinding: function(node) getBinding: function(node)
{ {
return this._bindings[node.getAttribute("xforms:bind")]; return this._bindings[node.getAttribute(XFORMS_NS_PREFIX + ":bind")];
}, },
getBindings: function() getBindings: function()
{ {
@@ -1248,19 +1316,20 @@ dojo.declare("alfresco.xforms.XForm",
dojo.debug("loading bindings for " + bind.nodeName); dojo.debug("loading bindings for " + bind.nodeName);
for (var i = 0; i < bind.childNodes.length; i++) for (var i = 0; i < bind.childNodes.length; i++)
{ {
if (bind.childNodes[i].nodeName.toLowerCase() == "xforms:bind") if (bind.childNodes[i].nodeName.toLowerCase() == XFORMS_NS_PREFIX + ":bind")
{ {
var id = bind.childNodes[i].getAttribute("id"); var id = bind.childNodes[i].getAttribute("id");
dojo.debug("loading binding " + id); dojo.debug("loading binding " + id);
result[id] = result[id] =
{ {
id: bind.childNodes[i].getAttribute("id"), id: bind.childNodes[i].getAttribute("id"),
readonly: bind.childNodes[i].getAttribute("xforms:readonly"), readonly: bind.childNodes[i].getAttribute(XFORMS_NS_PREFIX + ":readonly"),
required: bind.childNodes[i].getAttribute("xforms:required"), required: bind.childNodes[i].getAttribute(XFORMS_NS_PREFIX + ":required"),
nodeset: bind.childNodes[i].getAttribute("xforms:nodeset"), nodeset: bind.childNodes[i].getAttribute(XFORMS_NS_PREFIX + ":nodeset"),
type: bind.childNodes[i].getAttribute("xforms:type"), type: bind.childNodes[i].getAttribute(XFORMS_NS_PREFIX + ":type"),
constraint: bind.childNodes[i].getAttribute("xforms:constraint"), constraint: bind.childNodes[i].getAttribute(XFORMS_NS_PREFIX + ":constraint"),
parent: parent parent: parent,
widgets: {}
}; };
this._loadBindings(bind.childNodes[i], result[id], result); this._loadBindings(bind.childNodes[i], result[id], result);
} }
@@ -1271,24 +1340,24 @@ dojo.declare("alfresco.xforms.XForm",
{ {
dojo.debug("setting repeat index " + index + " on " + id); dojo.debug("setting repeat index " + index + " on " + id);
var req = create_ajax_request(this, var req = create_ajax_request(this,
"setRepeatIndex", "setRepeatIndex",
{ id: id, index: index }, { id: id, index: index },
function(type, data, evt) function(type, data, evt)
{ {
this.xform._handleEventLog(data.documentElement); this.xform._handleEventLog(data.documentElement);
}); });
send_ajax_request(req); send_ajax_request(req);
}, },
fireAction: function(id) fireAction: function(id)
{ {
var req = create_ajax_request(this, var req = create_ajax_request(this,
"fireAction", "fireAction",
{ id: id }, { id: id },
function(type, data, evt) function(type, data, evt)
{ {
dojo.debug("fireAction." + type); dojo.debug("fireAction." + type);
this.xform._handleEventLog(data.documentElement); this.xform._handleEventLog(data.documentElement);
}); });
send_ajax_request(req); send_ajax_request(req);
}, },
setXFormsValue: function(id, value) setXFormsValue: function(id, value)
@@ -1319,7 +1388,14 @@ dojo.declare("alfresco.xforms.XForm",
case "chiba-index-changed": case "chiba-index-changed":
{ {
var index = Number(xfe.properties["index"]) - 1; var index = Number(xfe.properties["index"]) - 1;
xfe.getTarget().handleIndexChanged(index); try
{
xfe.getTarget().handleIndexChanged(index);
}
catch (e)
{
dojo.debug(e);
}
break; break;
} }
case "chiba-state-changed": case "chiba-state-changed":
@@ -1332,7 +1408,39 @@ dojo.declare("alfresco.xforms.XForm",
case "chiba-prototype-cloned": case "chiba-prototype-cloned":
{ {
var prototypeId = xfe.properties["prototypeId"]; var prototypeId = xfe.properties["prototypeId"];
var clone = xfe.getTarget().handlePrototypeCloned(prototypeId); var originalId = xfe.properties["originalId"];
dojo.debug("handlePrototypeCloned(" + xfe.targetId +
", " + originalId +
", " + prototypeId + ")");
var prototypeNode = _findElementById(this.xformsNode, originalId);
var binding = this.getBinding(prototypeNode);
var prototypeToClone = null;
for (var w in binding.widgets)
{
if (binding.widgets[w] instanceof alfresco.xforms.Repeat)
{
var chibaData = _getElementsByTagNameNS(binding.widgets[w].xformsNode,
CHIBA_NS,
CHIBA_NS_PREFIX, "data");
if (chibaData.length == 0)
continue;
prototypeToClone = dojo.dom.firstElement(chibaData[chibaData.length - 1]);
}
}
if (!prototypeToClone)
throw new Error("unable to find prototype for " + originalId);
dojo.debug("cloning prototype " + prototypeToClone.getAttribute("id"));
var clone = prototypeToClone.cloneNode(true);
clone.setAttribute("id", prototypeId);
// if (true || originalId == xfe.targetId)
// var clone = xfe.getTarget().handlePrototypeCloned(prototypeId);
// else
// {
// var parentClone = prototypeClones[prototypeClones.length - 1];
// var clone = originalWidget.widget.handlePrototypeCloned(prototypeId);
// }
prototypeClones.push(clone); prototypeClones.push(clone);
break; break;
} }
@@ -1357,9 +1465,16 @@ dojo.declare("alfresco.xforms.XForm",
case "chiba-item-inserted": case "chiba-item-inserted":
{ {
var position = Number(xfe.properties["position"]) - 1; var position = Number(xfe.properties["position"]) - 1;
var originalId = xfe.properties["originalId"];
var clone = prototypeClones.pop(); var clone = prototypeClones.pop();
xfe.getTarget().handleItemInserted(clone, position); if (prototypeClones.length == 0)
xfe.getTarget().handleItemInserted(clone, position);
else
{
var parentClone = prototypeClones[prototypeClones.length - 1];
var parentRepeat = _findElementById(parentClone, xfe.targetId);
parentRepeat.appendChild(clone);
}
break; break;
} }
case "chiba-item-deleted": case "chiba-item-deleted":