Files
alfresco-community-repo/source/web/scripts/ajax/xforms.js
Derek Hulley 52a3fa1ed1 Merged V2.0 to HEAD
5146: AR-1122
   5148: AR-1116
   5149: RM-5
   5152: AR-1167
   5153: WCM-324
   5154: WCM-325, WCM-301, WCM-258, WCM-25, WCM-320
   5156: WCM-338
   5158: AR-1164
   5169: AR-1216
   5177: WCM-328


git-svn-id: https://svn.alfresco.com/repos/alfresco-enterprise/alfresco/HEAD/root@5327 c4b6b30b-aa2e-2d43-bbcb-ca4b014f7261
2007-03-07 12:01:58 +00:00

5058 lines
207 KiB
JavaScript

/*
* Copyright (C) 2005-2007 Alfresco Software Limited.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
////////////////////////////////////////////////////////////////////////////////
// XForms user interface
//
// This script communicates with the XFormBean to produce and manage an xform.
//
// This script requires dojo.js, tiny_mce.js, and upload_helper.js to be
// loaded in advance.
////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////
// initialization
//
// Initiliaze dojo requirements, tinymce, and add a hook to load the xform.
////////////////////////////////////////////////////////////////////////////////
djConfig.bindEncoding = "UTF-8";
dojo.require("dojo.date.common");
dojo.require("dojo.debug.console");
dojo.require("dojo.lang.assert");
dojo.require("dojo.lfx.html");
dojo.require("dojo.widget.Button");
dojo.require("dojo.widget.DatePicker");
dojo.require("dojo.widget.DebugConsole");
dojo.require("dojo.widget.Slider");
dojo.require("dojo.widget.TimePicker");
dojo.hostenv.writeIncludes();
function _xforms_init()
{
document.xform = new alfresco.xforms.XForm();
}
dojo.addOnLoad(_xforms_init);
tinyMCE.init({
theme: "advanced",
mode: "exact",
width: -1,
auto_resize: false,
force_p_newlines: false,
encoding: "UTF-8",
add_unload_trigger: false,
add_form_submit_trigger: false,
theme_advanced_toolbar_location: "top",
theme_advanced_toolbar_align: "left",
theme_advanced_buttons1: "bold,italic,underline,strikethrough,separator,fontselect,fontsizeselect",
theme_advanced_buttons2: "link,unlink,image,separator,justifyleft,justifycenter,justifyright,justifyfull,separator,bullist,numlist,separator,undo,redo,separator,forecolor,backcolor",
theme_advanced_buttons3: ""
});
////////////////////////////////////////////////////////////////////////////////
// constants
//
// These are the client side declared constants. Others relating to namespaces
// and the webapp context path are expected to be provided by the jsp including
// this script.
////////////////////////////////////////////////////////////////////////////////
alfresco_xforms_constants.XFORMS_ERROR_DIV_ID = "alfresco-xforms-error";
alfresco_xforms_constants.AJAX_LOADER_DIV_ID = "alfresco-ajax-loader";
alfresco_xforms_constants.EXPANDED_IMAGE = new Image();
alfresco_xforms_constants.EXPANDED_IMAGE.src =
alfresco_xforms_constants.WEBAPP_CONTEXT + "/images/icons/expanded.gif";
alfresco_xforms_constants.COLLAPSED_IMAGE = new Image();
alfresco_xforms_constants.COLLAPSED_IMAGE.src =
alfresco_xforms_constants.WEBAPP_CONTEXT + "/images/icons/collapsed.gif";
////////////////////////////////////////////////////////////////////////////////
// widgets
////////////////////////////////////////////////////////////////////////////////
/**
* Base class for all xforms widgets. Each widget has a set of common properties,
* particularly a corresponding xforms node, a node within the browser DOM,
* a parent widget, and state variables.
*/
dojo.declare("alfresco.xforms.Widget",
null,
function(xform, xformsNode)
{
this.xform = xform;
this.xformsNode = xformsNode;
this.id = this.xformsNode.getAttribute("id");
this._modified = false;
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);
}
this.domNode = document.createElement("div");
this.domNode.setAttribute("id", this.id + "-domNode");
this.domNode.widget = this;
dojo.html.setClass(this.domNode, "xformsItem");
},
{
/////////////////////////////////////////////////////////////////
// properties
/////////////////////////////////////////////////////////////////
/** A reference to the xform. */
xform: null,
/** The xformsNode managed by this widget. */
xformsNode: null,
/** The dom node containing the label for this widget. */
labelNode: null,
/** The parent widget, or null if this is the root widget. */
parent: null,
/** The dom node for this widget. */
domNode: null,
/** The dom node containing this widget. */
domContainer: null,
/** The parent widget which is using this as a composite. */
_compositeParent: null,
/////////////////////////////////////////////////////////////////
// methods
/////////////////////////////////////////////////////////////////
/** Sets the widget's modified state, as indicated by an XFormsEvent. */
setModified: function(b)
{
this._modified = b;
this._updateDisplay();
if (this.isValidForSubmit())
{
this.hideAlert();
}
},
/** Sets the widget's valid state, as indicated by an XFormsEvent */
setValid: function(b)
{
this._valid = b;
this._updateDisplay();
if (this.isValidForSubmit())
{
this.hideAlert();
}
else
{
this.showAlert();
}
},
/**
* Heuristic approach to determine if the widget is valid for submit or
* if it's causing an xforms-error.
*/
isValidForSubmit: function()
{
if (typeof this._valid != "undefined" && !this._valid)
{
dojo.debug(this.id + " is invalid");
return false;
}
if (!this._modified &&
this.isRequired() &&
this.getInitialValue() == null)
{
dojo.debug(this.id + " is unmodified and required and empty");
return false;
}
if (this.isRequired() && this.getValue() == null)
{
dojo.debug(this.id + " is required and empty");
return false;
}
dojo.debug(this.id + " is valid: {" +
"modified: " + this._modified +
", required: " + this.isRequired() +
", initial_value: " + this.getInitialValue() +
", value: " + this.getValue() + "}");
return true;
},
/** Returns the depth of the widget within the widget heirarchy. */
getDepth: function()
{
var result = 1;
var p = this.parent;
while (p)
{
result++;
p = p.parent;
}
return result;
},
/** Returns the root group element */
getViewRoot: function()
{
var p = this;
while (p.parent)
{
p = p.parent;
}
if (! (p instanceof alfresco.xforms.ViewRoot))
{
throw new Error("expected root widget " + p + " to be a view root");
}
return p;
},
/** Returns true if the parent is an ancestor of the given parent */
isAncestorOf: function(parent)
{
var p = this;
while (p.parent)
{
if (p.parent == parent)
{
return true;
}
p = p.parent;
}
return false;
},
/** Sets the widget's enabled state, as indicated by an XFormsEvent */
setEnabled: function(enabled)
{
},
/** Returns the widget's enabled state */
isEnabled: function()
{
return true;
},
/** Sets the widget's required state, as indicated by an XFormsEvent */
setRequired: function(b)
{
this._required = b;
this._updateDisplay();
},
/** Indicates if a value is required for the widget. */
isRequired: function()
{
if (typeof this._required != "undefined")
{
return this._required;
}
var binding = this.xform.getBinding(this.xformsNode);
return binding && binding.isRequired();
},
/** Sets the widget's readonly state, as indicated by an XFormsEvent */
setReadonly: function(readonly)
{
this._readonly = readonly;
},
/** Indicates if the widget's value is readonly. */
isReadonly: function()
{
if (typeof this._readonly != "undefined")
{
return this._readonly;
}
var binding = this.xform.getBinding(this.xformsNode);
return binding && binding.isReadonly();
},
isVisible: function()
{
return true;
},
/** Commits the changed value to the server */
_commitValueChange: function()
{
if (this._compositeParent)
{
this._compositeParent._commitValueChange();
}
else
{
this.xform.setXFormsValue(this.id, this.getValue());
}
},
/** Sets the value contained by the widget */
setValue: function(value, forceCommit)
{
if (forceCommit)
{
this.xform.setXFormsValue(this.id, value);
}
},
/** Returns the value contained by the widget, or null if none is set */
getValue: function()
{
return null;
},
/** Sets the widget's initial value. */
setInitialValue: function(value, forceCommit)
{
this._initialValue =
(typeof value == "string" && value.length == 0 ? null : value);
if (forceCommit)
{
this.xform.setXFormsValue(this.id, value);
}
},
/**
* Returns the widget's local value, either with a local variable, or by
* looking it up within the model section.
*/
getInitialValue: function()
{
if (typeof this._initialValue != "undefined")
{
return this._initialValue;
}
var xpath = this._getXPathInInstanceDocument();
var d = this.xformsNode.ownerDocument;
var contextNode = this.xform.getInstance();
dojo.debug("locating " + xpath + " in " + contextNode.nodeName);
this._initialValue = _evaluateXPath("/" + xpath,
this.xform.getInstance(),
XPathResult.FIRST_ORDERED_NODE_TYPE);
if (!this._initialValue)
{
dojo.debug("unable to resolve xpath /" + xpath + " for " + this.id);
this._initialValue = null;
}
else
{
this._initialValue = (this._initialValue.nodeType == dojo.dom.ELEMENT_NODE
? dojo.dom.textContent(this._initialValue)
: this._initialValue.nodeValue);
if (typeof this._initialValue == "string" && this._initialValue.length == 0)
{
this._initialValue = null;
}
dojo.debug("resolved xpath " + xpath + " to " + this._initialValue);
}
return this._initialValue;
},
/** Produces an xpath to the model node within the instance data document. */
_getXPathInInstanceDocument: function()
{
var binding = this.xform.getBinding(this.xformsNode);
var xpath = '';
var repeatIndices = this.getRepeatIndices();
do
{
var s = binding.nodeset;
if (binding.nodeset == '.')
{
binding = binding.parent;
}
if (binding.nodeset.match(/.+\[.+\]/))
{
s = binding.nodeset.replace(/([^\[]+)\[.*/, "$1");
s += '[' + (repeatIndices.shift().index) + ']';
}
xpath = s + (xpath.length != 0 ? '/' + xpath : "");
binding = binding.parent;
}
while (binding);
return xpath;
},
/** Returns the label node for this widget from the xforms document. */
_getLabelNode: function()
{
return this._getChildXFormsNode("label");
},
/** Returns the alert node for this widget from the xforms document. */
_getAlertNode: function()
{
return this._getChildXFormsNode("alert");
},
/** Returns a child node by name within the xform. */
_getChildXFormsNode: function(nodeName)
{
var x = _getElementsByTagNameNS(this.xformsNode,
alfresco_xforms_constants.XFORMS_NS,
alfresco_xforms_constants.XFORMS_PREFIX,
nodeName);
for (var i = 0; i < x.length; i++)
{
if (x[i].parentNode == this.xformsNode)
{
return x[i];
}
}
return null;
},
/** Returns the widget's label. */
getLabel: function()
{
var node = this._getLabelNode();
var result = node ? dojo.dom.textContent(node) : "";
if (djConfig.isDebug)
{
result += " [" + this.id + "]";
}
return result;
},
/** Returns the widget's alert text. */
getAlert: function()
{
var node = this._getAlertNode();
return node ? dojo.dom.textContent(node) : "";
},
/** Makes the label red. */
showAlert: function()
{
if (!dojo.html.hasClass(this.labelNode, "xformsItemLabelSubmitError"))
{
dojo.html.addClass(this.labelNode, "xformsItemLabelSubmitError");
}
},
/** Restores the label to its original color. */
hideAlert: function()
{
if (dojo.html.hasClass(this.labelNode, "xformsItemLabelSubmitError"))
{
dojo.html.removeClass(this.labelNode, "xformsItemLabelSubmitError");
}
},
/** Returns the value of the appearance attribute for widget */
getAppearance: function()
{
var result = (this.xformsNode.getAttribute("appearance") ||
this.xformsNode.getAttribute(alfresco_xforms_constants.XFORMS_PREFIX + ":appearance"));
return result == null || result.length == 0 ? null : result;
},
/** Updates the display of the widget. This is intended to be overridden. */
_updateDisplay: function()
{
// this.domContainer.style.backgroundColor =
// (!this._valid ? "yellow" : this._modified ? "lightgreen" : "white");
},
/** Destroy the widget and any resources no longer needed. */
_destroy: function()
{
dojo.debug("destroying " + this.id);
},
/**
* Returns an array of RepeatIndexDatas corresponding to all enclosing repeats.
* The closest repeat will be at index 0.
*/
getRepeatIndices: function()
{
var result = [];
var w = this;
while (w.parent)
{
if (w.parent instanceof alfresco.xforms.Repeat)
{
result.push(new alfresco.xforms.RepeatIndexData(w.parent,
w.parent.getChildIndex(w) + 1));
}
w = w.parent;
}
return result;
},
/**
*/
getParentGroups: function(appearance)
{
var result = [];
var w = this;
while (w.parent)
{
if (w.parent instanceof alfresco.xforms.Group)
{
if (appearance && w.parent.getAppearance() == appearance)
{
result.push(w.parent);
}
}
w = w.parent;
}
return result;
}
});
////////////////////////////////////////////////////////////////////////////////
// widgets for atomic types
////////////////////////////////////////////////////////////////////////////////
/** The file picker widget which handles xforms widget xf:upload. */
dojo.declare("alfresco.xforms.FilePicker",
alfresco.xforms.Widget,
function(xform, xformsNode)
{
},
{
/////////////////////////////////////////////////////////////////
// overridden methods
/////////////////////////////////////////////////////////////////
render: function(attach_point)
{
dojo.html.prependClass(this.domNode, "xformsFilePicker");
attach_point.appendChild(this.domNode);
//XXXarielb support readonly and disabled
this.widget = new FilePickerWidget(this.id,
this.domNode,
this.getInitialValue(),
false,
this._filePicker_changeHandler,
this._filePicker_resizeHandler);
this.widget.render();
},
getValue: function()
{
return this.widget.getValue();
},
setValue: function(value, forceCommit)
{
if (!this.widget)
{
this.setInitialValue(value, forceCommit);
}
else
{
alfresco.xforms.FilePicker.superclass.setValue.call(this, value, forceCommit);
this.widget.setValue(value);
}
},
/////////////////////////////////////////////////////////////////
// DOM event handlers
/////////////////////////////////////////////////////////////////
_filePicker_changeHandler: function(fpw)
{
fpw.node.widget._commitValueChange();
},
_filePicker_resizeHandler: function(fpw)
{
var w = fpw.node.widget;
w.domContainer.style.height =
Math.max(fpw.node.offsetHeight +
dojo.html.getMargin(w.domNode.parentNode).height,
20) + "px";
}
});
/** The textfield widget which handle xforms widget xf:input with any string or numerical type */
dojo.declare("alfresco.xforms.TextField",
alfresco.xforms.Widget,
function(xform, xformsNode)
{
},
{
/////////////////////////////////////////////////////////////////
// overridden methods
/////////////////////////////////////////////////////////////////
render: function(attach_point)
{
var initial_value = this.getInitialValue() || "";
attach_point.appendChild(this.domNode);
this.widget = document.createElement("input");
this.widget.setAttribute("type", "text");
this.widget.setAttribute("id", this.id + "-widget");
this.widget.setAttribute("value", initial_value);
if (this.getAppearance() == "full")
{
this.widget.style.width = "100%";
}
this.domNode.appendChild(this.widget);
if (this.isReadonly())
{
this.widget.setAttribute("readonly", this.isReadonly());
this.widget.setAttribute("disabled", this.isReadonly());
}
else
{
dojo.event.connect(this.widget, "onblur", this, this._widget_changeHandler);
}
},
setValue: function(value, forceCommit)
{
if (!this.widget)
{
this.setInitialValue(value, forceCommit);
}
else
{
alfresco.xforms.TextField.superclass.setValue.call(this, value, forceCommit);
this.widget.value = value;
}
},
getValue: function()
{
return (this.widget.value != null && this.widget.value.length == 0
? null
: this.widget.value);
},
/////////////////////////////////////////////////////////////////
// DOM event handlers
/////////////////////////////////////////////////////////////////
_widget_changeHandler: function(event)
{
this._commitValueChange();
}
});
/** The number range widget which handle xforms widget xf:range with any numerical type */
dojo.declare("alfresco.xforms.NumericalRange",
alfresco.xforms.Widget,
function(xform, xformsNode)
{
this.fractionDigits = (_hasAttribute(this.xformsNode, alfresco_xforms_constants.ALFRESCO_PREFIX + ":fractionDigits")
? Number(this.xformsNode.getAttribute(alfresco_xforms_constants.ALFRESCO_PREFIX + ":fractionDigits"))
: -1);
},
{
/////////////////////////////////////////////////////////////////
// overridden methods
/////////////////////////////////////////////////////////////////
render: function(attach_point)
{
var initial_value = this.getInitialValue() || "";
attach_point.appendChild(this.domNode);
var sliderDiv = document.createElement("div");
sliderDiv.style.fontWeight = "bold";
sliderDiv.style.marginBottom = "5px";
this.domNode.appendChild(sliderDiv);
var minimum = Number(this.xformsNode.getAttribute(alfresco_xforms_constants.XFORMS_PREFIX + ":start"));
var maximum = Number(this.xformsNode.getAttribute(alfresco_xforms_constants.XFORMS_PREFIX + ":end"));
var snapValues = 0;
if (this.fractionDigits == 0)
{
snapValues = maximum - minimum + 1;
}
sliderDiv.appendChild(document.createTextNode(minimum));
var sliderWidgetDiv = document.createElement("div");
sliderDiv.appendChild(sliderWidgetDiv);
this.widget = dojo.widget.createWidget("SliderHorizontal",
{
initialValue: initial_value,
minimumX: minimum,
maximumX: maximum,
showButtons: false,
activeDrag: false,
snapValues: snapValues
},
sliderWidgetDiv);
sliderDiv.appendChild(document.createTextNode(maximum));
this.currentValueDiv = document.createElement("div");
this.domNode.appendChild(this.currentValueDiv);
this.currentValueDiv.appendChild(document.createTextNode("Value: " + initial_value));
dojo.event.connect(this.widget,
"onValueChanged",
this,
this._hSlider_valueChangedHandler);
},
setValue: function(value, forceCommit)
{
if (!this.widget)
{
this.setInitialValue(value, forceCommit);
}
else
{
alfresco.xforms.NumericalRange.superclass.setValue.call(this, value, forceCommit);
this.widget.setValue(value);
}
},
getValue: function()
{
return this.widget.getValue();
},
/////////////////////////////////////////////////////////////////
// DOM event handlers
/////////////////////////////////////////////////////////////////
_hSlider_valueChangedHandler: function(value)
{
if (this.fractionDigits >= 0)
{
value = Math.round(value * Math.pow(10, this.fractionDigits)) / Math.pow(10, this.fractionDigits);
}
this.currentValueDiv.replaceChild(document.createTextNode("Value: " + value),
this.currentValueDiv.firstChild);
if (!this.widget._isDragInProgress)
{
this._commitValueChange();
}
}
});
/** The text area widget handles xforms widget xf:textarea with appearance minimal */
dojo.declare("alfresco.xforms.PlainTextEditor",
alfresco.xforms.Widget,
function(xform, xformsNode)
{
},
{
/////////////////////////////////////////////////////////////////
// overridden methods
/////////////////////////////////////////////////////////////////
render: function(attach_point)
{
attach_point.appendChild(this.domNode);
dojo.html.prependClass(this.domNode, "xformsTextArea");
var initialValue = this.getInitialValue() || "";
this.widget = document.createElement("textarea");
this.domNode.appendChild(this.widget);
this.widget.setAttribute("id", this.id + "-widget");
this.widget.setAttribute("value", initialValue);
if (this.isReadonly())
{
this.widget.setAttribute("readonly", this.isReadonly());
}
this.widget.style.width = "100%";
this.widget.style.height = "100%";
dojo.event.connect(this.widget, "onchange", this, this._textarea_changeHandler);
},
setValue: function(value, forceCommit)
{
if (!this.widget)
{
this.setInitialValue(value, forceCommit);
}
else
{
alfresco.xform.PlainTextEditor.superclass.setValue.call(this, value, forceCommit);
this.widget.value = value;
}
},
getValue: function()
{
return this.widget.value;
},
/////////////////////////////////////////////////////////////////
// DOM event handlers
/////////////////////////////////////////////////////////////////
_textarea_changeHandler: function(event)
{
this._commitValueChange();
}
});
/** The textfield widget which handle xforms widget xf:textarea. with appearance full or compact */
dojo.declare("alfresco.xforms.RichTextEditor",
alfresco.xforms.Widget,
function(xform, xformsNode)
{
this.focused = false;
},
{
/////////////////////////////////////////////////////////////////
// methods
/////////////////////////////////////////////////////////////////
_removeTinyMCE: function()
{
var value = tinyMCE.getContent(this.id);
tinyMCE.removeMCEControl(this.id);
},
_createTinyMCE:function()
{
tinyMCE.addMCEControl(this.widget, this.id);
var editorDocument = tinyMCE.getInstanceById(this.id).getDoc();
editorDocument.widget = this;
tinyMCE.addEvent(editorDocument,
dojo.render.html.ie ? "beforedeactivate" : "blur",
this._tinyMCE_blurHandler);
tinyMCE.addEvent(editorDocument, "focus", this._tinyMCE_focusHandler);
},
/////////////////////////////////////////////////////////////////
// overridden methods
/////////////////////////////////////////////////////////////////
render: function(attach_point)
{
attach_point.appendChild(this.domNode);
dojo.html.prependClass(this.domNode, "xformsTextArea");
this.widget = document.createElement("div");
this.domNode.appendChild(this.widget);
dojo.html.prependClass(this.widget, "xformsTextArea");
this.widget.innerHTML = this.getInitialValue() || "";
if (!this.isReadonly())
{
this._createTinyMCE();
}
},
setValue: function(value)
{
if (this.isReadonly())
{
this.widget.innerHTML = value;
}
else
{
tinyMCE.selectedInstance = tinyMCE.getInstanceById(this.id);
try
{
tinyMCE.setContent(value);
}
catch (e)
{
//XXXarielb figure this out - getting intermittent errors in IE.
dojo.debug(e);
}
}
},
getValue: function()
{
return this.isReadonly() ? this.widget.innerHTML : tinyMCE.getContent(this.id);
},
setReadonly: function(readonly)
{
alfresco.xforms.RichTextEditor.superclass.setReadonly.call(this, readonly);
var mce = tinyMCE.getInstanceById(this.id);
if (readonly && mce)
{
this._removeTinyMCE();
}
else if (!readonly && !mce && this.widget)
{
this._createTinyMCE();
}
},
_destroy: function()
{
alfresco.xforms.RichTextEditor.superclass._destroy.call(this);
if (!this.isReadonly())
{
dojo.debug("removing mce control " + this.id);
tinyMCE.removeMCEControl(this.id);
}
},
/////////////////////////////////////////////////////////////////
// DOM event handlers
/////////////////////////////////////////////////////////////////
_tinyMCE_blurHandler: function(event)
{
if (event.type == "beforedeactivate")
{
event.target = event.srcElement.ownerDocument;
}
var widget = event.target.widget;
widget._commitValueChange();
this.focused = false;
},
_tinyMCE_focusHandler: function(event)
{
var widget = event.target.widget;
var repeatIndices = widget.getRepeatIndices();
if (repeatIndices.length != 0 && !this.focused)
{
var r = repeatIndices[repeatIndices.length - 1].repeat;
var p = widget;
while (p && p.parent != r)
{
if (p.parent instanceof alfresco.xforms.Repeat)
{
throw new Error("unexpected parent repeat " + p.parent.id);
}
p = p.parent;
}
if (!p)
{
throw new Error("unable to find parent repeat " + r.id +
" of " + widget.id);
}
repeatIndices[repeatIndices.length - 1].repeat.setFocusedChild(p);
}
this.focused = true;
}
});
/** Base class for all select widgets. */
dojo.declare("alfresco.xforms.AbstractSelectWidget",
alfresco.xforms.Widget,
function(xform, xformsNode)
{
},
{
/////////////////////////////////////////////////////////////////
// methods
/////////////////////////////////////////////////////////////////
/**
* Returns the possible item values for the select control as an array
* of anonymous objects with properties id, label, value, and valid.
*/
_getItemValues: function()
{
var binding = this.xform.getBinding(this.xformsNode);
var values = _getElementsByTagNameNS(this.xformsNode,
alfresco_xforms_constants.XFORMS_NS,
alfresco_xforms_constants.XFORMS_PREFIX,
"item");
var result = [];
for (var i = 0; i < values.length; i++)
{
var label = _getElementsByTagNameNS(values[i],
alfresco_xforms_constants.XFORMS_NS,
alfresco_xforms_constants.XFORMS_PREFIX,
"label")[0];
var value = _getElementsByTagNameNS(values[i],
alfresco_xforms_constants.XFORMS_NS,
alfresco_xforms_constants.XFORMS_PREFIX,
"value")[0];
var valid = true;
if (binding.constraint)
{
if (!dojo.render.html.ie)
{
valid = _evaluateXPath(binding.constraint, value, XPathResult.BOOLEAN_TYPE);
dojo.debug("evaludated constraint " + binding.constraint +
" on " + dojo.dom.textContent(value) +
" to " + valid);
}
else
{
valid = !(dojo.dom.textContent(value) == dojo.dom.textContent(label) &&
dojo.dom.textContent(value).match(/^\[.+\]$/));
}
}
result.push({
id: value.getAttribute("id"),
label: valid ? dojo.dom.textContent(label) : "",
value: valid ? dojo.dom.textContent(value) : "_invalid_value_",
valid: valid
});
dojo.debug("values["+ i + "] = {id: " + result[i].id +
",label: " + result[i].label + ",value: " + result[i].value +
",valid: " + result[i].valid + "}");
}
return result;
}
});
/**
* Handles xforms widget xf:select. Produces either a multiselect list or a set of
* checkboxes depending on the number of inputs.
*/
dojo.declare("alfresco.xforms.Select",
alfresco.xforms.AbstractSelectWidget,
function(xform, xformsNode)
{
},
{
/////////////////////////////////////////////////////////////////
// overridden methods
/////////////////////////////////////////////////////////////////
render: function(attach_point)
{
var values = this._getItemValues();
var initial_value = this.getInitialValue();
initial_value = initial_value ? initial_value.split(' ') : [];
this._selectedValues = [];
if (this.getAppearance() == "full")
{
this.widget = document.createElement("div");
this.widget.style.width = "100%";
attach_point.appendChild(this.widget);
for (var i = 0; i < values.length; i++)
{
var checkboxDiv = document.createElement("div");
checkboxDiv.style.lineHeight = "16px";
this.widget.appendChild(checkboxDiv);
var checkbox = document.createElement("input");
checkbox.setAttribute("id", this.id + "_" + i + "-widget");
checkbox.setAttribute("name", this.id + "_" + i + "-widget");
checkbox.setAttribute("type", "checkbox");
checkbox.setAttribute("value", values[i].value);
if (initial_value.indexOf(values[i].value) != -1)
{
this._selectedValues.push(values[i].value);
checkbox.checked = true;
}
checkboxDiv.appendChild(checkbox);
checkboxDiv.appendChild(document.createTextNode(values[i].label));
dojo.event.connect(checkbox, "onclick", this, this._checkbox_clickHandler);
}
}
else
{
this.widget = document.createElement("select");
this.widget.setAttribute("id", this.id + "-widget");
this.widget.setAttribute("multiple", true);
attach_point.appendChild(this.widget);
for (var i = 0; i < values.length; i++)
{
var option = document.createElement("option");
option.appendChild(document.createTextNode(values[i].label));
option.setAttribute("value", values[i].value);
if (initial_value.indexOf(values[i].value) != -1)
{
this._selectedValues.push(values[i].value);
option.selected = true;
}
this.widget.appendChild(option);
}
dojo.event.connect(this.widget, "onblur", this, this._list_changeHandler);
}
},
setValue: function(value, forceCommit)
{
if (!this.widget)
{
this.setInitialValue(value, forceCommit);
}
else
{
alfresco.xforms.Select.superclass.setValue(this, value, forceCommit);
this._selectedValues = value.split(' ');
if (this.widget.nodeName.toLowerCase() == "div")
{
var checkboxes = this.widgets.getElementsByTagName("input");
for (var i = 0; i < checkboxes.length; i++)
{
checkboxes[i].checked =
this._selectedValues.indexOf(checkboxes[i].getAttribute("value")) != -1;
}
}
else if (this.widget.nodeName.toLowerCase() == "select")
{
var options = this.widgets.getElementsByTagName("option");
for (var i = 0; i < options.length; i++)
{
options[i].selected =
this._selectedValues.indexOf(options[i].getAttribute("value")) != -1;
}
}
else
{
throw new Error("unexpected nodeName for Select widget: " + this.widget.nodeName);
}
}
},
getValue: function()
{
return this._selectedValues.length == 0 ? null : this._selectedValues.join(" ");
},
/////////////////////////////////////////////////////////////////
// DOM event handlers
/////////////////////////////////////////////////////////////////
_list_changeHandler: function(event)
{
this._selectedValues = [];
for (var i = 0; i < event.target.options.length; i++)
{
if (event.target.options[i].selected)
{
this._selectedValues.push(event.target.options[i].getAttribute("value"));
}
}
this._commitValueChange();
},
_checkbox_clickHandler: function(event)
{
this._selectedValues = [];
var all_checkboxes = this.widget.getElementsByTagName("input");
for (var i = 0; i < all_checkboxes.length; i++)
{
if (all_checkboxes[i] && all_checkboxes[i].checked)
{
this._selectedValues.push(all_checkboxes[i].getAttribute("value"));
}
}
this._commitValueChange();
}
});
/**
* Handles xforms widget xf:select1. Produces either a combobox or a set of
* radios depending on the number of inputs.
*/
dojo.declare("alfresco.xforms.Select1",
alfresco.xforms.AbstractSelectWidget,
function(xform, xformsNode)
{
},
{
/////////////////////////////////////////////////////////////////
// overridden methods
/////////////////////////////////////////////////////////////////
render: function(attach_point)
{
var values = this._getItemValues();
var initial_value = this.getInitialValue();
if (this.getAppearance() == "full")
{
this.widget = document.createElement("div");
this.widget.style.width = "100%";
attach_point.appendChild(this.widget);
for (var i = 0; i < values.length; i++)
{
if (!values[i].valid)
{
// always skip the invalid values for radios
continue;
}
var radio_div = document.createElement("div");
radio_div.style.lineHeight = "16px";
this.widget.appendChild(radio_div);
var radio = document.createElement("input");
radio.setAttribute("id", this.id + "-widget");
radio.setAttribute("name", this.id + "-widget");
radio.setAttribute("type", "radio");
radio_div.appendChild(radio);
radio_div.appendChild(document.createTextNode(values[i].label));
radio.setAttribute("value", values[i].value);
if (values[i].value == initial_value)
{
this._selectedValue = initial_value;
radio.checked = true;
}
dojo.event.connect(radio, "onclick", this, this._radio_clickHandler);
}
this.widget.style.height = this.widget.offsetHeight + "px";
}
else
{
this.widget = document.createElement("select");
this.widget.setAttribute("id", this.id + "-widget");
attach_point.appendChild(this.widget);
for (var i = 0; i < values.length; i++)
{
if (initial_value && !values[i].valid)
{
// skip the invalid value if we have a default value
continue;
}
var option = document.createElement("option");
this.widget.appendChild(option);
option.appendChild(document.createTextNode(values[i].label));
option.setAttribute("value", values[i].value);
if (values[i].value == initial_value)
{
this._selectedValue = initial_value;
option.selected = true;
}
}
dojo.event.connect(this.widget, "onchange", this, this._combobox_changeHandler);
}
},
/** */
setValue: function(value, forceCommit)
{
if (!this.widget)
{
this.setInitialValue(value, forceCommit);
}
else
{
alfresco.xforms.Select1.superclass.setValue.call(this, value, forceCommit);
this._selectedValue = value;
if (this.widget.nodeName.toLowerCase() == "div")
{
var radios = this.widget.getElementsByTagName("input");
for (var i = 0; i < radios.length; i++)
{
radios[i].checked = radios[i].getAttribute("value") == this._selectedValue;
}
}
else if (this.widget.nodeName.toLowerCase() == "select")
{
var options = this.widget.getElementsByTagName("option");
for (var i = 0; i < options.length; i++)
{
options[i].selected = options[i].getAttribute("value") == this._selectedValue;
}
}
else
{
throw new Error("unexpected nodeName for Select1 widget: " + this.widget.nodeName);
}
}
},
getValue: function()
{
return this._selectedValue;
},
/////////////////////////////////////////////////////////////////
// DOM event handlers
/////////////////////////////////////////////////////////////////
_combobox_changeHandler: function(event)
{
this._selectedValue = event.target.options[event.target.selectedIndex].value;
this._commitValueChange();
},
_radio_clickHandler: function(event)
{
if (!event.target.checked)
{
var all_radios = this.widget.getElementsByTagName("input");
for (var i = 0; i < all_radios.length; i++)
{
if (all_radios[i].name == event.target.name)
{
all_radios[i].checked = event.target == all_radios[i];
}
}
}
this._selectedValue = event.target.value;
this._commitValueChange();
}
});
/**
* Handles xforms widget xf:select1 with a type of boolean.
*/
dojo.declare("alfresco.xforms.Checkbox",
alfresco.xforms.Widget,
function(xform, xformsNode)
{
},
{
/////////////////////////////////////////////////////////////////
// overridden methods
/////////////////////////////////////////////////////////////////
render: function(attach_point)
{
var initial_value = this.getInitialValue() == "true";
this.widget = document.createElement("input");
this.widget.setAttribute("type", "checkbox");
this.widget.setAttribute("id", this.id + "-widget");
attach_point.appendChild(this.widget);
if (initial_value)
{
this.widget.setAttribute("checked", true);
}
dojo.event.connect(this.widget, "onclick", this, this._checkbox_clickHandler);
},
setValue: function(value, forceCommit)
{
if (!this.widget)
{
this.setInitialValue(value, forceCommit);
}
else
{
alfresco.xforms.Checkbox.superclass.setValue.call(this, value, forceCommit);
this.widget.checked = value == "true";
}
},
getValue: function()
{
return this.widget.checked;
},
/////////////////////////////////////////////////////////////////
// DOM event handlers
/////////////////////////////////////////////////////////////////
_checkbox_clickHandler: function(event)
{
this._commitValueChange();
}
});
////////////////////////////////////////////////////////////////////////////////
// widgets for date types
////////////////////////////////////////////////////////////////////////////////
/** The date picker widget which handles xforms widget xf:input with type xf:date */
dojo.declare("alfresco.xforms.DatePicker",
alfresco.xforms.Widget,
function(xform, xformsNode)
{
this._noValueSet = (alfresco_xforms_constants.resources["eg"] + " " +
dojo.date.format(new Date(),
{datePattern: alfresco_xforms_constants.DATE_FORMAT,
selector: 'dateOnly'}));
},
{
_createPicker: function()
{
var datePickerDiv = document.createElement("div");
this.domNode.parentNode.appendChild(datePickerDiv);
var dp_initial_value = this.getValue() || dojo.date.toRfc3339(new Date());
this.widget.picker = dojo.widget.createWidget("DatePicker",
{
value: dp_initial_value
},
datePickerDiv);
this.domContainer.style.height =
Math.max(this.widget.picker.domNode.offsetHeight +
this.widget.offsetHeight +
dojo.html.getMargin(this.domNode.parentNode).height,
20) + "px";
dojo.event.connect(this.widget.picker,
"onValueChanged",
this,
this._datePicker_valueChangedHandler);
},
_destroyPicker: function()
{
if (this.widget.picker)
{
this.domNode.parentNode.removeChild(this.widget.picker.domNode);
this.widget.picker = null;
this.domContainer.style.height =
Math.max(this.widget.offsetHeight +
dojo.html.getMargin(this.domNode.parentNode).height,
20) + "px";
}
},
/////////////////////////////////////////////////////////////////
// overridden methods
/////////////////////////////////////////////////////////////////
render: function(attach_point)
{
var initial_value = this.getInitialValue();
attach_point.appendChild(this.domNode);
this.widget = document.createElement("input");
this.widget.setAttribute("id", this.id + "-widget");
this.widget.setAttribute("type", "text");
if (initial_value)
{
var jsDate = dojo.date.fromRfc3339(initial_value);
this.widget.setAttribute("value",
dojo.date.format(jsDate,
{datePattern: alfresco_xforms_constants.DATE_FORMAT,
selector: 'dateOnly'}));
}
else
{
this.widget.setAttribute("value", this._noValueSet);
dojo.html.addClass(this.widget, "xformsGhostText");
}
this.domNode.appendChild(this.widget);
var expandoImage = document.createElement("img");
expandoImage.setAttribute("src", alfresco_xforms_constants.WEBAPP_CONTEXT + "/images/icons/action.gif");
expandoImage.align = "absmiddle";
expandoImage.style.margin = "0px 5px";
this.domNode.appendChild(expandoImage);
dojo.event.connect(expandoImage, "onclick", this, this._expando_clickHandler);
dojo.event.connect(this.widget, "onfocus", this, this._dateTextBox_focusHandler);
dojo.event.connect(this.widget, "onchange", this, this._dateTextBox_changeHandler);
},
setValue: function(value, forceCommit)
{
if (!this.widget)
{
this.setInitialValue(value, forceCommit);
}
else
{
alfresco.xforms.DatePicker.superclass.setValue.call(this, value, forceCommit);
var jsDate = dojo.date.fromRfc3339(value);
this.widget.value = dojo.date.format(jsDate,
{datePattern: alfresco_xforms_constants.DATE_FORMAT,
selector: 'dateOnly'});
dojo.html.removeClass(this.widget, "xformsGhostText");
}
},
getValue: function()
{
if (this.widget.value == null ||
this.widget.value.length == 0 ||
this.widget.value == this._noValueSet)
{
return null;
}
else
{
var jsDate = dojo.date.parse(this.widget.value,
{datePattern: alfresco_xforms_constants.DATE_FORMAT,
selector: 'dateOnly'});
return dojo.date.toRfc3339(jsDate, "dateOnly");
}
},
/////////////////////////////////////////////////////////////////
// DOM event handlers
/////////////////////////////////////////////////////////////////
_dateTextBox_focusHandler: function(event)
{
this._destroyPicker();
},
_dateTextBox_changeHandler: function(event)
{
this._commitValueChange();
},
_datePicker_valueChangedHandler: function(date)
{
var rfcDate = dojo.date.toRfc3339(date, "dateOnly");
this._destroyPicker();
this.setValue(rfcDate);
this._commitValueChange();
},
_expando_clickHandler: function()
{
if (this.widget.picker)
{
this._destroyPicker();
}
else
{
this._createPicker();
}
}
});
/** The date picker widget which handles xforms widget xf:input with type xf:date */
dojo.declare("alfresco.xforms.TimePicker",
alfresco.xforms.Widget,
function(xform, xformsNode)
{
this._noValueSet = (alfresco_xforms_constants.resources["eg"] + " " +
dojo.date.format(new Date(),
{timePattern: alfresco_xforms_constants.TIME_FORMAT,
selector: "timeOnly"}));
this._xformsFormat = "HH:mm:ss";
},
{
/** */
_createPicker: function()
{
var timePickerDiv = document.createElement("div");
this.domNode.appendChild(timePickerDiv);
var jsDate = (this.getValue()
? dojo.date.parse(this.getValue(),
{timePattern: this._xformsFormat,
selector: "timeOnly"})
: new Date());
this.widget.picker = dojo.widget.createWidget("TimePicker",
{
value: jsDate
},
timePickerDiv);
this.widget.picker.anyTimeContainerNode.innerHTML = "";
// don't let it float - it screws up layout somehow
this.widget.picker.domNode.style.cssFloat = "none";
this.domContainer.style.height =
Math.max(this.widget.picker.domNode.offsetHeight +
this.widget.offsetHeight +
dojo.html.getMargin(this.domNode.parentNode).height,
20) + "px";
dojo.event.connect(this.widget.picker,
"onValueChanged",
this,
this._timePicker_valueChangedHandler);
},
_destroyPicker: function()
{
if (this.widget.picker)
{
this.domNode.removeChild(this.widget.picker.domNode);
this.widget.picker = null;
this.domContainer.style.height =
Math.max(this.widget.offsetHeight +
dojo.html.getMargin(this.domNode.parentNode).height,
20) + "px";
}
},
/////////////////////////////////////////////////////////////////
// overridden methods
/////////////////////////////////////////////////////////////////
render: function(attach_point)
{
var initial_value = this.getInitialValue();
attach_point.appendChild(this.domNode);
this.widget = document.createElement("input");
this.widget.setAttribute("id", this.id + "-widget");
this.widget.setAttribute("type", "text");
if (initial_value)
{
var jsDate = dojo.date.parse(initial_value, {timePattern: this._xformsFormat, selector: "timeOnly"});
this.widget.setAttribute("value",
dojo.date.format(jsDate,
{timePattern: alfresco_xforms_constants.TIME_FORMAT,
selector: "timeOnly"}));
}
else
{
this.widget.setAttribute("value", this._noValueSet);
dojo.html.addClass(this.widget, "xformsGhostText");
}
this.domNode.appendChild(this.widget);
var expandoImage = document.createElement("img");
expandoImage.setAttribute("src", alfresco_xforms_constants.WEBAPP_CONTEXT + "/images/icons/action.gif");
expandoImage.align = "absmiddle";
expandoImage.style.margin = "0px 5px";
this.domNode.appendChild(expandoImage);
dojo.event.connect(expandoImage, "onclick", this, this._expando_clickHandler);
dojo.event.connect(this.widget, "onfocus", this, this._timeTextBox_focusHandler);
dojo.event.connect(this.widget, "onchange", this, this._timeTextBox_changeHandler);
},
setValue: function(value, forceCommit)
{
if (!this.widget)
{
this.setInitialValue(value, forceCommit);
}
else
{
alfresco.xforms.TimePicker.superclass.setValue.call(this, value, forceCommit);
var jsDate = dojo.date.parse(value, {timePattern: this._xformsFormat, selector: "timeOnly"});
this.widget.value = dojo.date.format(jsDate,
{timePattern: alfresco_xforms_constants.TIME_FORMAT,
selector: "timeOnly"});
dojo.html.removeClass(this.widget, "xformsGhostText");
}
},
getValue: function()
{
if (this.widget.value == null ||
this.widget.value.length == 0 ||
this.widget.value == this._noValueSet)
{
return null;
}
else
{
var jsDate = dojo.date.parse(this.widget.value,
{timePattern: alfresco_xforms_constants.TIME_FORMAT,
selector: "timeOnly"});
return dojo.date.format(jsDate, {timePattern: this._xformsFormat, selector: "timeOnly"});
}
},
/////////////////////////////////////////////////////////////////
// DOM event handlers
/////////////////////////////////////////////////////////////////
_timeTextBox_focusHandler: function(event)
{
this._destroyPicker();
},
_timeTextBox_changeHandler: function(event)
{
this._commitValueChange();
},
_timePicker_valueChangedHandler: function(date)
{
var xfDate = dojo.date.format(date, {timePattern: this._xformsFormat, selector: "timeOnly"});
this.setValue(xfDate);
this._commitValueChange();
},
_expando_clickHandler: function()
{
if (this.widget.picker)
{
this._destroyPicker();
}
else
{
this._createPicker();
}
}
});
/** The date time picker widget which handles xforms widget xf:input with type xf:datetime */
dojo.declare("alfresco.xforms.DateTimePicker",
alfresco.xforms.Widget,
function(xform, xformsNode)
{
this._noValueSet = (alfresco_xforms_constants.resources["eg"] + " " +
dojo.date.format(new Date(),
{datePattern: alfresco_xforms_constants.DATE_TIME_FORMAT,
selector: "dateOnly"}));
},
{
/** */
_createPicker: function()
{
this._pickerDiv = document.createElement("div");
this._pickerDiv.style.position = "relative";
this._pickerDiv.style.width = this.widget.offsetWidth + "px";
this.domNode.appendChild(this._pickerDiv);
var datePickerDiv = document.createElement("div");
datePickerDiv.style.position = "absolute";
datePickerDiv.style.left = "0px";
datePickerDiv.style.top = "0px";
this._pickerDiv.appendChild(datePickerDiv);
var dp_initial_value = this.getValue() || dojo.date.toRfc3339(new Date());
this.widget.datePicker = dojo.widget.createWidget("DatePicker",
{
value: dp_initial_value
},
datePickerDiv);
var timePickerDiv = document.createElement("div");
timePickerDiv.style.position = "absolute";
timePickerDiv.style.right = "0px";
timePickerDiv.style.top = "0px";
this._pickerDiv.appendChild(timePickerDiv);
var jsDate = this.getValue() ? dojo.date.fromRfc3339(this.getValue()) : new Date();
this.widget.timePicker = dojo.widget.createWidget("TimePicker",
{
value: jsDate
},
timePickerDiv);
this.widget.timePicker.anyTimeContainerNode.innerHTML = "";
// don't let it float - it screws up layout somehow
this.widget.timePicker.domNode.style.cssFloat = "none";
this._pickerDiv.style.height = Math.max(this.widget.timePicker.domNode.offsetHeight,
this.widget.datePicker.domNode.offsetHeight);
this.domContainer.style.height =
Math.max(this._pickerDiv.offsetHeight +
this.widget.offsetHeight +
dojo.html.getMargin(this.domNode.parentNode).height,
20) + "px";
dojo.event.connect(this.widget.datePicker,
"onValueChanged",
this,
this._datePicker_valueChangedHandler);
dojo.event.connect(this.widget.timePicker,
"onValueChanged",
this,
this._timePicker_valueChangedHandler);
},
_destroyPicker: function()
{
if (this._pickerDiv)
{
this.domNode.removeChild(this._pickerDiv);
this.widget.datePicker = null;
this.widget.timePicker = null;
this._pickerDiv = null;
this.domContainer.style.height =
Math.max(this.widget.offsetHeight +
dojo.html.getMargin(this.domNode.parentNode).height,
20) + "px";
}
},
/////////////////////////////////////////////////////////////////
// overridden methods
/////////////////////////////////////////////////////////////////
render: function(attach_point)
{
var initial_value = this.getInitialValue();
attach_point.appendChild(this.domNode);
this.widget = document.createElement("input");
this.widget.setAttribute("id", this.id + "-widget");
this.widget.setAttribute("type", "text");
if (initial_value)
{
var jsDate = dojo.date.fromRfc3339(initial_value);
this.widget.setAttribute("value",
dojo.date.format(jsDate,
{timePattern: alfresco_xforms_constants.DATE_TIME_FORMAT,
selector: "timeOnly"}));
}
else
{
this.widget.setAttribute("value", this._noValueSet);
dojo.html.addClass(this.widget, "xformsGhostText");
}
this.domNode.appendChild(this.widget);
this.widget.style.width = (3 * this.widget.offsetWidth) + "px";
var expandoImage = document.createElement("img");
expandoImage.setAttribute("src", alfresco_xforms_constants.WEBAPP_CONTEXT + "/images/icons/action.gif");
expandoImage.align = "absmiddle";
expandoImage.style.margin = "0px 5px";
this.domNode.appendChild(expandoImage);
dojo.event.connect(expandoImage, "onclick", this, this._expando_clickHandler);
dojo.event.connect(this.widget, "onfocus", this, this._dateTimeTextBox_focusHandler);
dojo.event.connect(this.widget, "onchange", this, this._dateTimeTextBox_changeHandler);
},
setValue: function(value, forceCommit)
{
if (!this.widget)
{
this.setInitialValue(value, forceCommit);
}
else
{
alfresco.xforms.DateTimePicker.superclass.setValue.call(this, value, forceCommit);
var jsDate = dojo.date.fromRfc3339(value);
this.widget.value = dojo.date.format(jsDate,
{datePattern: alfresco_xforms_constants.DATE_TIME_FORMAT,
selector: "dateOnly"});
dojo.html.removeClass(this.widget, "xformsGhostText");
}
},
getValue: function()
{
if (this.widget.value == null ||
this.widget.value.length == 0 ||
this.widget.value == this._noValueSet)
{
return null;
}
else
{
var jsDate = dojo.date.parse(this.widget.value,
{datePattern: alfresco_xforms_constants.DATE_TIME_FORMAT,
selector: "dateOnly"});
return dojo.date.toRfc3339(jsDate);
}
},
/////////////////////////////////////////////////////////////////
// DOM event handlers
/////////////////////////////////////////////////////////////////
_dateTimeTextBox_focusHandler: function(event)
{
this._destroyPicker();
},
_dateTimeTextBox_changeHandler: function(event)
{
this._commitValueChange();
},
_timePicker_valueChangedHandler: function(date)
{
var value = this.getValue() ? dojo.date.fromRfc3339(this.getValue()) : new Date();
value.setHours(date.getHours());
value.setMinutes(date.getMinutes());
value = dojo.date.toRfc3339(value);
this.setValue(value);
this._commitValueChange();
},
_datePicker_valueChangedHandler: function(date)
{
var value = this.getValue() ? dojo.date.fromRfc3339(this.getValue()) : new Date();
value.setYear(date.getYear());
value.setMonth(date.getMonth());
value.setDate(date.getDate());
value = dojo.date.toRfc3339(value);
this.setValue(value);
this._commitValueChange();
},
_expando_clickHandler: function()
{
if (this._pickerDiv)
{
this._destroyPicker();
}
else
{
this._createPicker();
}
}
});
/** The year picker handles xforms widget xf:input with a gYear type */
dojo.declare("alfresco.xforms.YearPicker",
alfresco.xforms.TextField,
function(xform, xformsNode)
{
},
{
/////////////////////////////////////////////////////////////////
// overridden methods
/////////////////////////////////////////////////////////////////
render: function(attach_point)
{
alfresco.xforms.YearPicker.superclass.render.call(this, attach_point);
this.widget.size = "4";
this.widget.setAttribute("maxlength", "4");
},
getInitialValue: function()
{
var result = alfresco.xforms.YearPicker.superclass.getInitialValue.call(this);
return result ? result.replace(/^0*([^0]+)$/, "$1") : result;
},
setValue: function(value, forceCommit)
{
alfresco.xforms.YearPicker.superclass.setValue.call(this,
(value
? value.replace(/^0*([^0]+)$/, "$1")
: null),
forceCommit);
},
getValue: function()
{
var result = alfresco.xforms.YearPicker.superclass.getValue.call(this);
return result ? dojo.string.padLeft(result, 4, "0") : null;
}
});
/** The day picker widget which handles xforms widget xf:input with type xf:gDay */
dojo.declare("alfresco.xforms.DayPicker",
alfresco.xforms.Select1,
function(xform, xformsNode)
{
},
{
/////////////////////////////////////////////////////////////////
// overridden methods
/////////////////////////////////////////////////////////////////
_getItemValues: function()
{
var result = [];
result.push({id: "day_empty", label: "", value: "", valid: false});
for (var i = 1; i <= 31; i++)
{
result.push({
id: "day_" + i,
label: i,
value: "---" + (i < 10 ? "0" + i : i),
valid: true});
}
return result;
}
});
/** The month picker widget which handles xforms widget xf:input with type xf:gMonth */
dojo.declare("alfresco.xforms.MonthPicker",
alfresco.xforms.Select1,
function(xform, xformsNode)
{
},
{
/////////////////////////////////////////////////////////////////
// overridden methods
/////////////////////////////////////////////////////////////////
_getItemValues: function()
{
var result = [];
result.push({id: "month_empty", label: "", value: "", valid: false});
for (var i = 0; i <= 12; i++)
{
var d = new Date();
d.setMonth(i);
result.push({
id: "month_" + i,
label: dojo.date.getMonthName(d),
value: "--" + (i + 1 < 10 ? "0" + (i + 1) : i + 1),
valid: true});
}
return result;
}
});
/** The month day picker widget which handles xforms widget xf:input with type xf:gMonthDay */
dojo.declare("alfresco.xforms.MonthDayPicker",
alfresco.xforms.Widget,
function(xform, xformsNode)
{
this.monthPicker = new alfresco.xforms.MonthPicker(xform, xformsNode);
this.monthPicker._compositeParent = this;
this.dayPicker = new alfresco.xforms.DayPicker(xform, xformsNode);
this.dayPicker._compositeParent = this;
},
{
/////////////////////////////////////////////////////////////////
// overridden methods
/////////////////////////////////////////////////////////////////
render: function(attach_point)
{
this.setValue(this.getInitialValue());
attach_point.appendChild(this.domNode);
this.dayPicker.render(this.domNode);
this.dayPicker.widget.style.marginRight = "10px";
this.monthPicker.render(this.domNode);
},
setValue: function(value)
{
this.monthPicker.setValue(value ? value.match(/^--[^-]+/)[0] : null);
this.dayPicker.setValue(value ? "---" + value.replace(/^--[^-]+-/, "") : null);
},
getValue: function()
{
// format is --MM-DD
var day = this.dayPicker.getValue();
var month = this.monthPicker.getValue();
return month && day ? day.replace(/^--/, month) : null;
}
});
/** The year month picker widget which handles xforms widget xf:input with type xf:gYearMonth */
dojo.declare("alfresco.xforms.YearMonthPicker",
alfresco.xforms.Widget,
function(xform, xformsNode)
{
this.yearPicker = new alfresco.xforms.YearPicker(xform, xformsNode);
this.yearPicker._compositeParent = this;
this.monthPicker = new alfresco.xforms.MonthPicker(xform, xformsNode);
this.monthPicker._compositeParent = this;
},
{
/////////////////////////////////////////////////////////////////
// overridden methods
/////////////////////////////////////////////////////////////////
render: function(attach_point)
{
this.setValue(this.getInitialValue());
attach_point.appendChild(this.domNode);
this.monthPicker.render(this.domNode);
this.monthPicker.widget.style.marginRight = "10px";
this.yearPicker.domNode.style.display = "inline";
this.yearPicker.render(this.domNode);
},
setValue: function(value)
{
this.monthPicker.setValue(value ? value.replace(/^[^-]+-/, "--") : null);
this.yearPicker.setValue(value ? value.match(/^[^-]+/)[0] : null);
},
getValue: function()
{
// format is CCYY-MM
var year = this.yearPicker.getValue();
var month = this.monthPicker.getValue();
return year && month ? month.replace(/^-/, year) : null;
}
});
////////////////////////////////////////////////////////////////////////////////
// widgets for group types
////////////////////////////////////////////////////////////////////////////////
/**
* Handles xforms widget xf:group. A group renders and manages a set of children
* and provides a header for expanding and collapsing the group. A group header
* is shown for all group that don't have xf:appearance set to 'repeated' and
* that are not the root group.
*/
dojo.declare("alfresco.xforms.Group",
alfresco.xforms.Widget,
function(xform, xformsNode)
{
this._children = [];
dojo.html.removeClass(this.domNode, "xformsItem");
},
{
/////////////////////////////////////////////////////////////////
// methods & properties
/////////////////////////////////////////////////////////////////
_groupHeaderNode: null,
/** Returns the child at the specified index or null if the index is out of range. */
getChildAt: function(index)
{
return index < this._children.length ? this._children[index] : null;
},
/** Returns the index of a particular child or -1 if the child was not found. */
getChildIndex: function(child)
{
for (var i = 0; i < this._children.length; i++)
{
dojo.debug(this.id + "[" + i + "]: " +
" is " + this._children[i].id +
" the same as " + child.id + "?");
if (this._children[i] == child)
{
return i;
}
}
return -1;
},
/** Adds the child to end of the list of children. */
addChild: function(child)
{
return this._insertChildAt(child, this._children.length);
},
/** Inserts a child at the specified position. */
_insertChildAt: function(child, position)
{
dojo.debug(this.id + "._insertChildAt(" + child.id + ", " + position + ")");
child.parent = this;
child.domContainer = document.createElement("div");
child.domContainer.setAttribute("id", child.id + "-domContainer");
dojo.html.addClass(child.domContainer, "xformsItemDOMContainer");
if (this.parent && this.parent.domNode)
{
child.domContainer.style.top = this.parent.domNode.style.bottom;
}
if (!this.domNode.childContainerNode.parentNode)
{
// only add this to the dom once we're adding a child
this.domNode.appendChild(this.domNode.childContainerNode);
}
if (position == this._children.length)
{
this.domNode.childContainerNode.appendChild(child.domContainer);
this._children.push(child);
}
else
{
this.domNode.childContainerNode.insertBefore(child.domContainer,
this.getChildAt(position).domContainer);
this._children.splice(position, 0, child);
}
function shouldInsertDivider(group, child, position)
{
if (group.getAppearance() != "full")
{
return false;
}
if (group instanceof alfresco.xforms.Repeat)
{
return false;
}
if (!child.isVisible())
{
return false;
}
if (group._children[position - 1] instanceof alfresco.xforms.Group)
{
return true;
}
if (child instanceof alfresco.xforms.Group)
{
for (var i = position - 1; i > 0; i--)
{
if (group._children[i].isVisible())
{
return true;
}
}
}
return false;
}
if (shouldInsertDivider(this, child, position))
{
var divider = document.createElement("div");
dojo.html.setClass(divider, "xformsGroupDivider");
this.domNode.childContainerNode.insertBefore(divider,
child.domContainer);
}
var labelDiv = null;
if (!(child instanceof alfresco.xforms.Group))
{
var labelDiv = document.createElement("div");
labelDiv.setAttribute("id", child.id + "-label");
labelDiv.style.position = "relative";
labelDiv.style.left = "0px";
child.domContainer.appendChild(labelDiv);
var requiredImage = document.createElement("img");
requiredImage.setAttribute("src",
alfresco_xforms_constants.WEBAPP_CONTEXT + "/images/icons/required_field.gif");
requiredImage.style.verticalAlign = "middle";
requiredImage.style.marginLeft = "5px";
requiredImage.style.marginRight = "5px";
requiredImage.style.left = "0px";
requiredImage.style.position = "relative";
requiredImage.style.top = "0px";
labelDiv.appendChild(requiredImage);
if (!child.isRequired())
{
requiredImage.style.visibility = "hidden";
}
var label = child.getLabel();
if (label)
{
child.labelNode = document.createElement("span");
child.labelNode.style.verticalAlign = "middle";
labelDiv.appendChild(child.labelNode);
child.labelNode.appendChild(document.createTextNode(label));
}
}
var contentDiv = document.createElement("div");
contentDiv.setAttribute("id", child.id + "-content");
dojo.html.setClass(contentDiv, "xformsGroupItem");
child.domContainer.appendChild(contentDiv);
contentDiv.style.left = (child instanceof alfresco.xforms.Group
? "0px"
: "30%");
contentDiv.style.width = (child instanceof alfresco.xforms.Group
? "100%"
: (1 - (contentDiv.offsetLeft /
child.domContainer.offsetWidth)) * 100 + "%");
child.render(contentDiv);
if (!(child instanceof alfresco.xforms.Group))
{
child.domContainer.style.height =
Math.max(contentDiv.offsetHeight +
dojo.html.getMargin(contentDiv).height, 20) + "px";
// child.domContainer.style.lineHeight = child.domContainer.style.height;
}
dojo.debug(contentDiv.getAttribute("id") + " offsetTop is " + contentDiv.offsetTop);
contentDiv.style.top = "-" + Math.max(0, contentDiv.offsetTop -
dojo.html.getPixelValue(contentDiv, "margin-top")) + "px";
if (labelDiv)
{
labelDiv.style.top = (contentDiv.offsetTop + ((.5 * contentDiv.offsetHeight) -
(.5 * labelDiv.offsetHeight))) + "px";
}
contentDiv.widget = child;
this._updateDisplay();
this._childAdded(child);
return child.domContainer;
},
/** Removes the child at the specified position. */
_removeChildAt: function(position)
{
var child = this.getChildAt(position);
if (!child)
{
throw new Error("unable to find child at " + position);
}
this._children.splice(position, 1);
child.domContainer.group = this;
var anim = dojo.lfx.html.fadeOut(child.domContainer, 500);
anim.onEnd = function()
{
child.domContainer.style.display = "none";
child._destroy();
dojo.dom.removeChildren(child.domContainer);
dojo.dom.removeNode(child.domContainer);
child.domContainer.group._updateDisplay();
};
anim.play();
this._childRemoved(child);
return child;
},
/** Event handler for when a child has been added. */
_childAdded: function(child) { },
/** Event handler for when a child has been removed. */
_childRemoved: function(child) { },
/////////////////////////////////////////////////////////////////
// overridden methods & properties
/////////////////////////////////////////////////////////////////
isValidForSubmit: function()
{
return true;
},
/** Iterates all children a produces an array of widgets which are invalid for submit. */
getWidgetsInvalidForSubmit: function()
{
var result = [];
for (var i = 0; i < this._children.length; i++)
{
if (this._children[i] instanceof alfresco.xforms.Group)
{
result = result.concat(this._children[i].getWidgetsInvalidForSubmit());
}
else if (!this._children[i].isValidForSubmit())
{
result.push(this._children[i]);
}
}
return result;
},
/** Recusively destroys all children. */
_destroy: function()
{
alfresco.xforms.Group.superclass._destroy.call(this);
for (var i = 0; i < this._children.length; i++)
{
this._children[i]._destroy();
}
},
setReadonly: function(readonly)
{
alfresco.xforms.Group.superclass.setReadonly.call(this, readonly);
for (var i = 0; i < this._children.length; i++)
{
this._children[i].setReadonly(readonly);
}
},
render: function(attach_point)
{
this.domNode.widget = this;
if (false && djConfig.isDebug)
{
var idNode = document.createElement("div");
idNode.style.backgroundColor = "red";
idNode.appendChild(document.createTextNode(this.getLabel()));
this.domNode.appendChild(idNode);
}
if (this.getAppearance() == "full")
{
dojo.html.setClass(this.domNode, "xformsGroup");
this.domNode.style.position = "relative";
this.domNode.style.marginLeft = 10 + "px";
this.domNode.style.marginRight = (parseInt(this.domNode.style.marginLeft) / 3) + "px";
if (dojo.render.html.ie)
{
this.domNode.style.width = "100%";
}
else
{
this.domNode.style.width = (1 - ((dojo.html.getBorder(this.domNode).width +
dojo.html.getPadding(this.domNode).width +
dojo.html.getMargin(this.domNode).width) /
attach_point.offsetWidth)) * 100 + "%";
}
this._groupHeaderNode = document.createElement("div");
this._groupHeaderNode.setAttribute("id", this.id + "-groupHeaderNode");
dojo.html.setClass(this._groupHeaderNode, "xformsGroupHeader");
this.domNode.appendChild(this._groupHeaderNode);
this.toggleExpandedImage = document.createElement("img");
this._groupHeaderNode.appendChild(this.toggleExpandedImage);
this.toggleExpandedImage.setAttribute("src", alfresco_xforms_constants.EXPANDED_IMAGE.src);
this.toggleExpandedImage.align = "absmiddle";
this.toggleExpandedImage.style.marginLeft = "5px";
this.toggleExpandedImage.style.marginRight = "5px";
dojo.event.connect(this.toggleExpandedImage,
"onclick",
this,
this._toggleExpanded_clickHandler);
this._groupHeaderNode.appendChild(document.createTextNode(this.getLabel()));
}
attach_point.appendChild(this.domNode);
this.domNode.childContainerNode = document.createElement("div");
this.domNode.childContainerNode.setAttribute("id", this.id + "-childContainerNode");
this.domNode.childContainerNode.style.position = "relative";
this.domNode.childContainerNode.style.width = "100%";
return this.domNode;
},
/** Indicates if the group is expanded. */
isExpanded: function()
{
return (this.toggleExpandedImage.getAttribute("src") ==
alfresco_xforms_constants.EXPANDED_IMAGE.src);
},
/**
* Sets the expanded state of the widget. If collapsed, everything but the header
* will be hidden.
*/
setExpanded: function(expanded)
{
if (expanded != this.isExpanded())
{
this.toggleExpandedImage.src =
(expanded
? alfresco_xforms_constants.EXPANDED_IMAGE.src
: alfresco_xforms_constants.COLLAPSED_IMAGE.src);
this.domNode.childContainerNode.style.display = expanded ? "block" : "none";
}
},
_updateDisplay: function()
{
if (dojo.render.html.ie)
{
this.domNode.style.width = "100%";
}
else
{
this.domNode.style.width = (1 - ((dojo.html.getBorder(this.domNode).width +
dojo.html.getPadding(this.domNode).width +
dojo.html.getMargin(this.domNode).width) /
this.domNode.parentNode.offsetWidth)) * 100 + "%";
}
for (var i = 0; i < this._children.length; i++)
{
var contentDiv = document.getElementById(this._children[i].id + "-content");
contentDiv.style.position = "static";
contentDiv.style.top = "0px";
contentDiv.style.left = "0px";
contentDiv.style.position = "relative";
contentDiv.style.left = (this._children[i] instanceof alfresco.xforms.Group
? "0px"
: "30%");
contentDiv.style.width = (this._children[i] instanceof alfresco.xforms.Group
? "100%"
: (1 - (contentDiv.offsetLeft /
this._children[i].domContainer.parentNode.offsetWidth)) * 100 + "%");
this._children[i]._updateDisplay();
if (!(this._children[i] instanceof alfresco.xforms.Group))
{
this._children[i].domContainer.style.height =
Math.max(contentDiv.offsetHeight +
dojo.html.getMargin(contentDiv).height, 20) + "px";
}
contentDiv.style.top = "-" + Math.max(0, contentDiv.offsetTop -
dojo.html.getPixelValue(contentDiv, "margin-top")) + "px";
var labelDiv = document.getElementById(this._children[i].id + "-label");
if (labelDiv)
{
labelDiv.style.position = "static";
labelDiv.style.top = "0px";
labelDiv.style.left = "0px";
labelDiv.style.position = "relative";
labelDiv.style.top = (contentDiv.offsetTop + ((.5 * contentDiv.offsetHeight) -
(.5 * labelDiv.offsetHeight))) + "px";
}
}
},
showAlert: function()
{
for (var i = 0; i < this._children.length; i++)
{
this._children[i].showAlert();
}
},
hideAlert: function()
{
for (var i = 0; i < this._children.length; i++)
{
this._children[i].hideAlert();
}
},
/////////////////////////////////////////////////////////////////
// DOM event handlers
/////////////////////////////////////////////////////////////////
_toggleExpanded_clickHandler: function(event)
{
this.setExpanded(!this.isExpanded());
}
});
dojo.declare("alfresco.xforms.SwitchGroup",
alfresco.xforms.Group,
function(xform, xformsNode)
{
if (this.getInitialValue())
{
var initialValueTrigger = this._getCaseToggleTriggerByTypeValue(this.getInitialValue());
this._selectedCaseId = initialValueTrigger.getActions()["toggle"].properties["case"];
}
},
{
/////////////////////////////////////////////////////////////////
// methods & properties
/////////////////////////////////////////////////////////////////
_getCaseToggleTriggers: function()
{
var bw = this.xform.getBinding(this.xformsNode).widgets;
var result = [];
for (var i in bw)
{
if (! (bw[i] instanceof alfresco.xforms.Trigger))
{
continue;
}
var action = bw[i].getActions()["toggle"];
if (action)
{
result.push(bw[i]);
}
}
return result;
},
_getCaseToggleTriggerByCaseId: function(caseId)
{
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].getActions()["toggle"];
if (!action)
{
continue;
}
if (action.properties["case"] == caseId)
{
return bw[i];
}
}
throw new Error("unable to find trigger " + type +
", properties " + properties +
" for " + this.id);
},
_getCaseToggleTriggerByTypeValue: function(typeValue)
{
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].getActions()["setvalue"];
if (!action)
{
continue;
}
if (action.properties["value"] == typeValue)
{
return bw[i];
}
}
throw new Error("unable to find toggle trigger for type value " + typeValue +
" for " + this.id);
},
/////////////////////////////////////////////////////////////////
// overridden methods & properties
/////////////////////////////////////////////////////////////////
/** */
_insertChildAt: function(child, position)
{
var childDomContainer =
alfresco.xforms.SwitchGroup.superclass._insertChildAt.call(this, child, position);
if (child.id == this._selectedCaseId)
{
this._getCaseToggleTriggerByCaseId(this._selectedCaseId).fire();
}
else
{
childDomContainer.style.display = "none";
}
return childDomContainer;
},
render: function(attach_point)
{
alfresco.xforms.SwitchGroup.superclass.render.call(this, attach_point);
this._groupHeaderNode = document.createElement("div");
this._groupHeaderNode.setAttribute("id", this.id + "-groupHeaderNode");
this.domNode.appendChild(this._groupHeaderNode);
this._groupHeaderNode.style.backgroundColor = "orange";
var cases = this._getCaseToggleTriggers();
for (var i = 0; i < cases.length; i++)
{
var d = document.createElement("div");
this._groupHeaderNode.appendChild(d);
var link = document.createElement("a");
link.setAttribute("id", cases[i].id);
link.trigger = cases[i];
link.appendChild(document.createTextNode(cases[i].getLabel()));
d.appendChild(link);
dojo.event.browser.addListener(link,
"onclick",
function(event)
{
event.target.trigger.fire();
});
}
},
/////////////////////////////////////////////////////////////////
// XForms event handlers
/////////////////////////////////////////////////////////////////
/** */
handleSwitchToggled: function(selectedCaseId, deselectedCaseId)
{
dojo.debug(this.id + ".handleSwitchToggled(" + selectedCaseId +
", " + deselectedCaseId + ")");
this.selectedCaseId = selectedCaseId;
for (var i = 0; i < this._children.length; i++)
{
if (this._children[i].id == selectedCaseId)
{
this._children[i].domContainer.style.display = "block";
}
else if (this._children[i].id == deselectedCaseId)
{
this._children[i].domContainer.style.display = "none";
}
}
this._updateDisplay();
}
});
/** */
dojo.declare("alfresco.xforms.CaseGroup",
alfresco.xforms.Group,
function(xform, xformsNode)
{
},
{
});
/**
* Handles xforms widget xf:group for the root group. Does some special rendering
* to present a title rather than a group header.
*/
dojo.declare("alfresco.xforms.ViewRoot",
alfresco.xforms.Group,
function(xform, xformsNode)
{
this.focusedRepeat = null;
},
{
/////////////////////////////////////////////////////////////////
// overridden methods & properties
/////////////////////////////////////////////////////////////////
render: function(attach_point)
{
this.domNode.widget = this;
this.domNode.style.position = "relative";
this.domNode.style.width = "100%";
dojo.html.setClass(this.domNode, "xformsViewRoot");
this._groupHeaderNode = document.createElement("div");
this._groupHeaderNode.id = this.id + "-groupHeaderNode";
dojo.html.setClass(this._groupHeaderNode, "xformsViewRootHeader");
this.domNode.appendChild(this._groupHeaderNode);
var icon = document.createElement("img");
this._groupHeaderNode.appendChild(icon);
icon.setAttribute("src", alfresco_xforms_constants.WEBAPP_CONTEXT + "/images/icons/file_large.gif");
icon.align = "absmiddle";
icon.style.marginLeft = "5px";
icon.style.marginRight = "5px";
this._groupHeaderNode.appendChild(document.createTextNode(this.getLabel()));
attach_point.appendChild(this.domNode);
this.domNode.childContainerNode = document.createElement("div");
this.domNode.childContainerNode.setAttribute("id", this.id + "-childContainerNode");
this.domNode.childContainerNode.style.position = "relative";
this.domNode.childContainerNode.style.width = "100%";
return this.domNode;
},
/** */
getLabel: function()
{
var result = alfresco.xforms.ViewRoot.superclass.getLabel.call(this);
result += " " + alfresco_xforms_constants.FORM_INSTANCE_DATA_NAME;
return result;
}
});
/** A struct for providing repeat index data. */
alfresco.xforms.RepeatIndexData = function(repeat, index)
{
this.repeat = repeat;
this.index = index;
this.toString = function()
{
return "{" + this.repeat.id + " = " + this.index + "}";
};
}
/**
* Handles xforms widget xf:repeat.
*/
dojo.declare("alfresco.xforms.Repeat",
alfresco.xforms.Group,
function(xform, xformsNode)
{
this.repeatControls = [];
this._selectedIndex = -1;
this._locked = false;
},
{
/////////////////////////////////////////////////////////////////
// methods & properties
/////////////////////////////////////////////////////////////////
/**
* Indicates whether or not this repeat can insert more children based
* on the alf:maximum restriction.
*/
isInsertRepeatItemEnabled: function()
{
var maximum = this.xform.getBinding(this.xformsNode).maximum;
maximum = isNaN(maximum) ? Number.MAX_VALUE : maximum;
return this._children.length < maximum;
},
/**
* Indicates whether or not this repeat can removed children based
* on the alf:minimum restriction.
*/
isRemoveRepeatItemEnabled: function()
{
var minimum = this.xform.getBinding(this.xformsNode).minimum;
minimum = isNaN(minimum) ? this.isRequired() ? 1 : 0 : minimum;
return this._children.length > minimum;
},
/**
* Returns the currently selected index or -1 if this repeat has no repeat items.
*/
getSelectedIndex: function()
{
this._selectedIndex = Math.min(this._children.length, this._selectedIndex);
if (this._children.length == 0)
{
this._selectedIndex = -1;
}
return this._selectedIndex;
},
/**
* Helper function to locate the appropriate repeat item trigger for this repeat.
* This is done by locating all related widgets via binding, and selecting the
* Trigger who's action type is the type provided and where the properties
* provided are the same for that action. This approach is used rather than simply
* looking up the trigger by id since the id isn't known for nested repeats as
* chiba modifies them.
*/
_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].getActions()[type];
if (!action)
{
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);
},
/**
* Sets the currently selected child by calliing XFormsBean.setRepeatIndeces.
* If the child provided is null, the index is set to 0.
*/
setFocusedChild: function(child)
{
var oldFocusedRepeat = this.getViewRoot().focusedRepeat;
this.getViewRoot().focusedRepeat = this;
if (oldFocusedRepeat != null && oldFocusedRepeat != this)
{
if (!oldFocusedRepeat.isAncestorOf(this))
{
oldFocusedRepeat._selectedIndex = -1;
}
oldFocusedRepeat._updateDisplay();
}
var repeatIndices = this.getRepeatIndices();
if (!child)
{
repeatIndices.push(new alfresco.xforms.RepeatIndexData(this, 0));
this.xform.setRepeatIndeces(repeatIndices);
}
else
{
var index = this.getChildIndex(child);
if (index < 0)
{
throw new Error("unable to find child " + child.id + " in " + this.id);
}
repeatIndices.push(new alfresco.xforms.RepeatIndexData(this, index + 1));
// xforms repeat indexes are 1-based
this.xform.setRepeatIndeces(repeatIndices);
}
},
/**
* Calls swapRepeatItems on the XFormsBean which will produce the event log
* to insert and remove the appropriate repeat items.
*/
_swapChildren: function(fromIndex, toIndex)
{
dojo.debug(this.id + ".swapChildren(" + fromIndex + ", " + toIndex + ")");
var fromChild = this.getChildAt(fromIndex);
var toChild = this.getChildAt(toIndex);
this.xform.swapRepeatItems(fromChild, toChild);
var anim = dojo.lfx.html.fadeOut(fromChild.domContainer, 500);
anim.onEnd = function()
{
fromChild.domContainer.style.display = "none";
};
anim.play();
},
/**
* Updates the repeat controls by changing the opacity on the image based on
* whether or not the action is enabled.
*/
_updateRepeatControls: function()
{
var insertEnabled = this.isInsertRepeatItemEnabled();
var removeEnabled = this.isRemoveRepeatItemEnabled();
for (var i = 0; i < this.repeatControls.length; i++)
{
dojo.html.setOpacity(this.repeatControls[i].moveRepeatItemUpImage,
i == 0 ? .3 : 1);
dojo.html.setOpacity(this.repeatControls[i].moveRepeatItemDownImage,
i == this.repeatControls.length - 1 ? .3 : 1);
dojo.html.setOpacity(this.repeatControls[i].insertRepeatItemImage,
insertEnabled ? 1 : .3);
dojo.html.setOpacity(this.repeatControls[i].removeRepeatItemImage,
removeEnabled ? 1 : .3);
}
},
/////////////////////////////////////////////////////////////////
// overridden methods & properties
/////////////////////////////////////////////////////////////////
/** When debugging, insert the id into the label. */
getLabel: function()
{
var label = this.parent.getLabel();
if (djConfig.isDebug)
{
label += " [" + this.id + "]";
}
return label;
},
/** Overrides _insertChildAt in Group to provide repeater controls. */
_insertChildAt: function(child, position)
{
this.repeatControls.splice(position, 0, document.createElement("div"));
var images =
[
{ name: "insertRepeatItemImage", src: "plus", action: this._insertRepeatItemAfter_handler },
{ name: "moveRepeatItemUpImage", src: "arrow_up", action: this._moveRepeatItemUp_handler },
{ name: "moveRepeatItemDownImage", src: "arrow_down", action: this._moveRepeatItemDown_handler },
{ name: "removeRepeatItemImage", src: "minus", action: this._removeRepeatItem_handler }
];
var repeatControlsWidth = 0;
for (var i = 0; i < images.length; i++)
{
var img = document.createElement("img");
this.repeatControls[position][images[i].name] = img;
img.setAttribute("src", (alfresco_xforms_constants.WEBAPP_CONTEXT + "/images/icons/" +
images[i].src + ".gif"));
img.style.width = "16px";
img.style.height = "16px";
img.style.margin = "2px 5px 2px " + (i == 0 ? 5 : 0) + "px";
img.repeat = this;
repeatControlsWidth += (parseInt(img.style.width) +
dojo.html.getMargin(img).width);
this.repeatControls[position].appendChild(img);
dojo.event.connect(img, "onclick", this, images[i].action);
}
var result = alfresco.xforms.Repeat.superclass._insertChildAt.call(this, child, position);
child.repeat = this;
dojo.event.connect(result, "onclick", function(event)
{
child.repeat.setFocusedChild(child);
event.stopPropagation();
});
dojo.html.addClass(result, "xformsRepeatItem");
if (result.nextSibling)
{
result.parentNode.insertBefore(this.repeatControls[position],
result.nextSibling);
}
else
{
result.parentNode.appendChild(this.repeatControls[position]);
}
dojo.html.setClass(this.repeatControls[position], "xformsRepeatControls");
this.repeatControls[position].style.width = repeatControlsWidth + "px";
this.repeatControls[position].style.backgroundColor =
dojo.html.getStyle(result, "background-color");
result.style.paddingBottom = (.5 * this.repeatControls[position].offsetHeight) + "px";
this.repeatControls[position].style.top = -(.5 * (this.repeatControls[position].offsetHeight ) +
dojo.html.getPixelValue(result, "margin-bottom") +
dojo.html.getBorderExtent(result, "bottom")) + "px";
this.repeatControls[position].style.marginRight =
(.5 * result.offsetWidth -
.5 * this.repeatControls[position].offsetWidth) + "px";
this.repeatControls[position].style.marginLeft =
(.5 * result.offsetWidth -
.5 * this.repeatControls[position].offsetWidth) + "px";
return result;
},
/**
* Overrides _removeChildAt in Group to remove the repeat controls associated with
* the repeat item.
*/
_removeChildAt: function(position)
{
this.repeatControls[position].style.display = "none";
dojo.dom.removeChildren(this.repeatControls[position]);
dojo.dom.removeNode(this.repeatControls[position]);
this.repeatControls.splice(position, 1);
return alfresco.xforms.Repeat.superclass._removeChildAt.call(this, position);
},
/** Disables insert before. */
_childAdded: function(child)
{
dojo.html.setOpacity(this.headerInsertRepeatItemImage, .3);
this._updateRepeatControls();
},
/** Reenables insert before if there are no children left. */
_childRemoved: function(child)
{
if (this._children.length == 0)
{
dojo.html.setOpacity(this.headerInsertRepeatItemImage, 1);
}
this._updateRepeatControls();
},
render: function(attach_point)
{
this.domNode = alfresco.xforms.Repeat.superclass.render.call(this, attach_point);
dojo.html.addClass(this.domNode, "xformsRepeat");
// clear the border bottom for the group header since we'll be getting it
// from the repeat item border
this._groupHeaderNode.style.borderBottomWidth = "0px";
this._groupHeaderNode.repeat = this;
dojo.event.connect(this._groupHeaderNode, "onclick", function(event)
{
if (event.target == event.currentTarget)
{
event.currentTarget.repeat.setFocusedChild(null);
}
});
this.headerInsertRepeatItemImage = document.createElement("img");
this.headerInsertRepeatItemImage.repeat = this;
this._groupHeaderNode.appendChild(this.headerInsertRepeatItemImage);
this.headerInsertRepeatItemImage.setAttribute("src",
alfresco_xforms_constants.WEBAPP_CONTEXT +
"/images/icons/plus.gif");
this.headerInsertRepeatItemImage.style.width = "16px";
this.headerInsertRepeatItemImage.style.height = "16px";
this.headerInsertRepeatItemImage.align = "absmiddle";
this.headerInsertRepeatItemImage.style.marginLeft = "5px";
dojo.event.connect(this.headerInsertRepeatItemImage,
"onclick",
this,
this._headerInsertRepeatItemBefore_handler);
return this.domNode;
},
_updateDisplay: function()
{
alfresco.xforms.Repeat.superclass._updateDisplay.call(this);
if (this.getViewRoot().focusedRepeat != null &&
(this.getViewRoot().focusedRepeat == this ||
this.getViewRoot().focusedRepeat.isAncestorOf(this)))
{
if (!dojo.html.hasClass(this._groupHeaderNode, "xformsRepeatFocusedHeader"))
{
dojo.html.addClass(this._groupHeaderNode, "xformsRepeatFocusedHeader");
}
}
else if (dojo.html.hasClass(this._groupHeaderNode, "xformsRepeatFocusedHeader"))
{
dojo.html.removeClass(this._groupHeaderNode, "xformsRepeatFocusedHeader");
}
for (var i = 0; i < this._children.length; i++)
{
if (i + 1 == this.getSelectedIndex() && this.getViewRoot().focusedRepeat == this)
{
if (dojo.html.hasClass(this._children[i].domContainer, "xformsRowOdd"))
{
dojo.html.removeClass(this._children[i].domContainer, "xformsRowOdd");
}
if (dojo.html.hasClass(this._children[i].domContainer, "xformsRowEven"))
{
dojo.html.removeClass(this._children[i].domContainer, "xformsRowEven");
}
if (!dojo.html.hasClass(this._children[i].domContainer,"xformsRepeatItemSelected"))
{
dojo.html.addClass(this._children[i].domContainer, "xformsRepeatItemSelected");
}
}
else
{
if (dojo.html.hasClass(this._children[i].domContainer, "xformsRepeatItemSelected"))
{
dojo.html.removeClass(this._children[i].domContainer, "xformsRepeatItemSelected");
}
if (dojo.html.hasClass(this._children[i].domContainer, "xformsRow" + (i % 2 ? "Odd" : "Even")))
{
dojo.html.removeClass(this._children[i].domContainer, "xformsRow" + (i % 2 ? "Odd" : "Even"));
}
dojo.html.addClass(this._children[i].domContainer,
"xformsRow" + (i % 2 ? "Even" : "Odd"));
}
this.repeatControls[i].style.backgroundColor =
dojo.html.getStyle(this._children[i].domContainer, "background-color");
}
},
/////////////////////////////////////////////////////////////////
// DOM event handlers
/////////////////////////////////////////////////////////////////
/**
* Event handler for insert after. If insert is enabled, causes a setRepeatIndeces
* and an insert.
*/
_insertRepeatItemAfter_handler: function(event)
{
dojo.event.browser.stopEvent(event);
var repeat = event.target.repeat;
if (!repeat._locked && repeat.isInsertRepeatItemEnabled())
{
var index = repeat.repeatControls.indexOf(event.target.parentNode);
var repeatItem = repeat.getChildAt(index);
this.setFocusedChild(repeatItem);
var trigger = this._getRepeatItemTrigger("insert", { position: "after" });
trigger.fire();
repeat._locked = true;
}
},
/**
* Event handler for insert before. If insert is enabled, causes a setRepeatIndeces
* and an insert.
*/
_headerInsertRepeatItemBefore_handler: function(event)
{
if (this._children.length == 0)
{
dojo.event.browser.stopEvent(event);
var repeat = event.target.repeat;
if (!repeat._locked && repeat.isInsertRepeatItemEnabled())
{
this.setFocusedChild(null);
var trigger = this._getRepeatItemTrigger("insert", { position: "before" });
trigger.fire();
repeat._locked = true;
}
}
},
/**
* Event handler for remove. If remove is enabled, causes a setRepeatIndeces
* and an delete.
*/
_removeRepeatItem_handler: function(event)
{
dojo.event.browser.stopEvent(event);
var repeat = event.target.repeat;
if (!repeat._locked && repeat.isRemoveRepeatItemEnabled())
{
var index = repeat.repeatControls.indexOf(event.target.parentNode);
var repeatItem = repeat.getChildAt(index);
this.setFocusedChild(repeatItem);
var trigger = this._getRepeatItemTrigger("delete", {});
trigger.fire();
repeat._locked = true;
}
},
/**
* Event handler for move up. Calls swap children with the child before
* if the current select child is not the first child.
*/
_moveRepeatItemUp_handler: function(event)
{
dojo.event.browser.stopEvent(event);
var repeat = event.target.repeat;
var index = repeat.repeatControls.indexOf(event.target.parentNode);
if (!repeat._locked && index != 0 && repeat._children.length != 1)
{
var repeatItem = repeat.getChildAt(index);
this.setFocusedChild(repeatItem);
repeat._swapChildren(index, index - 1);
repeat._locked = true;
}
},
/**
* Event handler for move down. Calls swap children with the child after
* if the current select child is not the last child.
*/
_moveRepeatItemDown_handler: function(event)
{
dojo.event.browser.stopEvent(event);
var repeat = event.target.repeat;
var index = repeat.repeatControls.indexOf(event.target.parentNode);
if (!repeat._locked && index != repeat._children.length - 1 && repeat._children.length != 1)
{
var repeatItem = repeat.getChildAt(index);
this.setFocusedChild(repeatItem);
repeat._swapChildren(index, index + 1);
repeat._locked = true;
}
},
/////////////////////////////////////////////////////////////////
// XForms event handlers
/////////////////////////////////////////////////////////////////
/** Sets the selected index. */
handleIndexChanged: function(index)
{
dojo.debug(this.id + ".handleIndexChanged(" + index + ")");
this._selectedIndex = index;
this._updateDisplay();
},
/** Returns a clone of the specified prototype id. */
handlePrototypeCloned: function(prototypeId)
{
dojo.debug(this.id + ".handlePrototypeCloned("+ prototypeId +")");
var chibaData = _getElementsByTagNameNS(this.xformsNode,
alfresco_xforms_constants.CHIBA_NS,
alfresco_xforms_constants.CHIBA_PREFIX,
"data");
chibaData = chibaData[chibaData.length - 1];
dojo.debug(alfresco_xforms_constants.CHIBA_PREFIX + ":data == " + dojo.dom.innerXML(chibaData));
var prototypeToClone = dojo.dom.firstElement(chibaData);
if (prototypeToClone.getAttribute("id") != prototypeId)
{
throw new Error("unable to locate " + prototypeId +
" in " + this.id);
}
return prototypeToClone.cloneNode(true);
},
/** Inserts the clonedPrototype at the specified position. */
handleItemInserted: function(clonedPrototype, position)
{
dojo.debug(this.id + ".handleItemInserted(" + clonedPrototype.nodeName +
", " + position + ")");
var w = this.xform.createWidget(clonedPrototype);
this._insertChildAt(w, position);
this.xform.loadWidgets(w.xformsNode, w);
this._locked = false;
},
/** Deletes the item at the specified position. */
handleItemDeleted: function(position)
{
dojo.debug(this.id + ".handleItemDeleted(" + position + ")");
this._removeChildAt(position);
this._locked = false;
}
});
////////////////////////////////////////////////////////////////////////////////
// trigger widgets
////////////////////////////////////////////////////////////////////////////////
/**
* Handles xforms widget xf:trigger.
*/
dojo.declare("alfresco.xforms.Trigger",
alfresco.xforms.Widget,
function(xform, xformsNode)
{
},
{
/////////////////////////////////////////////////////////////////
// methods & properties
/////////////////////////////////////////////////////////////////
/** TODO: DOCUMENT */
getActions: function()
{
if (typeof this._actions == "undefined")
{
var actionNode = _getElementsByTagNameNS(this.xformsNode,
alfresco_xforms_constants.XFORMS_NS,
alfresco_xforms_constants.XFORMS_PREFIX,
"action")[0];
this._actions = {};
for (var i = 0; i < actionNode.childNodes.length; i++)
{
if (actionNode.childNodes[i].nodeType != dojo.dom.ELEMENT_NODE)
{
continue;
}
var a = new alfresco.xforms.XFormsAction(this.xform, actionNode.childNodes[i]);
this._actions[a.getType()] = a;
}
}
return this._actions;
},
/** fires the xforms action associated with the trigger */
fire: function()
{
this.xform.fireAction(this.id);
},
/////////////////////////////////////////////////////////////////
// overridden methods
/////////////////////////////////////////////////////////////////
isValidForSubmit: function()
{
return true;
},
isVisible: function()
{
return false;
},
render: function(attach_point)
{
var nodeRef = document.createElement("div");
attach_point.appendChild(nodeRef);
this.widget = dojo.widget.createWidget("Button",
{
widgetId: this.id + "-widget",
caption: this.getLabel() + " " + this.id
},
nodeRef);
dojo.event.connect(this.widget, "onClick", this, this._clickHandler);
this.domContainer.style.display = "none";
},
/////////////////////////////////////////////////////////////////
// DOM event handlers
/////////////////////////////////////////////////////////////////
_clickHandler: function(event)
{
this.fire();
}
});
/**
* Handles xforms widget xf:submit.
*/
dojo.declare("alfresco.xforms.Submit",
alfresco.xforms.Trigger,
function(xform, xformsNode)
{
var submit_buttons = (this.id == "submit"
? _xforms_getSubmitButtons()
: (this.id == "save-draft"
? _xforms_getSaveDraftButtons()
: null));
if (submit_buttons == null)
{
throw new Error("unknown submit button " + this.id);
}
for (var i = 0; i < submit_buttons.length; i++)
{
dojo.debug("adding submit handler for " + submit_buttons[i].getAttribute('id'));
submit_buttons[i].widget = this;
dojo.event.browser.addListener(submit_buttons[i],
"onclick",
this._submitButton_clickHandler,
false);
}
},
{
/////////////////////////////////////////////////////////////////
// DOM event handlers
/////////////////////////////////////////////////////////////////
_clickHandler: function(event)
{
this.done = false;
_hide_errors();
this.fire();
},
/** */
_submitButton_clickHandler: function(event)
{
if (!event.target.widget)
{
return true;
}
var xform = event.target.widget.xform;
if (xform.submitWidget && xform.submitWidget.done)
{
dojo.debug("done - doing base click on " + xform.submitWidget.currentButton.id);
xform.submitWidget.currentButton = null;
xform.submitWidget = null;
return true;
}
else
{
dojo.debug("triggering submit from handler " + event.target.id);
dojo.event.browser.stopEvent(event);
_hide_errors();
xform.submitWidget = event.target.widget;
xform.submitWidget.currentButton = event.target;
xform.submitWidget.widget.buttonClick();
return false;
}
}
});
/**
* A struct describing an xforms action block.
*/
dojo.declare("alfresco.xforms.XFormsAction",
null,
function(xform, xformsNode)
{
this.xform = xform;
this.xformsNode = xformsNode;
/** All properties of the action as map of key value pairs */
this.properties = [];
for (var i = 0; i < this.xformsNode.attributes.length; i++)
{
var attr = this.xformsNode.attributes[i];
if (attr.nodeName.match(new RegExp("^" + alfresco_xforms_constants.XFORMS_PREFIX + ":")))
{
this.properties[attr.nodeName.substring((alfresco_xforms_constants.XFORMS_PREFIX + ":").length)] =
attr.nodeValue;
}
}
if (this.getType() == "setvalue" && !this.properties["value"])
{
this.properties["value"] = this.xformsNode.firstChild.nodeValue;
}
},
{
/** Returns the action type. */
getType: function()
{
return this.xformsNode.nodeName.substring((alfresco_xforms_constants.XFORMS_PREFIX + ":").length);
}
});
////////////////////////////////////////////////////////////////////////////////
// xforms data model
////////////////////////////////////////////////////////////////////////////////
/**
* An xforms event. A log of events is returned by any xforms action and
* is used to update the UI appropriately.
*/
dojo.declare("alfresco.xforms.XFormsEvent",
null,
function(node)
{
this.type = node.nodeName;
this.targetId = node.getAttribute("targetId");
this.targetName = node.getAttribute("targetName");
this.properties = {};
for (var i = 0; i < node.childNodes.length; i++)
{
if (node.childNodes[i].nodeType == dojo.dom.ELEMENT_NODE)
{
this.properties[node.childNodes[i].getAttribute("name")] =
node.childNodes[i].getAttribute("value");
}
}
},
{
/** Returns the widget managing the specified target id. */
getTarget: function()
{
var targetDomNode = document.getElementById(this.targetId + "-content");
if (!targetDomNode)
{
throw new Error("unable to find node " + this.targetId + "-content");
}
return targetDomNode.widget;
}
});
/**
* A parsed xf:bind.
*/
dojo.declare("alfresco.xforms.Binding",
null,
function(xformsNode, parent)
{
this.xformsNode = xformsNode;
this.id = this.xformsNode.getAttribute("id");
this.nodeset = this.xformsNode.getAttribute(alfresco_xforms_constants.XFORMS_PREFIX + ":nodeset");
this._readonly =
(_hasAttribute(this.xformsNode, alfresco_xforms_constants.XFORMS_PREFIX + ":readonly")
? this.xformsNode.getAttribute(alfresco_xforms_constants.XFORMS_PREFIX + ":readonly") == "true()"
: null);
this._required =
(_hasAttribute(this.xformsNode, alfresco_xforms_constants.XFORMS_PREFIX + ":required")
? this.xformsNode.getAttribute(alfresco_xforms_constants.XFORMS_PREFIX + ":required") == "true()"
: null);
this._type =
(_hasAttribute(this.xformsNode, alfresco_xforms_constants.XFORMS_PREFIX + ":type")
? this.xformsNode.getAttribute(alfresco_xforms_constants.XFORMS_PREFIX + ":type")
: null);
this.constraint =
(_hasAttribute(this.xformsNode, alfresco_xforms_constants.XFORMS_PREFIX + ":constraint")
? this.xformsNode.getAttribute(alfresco_xforms_constants.XFORMS_PREFIX + ":constraint")
: null);
this.maximum = this.xformsNode.getAttribute(alfresco_xforms_constants.XFORMS_PREFIX + ":maxOccurs");
this.maximum = this.maximum == "unbounded" ? Number.MAX_VALUE : parseInt(this.maximum);
this.minimum = parseInt(this.xformsNode.getAttribute(alfresco_xforms_constants.XFORMS_PREFIX + ":minOccurs"));
this.parent = parent;
this.widgets = {};
},
{
/** Returns the expected schema type for this binding. */
getType: function()
{
return (this._type != null
? this._type
: (this.parent != null ? this.parent.getType() : null));
},
/** Returns true if a node bound by this binding has a readonly value */
isReadonly: function()
{
return (this._readonly != null ? this._readonly :
(this.parent != null ? this.parent.isReadonly() : false));
},
/** Returns true if a node bound by this binding has a required value */
isRequired: function()
{
return (this._required != null ? this._required :
(this.parent != null ? this.parent.isRequired() : false));
},
toString: function()
{
return ("{id: " + this.id +
",type: " + this.getType() +
",required: " + this.isRequired() +
",readonly: " + this.isReadonly() +
",nodeset: " + this.nodeset + "}");
}
});
/**
* Manages the xforms document.
*/
dojo.declare("alfresco.xforms.XForm",
null,
/** Makes a request to the XFormsBean to load the xforms document. */
function()
{
var req = AjaxHelper.createRequest(this,
"getXForm",
{},
function(type, data, evt)
{
this.target._loadHandler(data);
});
AjaxHelper.sendRequest(req);
},
{
/////////////////////////////////////////////////////////////////
// Initialization
/////////////////////////////////////////////////////////////////
/** Parses the xforms document and produces the widget tree. */
_loadHandler: function(xformDocument)
{
this.xformDocument = xformDocument;
this.xformsNode = xformDocument.documentElement;
this._bindings = this._loadBindings(this.getModel());
var bindings = this.getBindings();
for (var i = 0; false && i < bindings.length; i++)
{
dojo.debug("bindings[" + i + "]=" + bindings[i].id +
", parent = " + (bindings[i].parent
? bindings[i].parent.id
: 'null'));
}
var alfUI = document.getElementById(alfresco_xforms_constants.XFORMS_UI_DIV_ID);
alfUI.style.width = "100%";
var rootGroup = _getElementsByTagNameNS(this.getBody(),
alfresco_xforms_constants.XFORMS_NS,
alfresco_xforms_constants.XFORMS_PREFIX,
"group")[0];
this.rootWidget = new alfresco.xforms.ViewRoot(this, rootGroup);
this.rootWidget.render(alfUI);
this.loadWidgets(rootGroup, this.rootWidget);
// this.rootWidget._updateDisplay();
},
/** Creates the widget for the provided xforms node. */
createWidget: function(xformsNode)
{
dojo.debug("creating widget for " + xformsNode.nodeName.toLowerCase());
switch (xformsNode.nodeName.toLowerCase())
{
case alfresco_xforms_constants.XFORMS_PREFIX + ":group":
return new alfresco.xforms.Group(this, xformsNode);
case alfresco_xforms_constants.XFORMS_PREFIX + ":repeat":
return new alfresco.xforms.Repeat(this, xformsNode);
case alfresco_xforms_constants.XFORMS_PREFIX + ":textarea":
return (new alfresco.xforms.Widget(this, xformsNode).getAppearance() == "minimal"
? new alfresco.xforms.PlainTextEditor(this, xformsNode)
: new alfresco.xforms.RichTextEditor(this, xformsNode));
case alfresco_xforms_constants.XFORMS_PREFIX + ":upload":
return new alfresco.xforms.FilePicker(this, xformsNode);
case alfresco_xforms_constants.XFORMS_PREFIX + ":range":
return new alfresco.xforms.NumericalRange(this, xformsNode);
case alfresco_xforms_constants.XFORMS_PREFIX + ":input":
{
var type = this.getBinding(xformsNode).getType();
switch (type)
{
// date types
case "date":
return new alfresco.xforms.DatePicker(this, xformsNode);
case "time":
return new alfresco.xforms.TimePicker(this, xformsNode);
case "gMonth":
return new alfresco.xforms.MonthPicker(this, xformsNode);
case "gDay":
return new alfresco.xforms.DayPicker(this, xformsNode);
case "gYear":
return new alfresco.xforms.YearPicker(this, xformsNode);
case "gYearMonth":
return new alfresco.xforms.YearMonthPicker(this, xformsNode);
case "gMonthDay":
return new alfresco.xforms.MonthDayPicker(this, xformsNode);
case "dateTime":
return new alfresco.xforms.DateTimePicker(this, xformsNode);
case "yearMonthDuration":
case "dayTimeDuration":
// number types
case "byte":
case "double":
case "float":
case "int":
case "integer":
case "long":
case "negativeInteger":
case "nonNegativeInteger":
case "nonPositiveInteger":
case "short":
case "unsignedByte":
case "unsignedInt":
case "unsignedLong":
case "unsignedShort":
case "positiveInteger":
// string types
case "string":
case "normalizedString":
default:
return new alfresco.xforms.TextField(this, xformsNode);
}
}
case alfresco_xforms_constants.XFORMS_PREFIX + ":select":
return new alfresco.xforms.Select(this, xformsNode);
case alfresco_xforms_constants.XFORMS_PREFIX + ":select1":
return (this.getBinding(xformsNode).getType() == "boolean"
? new alfresco.xforms.Checkbox(this, xformsNode)
: new alfresco.xforms.Select1(this, xformsNode));
case alfresco_xforms_constants.XFORMS_PREFIX + ":submit":
return new alfresco.xforms.Submit(this, xformsNode);
case alfresco_xforms_constants.XFORMS_PREFIX + ":trigger":
return new alfresco.xforms.Trigger(this, xformsNode);
case alfresco_xforms_constants.CHIBA_PREFIX + ":data":
case alfresco_xforms_constants.XFORMS_PREFIX + ":label":
case alfresco_xforms_constants.XFORMS_PREFIX + ":alert":
{
dojo.debug("ignoring " + xformsNode.nodeName);
return null;
}
case alfresco_xforms_constants.XFORMS_PREFIX + ":switch":
return new alfresco.xforms.SwitchGroup(this, xformsNode);
case alfresco_xforms_constants.XFORMS_PREFIX + ":case":
return new alfresco.xforms.CaseGroup(this, xformsNode);
default:
throw new Error("unknown type " + xformsNode.nodeName);
}
},
/** Loads all widgets for the provided xforms node's children. */
loadWidgets: function(xformsNode, parentWidget)
{
for (var i = 0; i < xformsNode.childNodes.length; i++)
{
if (xformsNode.childNodes[i].nodeType != dojo.dom.ELEMENT_NODE)
{
continue;
}
dojo.debug("loading " + xformsNode.childNodes[i].nodeName +
" into " + parentWidget.id);
if (xformsNode.childNodes[i].getAttribute(alfresco_xforms_constants.ALFRESCO_PREFIX +
":prototype") == "true")
{
dojo.debug(xformsNode.childNodes[i].getAttribute("id") +
" is a prototype, ignoring");
continue;
}
var w = this.createWidget(xformsNode.childNodes[i]);
if (w != null)
{
dojo.debug("created " + w.id + " for " + xformsNode.childNodes[i].nodeName);
parentWidget.addChild(w);
if (w instanceof alfresco.xforms.Group)
{
this.loadWidgets(xformsNode.childNodes[i], w);
}
}
}
},
/** Loads all bindings from the xforms document. */
_loadBindings: function(bind, parent, result)
{
result = result || [];
for (var i = 0; i < bind.childNodes.length; i++)
{
if (bind.childNodes[i].nodeName.toLowerCase() ==
alfresco_xforms_constants.XFORMS_PREFIX + ":bind")
{
var b = new alfresco.xforms.Binding(bind.childNodes[i], parent);
result[b.id] = b;
dojo.debug("loaded binding " + b);
this._loadBindings(bind.childNodes[i], result[b.id], result);
}
}
return result;
},
/////////////////////////////////////////////////////////////////
// XForms model properties & methods
/////////////////////////////////////////////////////////////////
/** Returns the model section of the xforms document. */
getModel: function()
{
return _getElementsByTagNameNS(this.xformsNode,
alfresco_xforms_constants.XFORMS_NS,
alfresco_xforms_constants.XFORMS_PREFIX,
"model")[0];
},
/** Returns the instance section of the xforms document. */
getInstance: function()
{
var model = this.getModel();
return _getElementsByTagNameNS(model,
alfresco_xforms_constants.XFORMS_NS,
alfresco_xforms_constants.XFORMS_PREFIX,
"instance")[0];
},
/** Returns the body section of the xforms document. */
getBody: function()
{
var b = _getElementsByTagNameNS(this.xformsNode,
alfresco_xforms_constants.XHTML_NS,
alfresco_xforms_constants.XHTML_PREFIX,
"body");
return b[b.length - 1];
},
/** Returns the binding corresponding to the provided xforms node. */
getBinding: function(xformsNode)
{
return this._bindings[xformsNode.getAttribute(alfresco_xforms_constants.XFORMS_PREFIX + ":bind")];
},
/** Returns all parsed bindings. */
getBindings: function()
{
return this._bindings;
},
/////////////////////////////////////////////////////////////////
// XFormsBean interaction
/////////////////////////////////////////////////////////////////
/** swaps the specified repeat items by calling XFormsBean.swapRepeatItems. */
swapRepeatItems: function(fromChild, toChild)
{
var params =
{
fromItemId: fromChild.xformsNode.getAttribute("id"),
toItemId: toChild.xformsNode.getAttribute("id"),
instanceId: this.getInstance().getAttribute("id")
};
var req = AjaxHelper.createRequest(this,
"swapRepeatItems",
params,
function(type, data, event)
{
this.target._handleEventLog(data.documentElement)
});
AjaxHelper.sendRequest(req);
},
/** sets the repeat indexes by calling XFormsBean.setRepeatIndeces. */
setRepeatIndeces: function(repeatIndeces)
{
dojo.debug("setting repeat indeces [" + repeatIndeces.join(", ") + "]");
var params = { };
params["repeatIds"] = [];
for (var i = 0; i < repeatIndeces.length; i++)
{
params.repeatIds.push(repeatIndeces[i].repeat.id);
params[repeatIndeces[i].repeat.id] = repeatIndeces[i].index;
}
params.repeatIds = params.repeatIds.join(",");
var req = AjaxHelper.createRequest(this,
"setRepeatIndeces",
params,
function(type, data, evt)
{
this.target._handleEventLog(data.documentElement);
});
AjaxHelper.sendRequest(req);
},
/** Fires an action specified by the id by calling XFormsBean.fireAction. */
fireAction: function(id)
{
var req = AjaxHelper.createRequest(this,
"fireAction",
{ id: id },
function(type, data, evt)
{
dojo.debug("fireAction." + type);
this.target._handleEventLog(data.documentElement);
});
AjaxHelper.sendRequest(req);
},
/** Sets the value of the specified control id by calling XFormsBean.setXFormsValue. */
setXFormsValue: function(id, value)
{
value = value == null ? "" : value;
dojo.debug("setting value " + id + " = " + value);
var req = AjaxHelper.createRequest(this,
"setXFormsValue",
{ id: id, value: value },
function(type, data, evt)
{
this.target._handleEventLog(data.documentElement);
});
AjaxHelper.sendRequest(req);
},
/** Handles the xforms event log resulting from a call to the XFormsBean. */
_handleEventLog: function(events)
{
var prototypeClones = [];
var generatedIds = null;
for (var i = 0; i < events.childNodes.length; i++)
{
if (events.childNodes[i].nodeType != dojo.dom.ELEMENT_NODE)
{
continue;
}
var xfe = new alfresco.xforms.XFormsEvent(events.childNodes[i]);
dojo.debug("parsing " + xfe.type +
"(" + xfe.targetId + ", " + xfe.targetName + ")");
switch (xfe.type)
{
case "chiba-index-changed":
{
var index = Number(xfe.properties["index"]);
try
{
xfe.getTarget().handleIndexChanged(index);
}
catch (e)
{
dojo.debug(e);
}
break;
}
case "chiba-state-changed":
{
dojo.debug("handleStateChanged(" + xfe.targetId + ")");
xfe.getTarget().setModified(true);
if ("valid" in xfe.properties)
{
xfe.getTarget().setValid(xfe.properties["valid"] == "true");
}
if ("required" in xfe.properties)
{
xfe.getTarget().setRequired(xfe.properties["required"] == "true");
}
if ("readonly" in xfe.properties)
{
xfe.getTarget().setReadonly(xfe.properties["readonly"] == "true");
}
if ("enabled" in xfe.properties)
{
xfe.getTarget().setEnabled(xfe.properties["enabled"] == "true");
}
if ("value" in xfe.properties)
{
dojo.debug("setting " + xfe.getTarget().id + " = " + xfe.properties["value"]);
xfe.getTarget().setValue(xfe.properties["value"]);
}
break;
}
case "chiba-prototype-cloned":
{
var prototypeId = xfe.properties["prototypeId"];
var originalId = xfe.properties["originalId"];
dojo.debug("handlePrototypeCloned(" + xfe.targetId +
", " + originalId +
", " + prototypeId + ")");
var clone = null;
var prototypeNode = _findElementById(this.xformsNode, prototypeId);
if (prototypeNode)
{
dojo.debug("cloning prototype " + prototypeNode.getAttribute("id"));
clone = prototypeNode.cloneNode(true);
}
else
{
dojo.debug("cloning prototype " + originalId);
var prototypeNode = _findElementById(this.xformsNode, originalId);
//clone = prototypeNode.cloneNode(true);
clone = prototypeNode.ownerDocument.createElement(alfresco_xforms_constants.XFORMS_PREFIX + ":group");
clone.setAttribute(alfresco_xforms_constants.XFORMS_PREFIX + ":appearance", "repeated");
for (var j = 0; j < prototypeNode.childNodes.length; j++)
{
clone.appendChild(prototypeNode.childNodes[j].cloneNode(true));
}
clone.setAttribute("id", prototypeId);
}
if (clone == null)
{
throw new Error("unable to clone prototype " + prototypeId);
}
dojo.debug("created clone " + clone.getAttribute("id") +
" nodeName " + clone.nodeName +
" parentClone " + (prototypeClones.length != 0
? prototypeClones.peek().getAttribute("id")
: null));
prototypeClones.push(clone);
break;
}
case "chiba-id-generated":
{
var originalId = xfe.properties["originalId"];
dojo.debug("handleIdGenerated(" + xfe.targetId + ", " + originalId + ")");
var node = _findElementById(prototypeClones.peek(), originalId);
if (!node)
{
throw new Error("unable to find " + originalId +
" in clone " + dojo.dom.innerXML(clone));
}
dojo.debug("applying id " + xfe.targetId +
" to " + node.nodeName + "(" + originalId + ")");
node.setAttribute("id", xfe.targetId);
generatedIds = generatedIds || new Object();
generatedIds[xfe.targetId] = originalId;
if (prototypeClones.length != 1)
{
var e = _findElementById(prototypeClones[prototypeClones.length - 2], originalId);
if (e)
{
e.setAttribute(alfresco_xforms_constants.ALFRESCO_PREFIX + ":prototype", "true");
}
}
break;
}
case "chiba-item-inserted":
{
var position = Number(xfe.properties["position"]) - 1;
var originalId = xfe.properties["originalId"];
var clone = prototypeClones.pop();
// walk all nodes of the clone and ensure that they have generated ids.
// those that do not are nested repeats that should not be added
dojo.lang.assert(clone.getAttribute("id") in generatedIds,
"expected clone id " + clone.getAttribute("id") +
" to be a generated id");
function _removeNonGeneratedChildNodes(node, ids)
{
var child = node.firstChild;
while (child)
{
var next = child.nextSibling;
if (child.nodeType == dojo.dom.ELEMENT_NODE)
{
if (child.getAttribute("id") in ids)
{
_removeNonGeneratedChildNodes(child, ids);
}
else
{
node.removeChild(child);
}
}
child = next;
}
};
_removeNonGeneratedChildNodes(clone, generatedIds);
if (prototypeClones.length != 0)
{
dojo.debug("using parentClone " + prototypeClones.peek().getAttribute("id") +
" of " + clone.getAttribute("id"));
var parentRepeat = _findElementById(prototypeClones.peek(), xfe.targetId);
parentRepeat.appendChild(clone);
}
else
{
dojo.debug("no parentClone found, directly insert " + clone.getAttribute("id") +
" on " + xfe.targetId);
xfe.getTarget().handleItemInserted(clone, position);
}
break;
}
case "chiba-item-deleted":
{
var position = Number(xfe.properties["position"]) - 1;
xfe.getTarget().handleItemDeleted(position);
break;
}
case "chiba-replace-all":
{
if (this.submitWidget)
{
this.submitWidget.done = true;
this.submitWidget.currentButton.click();
}
break;
}
case "chiba-switch-toggled":
{
var switchElement = xfe.getTarget();
switchElement.handleSwitchToggled(xfe.properties["selected"],
xfe.properties["deselected"]);
}
case "xforms-valid":
{
xfe.getTarget().setValid(true);
xfe.getTarget().setModified(true);
break;
}
case "xforms-invalid":
{
xfe.getTarget().setValid(false);
xfe.getTarget().setModified(true);
break;
}
case "xforms-required":
{
xfe.getTarget().setRequired(true);
break;
}
case "xforms-optional":
{
xfe.getTarget().setRequired(false);
break;
}
case "xforms-submit-error":
{
this.submitWidget = null;
var invalid = this.rootWidget.getWidgetsInvalidForSubmit();
_show_error(document.createTextNode(alfresco_xforms_constants.resources["validation_provide_values_for_required_fields"]));
var error_list = document.createElement("ul");
for (var j = 0; j < invalid.length; j++)
{
var error_item = document.createElement("li");
error_item.appendChild(document.createTextNode(invalid[j].getAlert()));
error_list.appendChild(error_item);
invalid[j].showAlert();
}
_show_error(error_list);
break;
}
default:
{
dojo.debug("unhandled event " + events.childNodes[i].nodeName);
}
}
}
}
});
////////////////////////////////////////////////////////////////////////////////
// error message display management
////////////////////////////////////////////////////////////////////////////////
/** hides the error message display. */
function _hide_errors()
{
var errorDiv = document.getElementById(alfresco_xforms_constants.XFORMS_ERROR_DIV_ID);
if (errorDiv)
{
dojo.dom.removeChildren(errorDiv);
errorDiv.style.display = "none";
}
}
/** shows the error message display. */
function _show_error(msg)
{
var errorDiv = document.getElementById(alfresco_xforms_constants.XFORMS_ERROR_DIV_ID);
if (!errorDiv)
{
errorDiv = document.createElement("div");
errorDiv.setAttribute("id", alfresco_xforms_constants.XFORMS_ERROR_DIV_ID);
dojo.html.setClass(errorDiv, "infoText statusErrorText xformsError");
var alfUI = document.getElementById(alfresco_xforms_constants.XFORMS_UI_DIV_ID);
dojo.dom.prependChild(errorDiv, alfUI);
}
if (errorDiv.style.display == "block")
{
errorDiv.appendChild(document.createElement("br"));
}
else
{
errorDiv.style.display = "block";
}
errorDiv.appendChild(msg);
}
////////////////////////////////////////////////////////////////////////////////
// AJAX helpers
////////////////////////////////////////////////////////////////////////////////
function AjaxHelper()
{
}
/** Creates an ajax request object. */
AjaxHelper.createRequest = function(target, serverMethod, methodArgs, load, error)
{
var result = new dojo.io.Request(alfresco_xforms_constants.WEBAPP_CONTEXT +
"/ajax/invoke/XFormsBean." + serverMethod,
"text/xml");
result.target = target;
result.content = methodArgs;
result.method = "POST";
result.load = load;
dojo.event.connect(result, "load", function(type, data, evt)
{
AjaxHelper._loadHandler(result);
});
result.error = error || function(type, e, impl)
{
dojo.debug("error [" + type + "] " + e.message);
if (impl.status == 401)
{
document.getElementById("logout").onclick();
}
else
{
_show_error(document.createTextNode(e.message));
AjaxHelper._loadHandler(this);
}
};
return result;
}
/** Sends an ajax request object. */
AjaxHelper.sendRequest = function(req)
{
AjaxHelper._sendHandler(req);
req.encoding = "utf-8";
dojo.io.queueBind(req);
}
/**
* Returns the ajax loader div element. If it hasn't yet been created, it is created.
*/
AjaxHelper._getLoaderElement = function()
{
var result = document.getElementById(alfresco_xforms_constants.AJAX_LOADER_DIV_ID);
if (result)
{
return result;
}
result = document.createElement("div");
result.setAttribute("id", alfresco_xforms_constants.AJAX_LOADER_DIV_ID);
dojo.html.setClass(result, "xformsAjaxLoader");
dojo.html.hide(result);
document.body.appendChild(result);
return result;
}
/** All pending ajax requests. */
AjaxHelper._requests = [];
/** Updates the loader message or hides it if nothing is being loaded. */
AjaxHelper._updateLoaderDisplay = function()
{
var ajaxLoader = AjaxHelper._getLoaderElement();
ajaxLoader.innerHTML = (AjaxHelper._requests.length == 0
? alfresco_xforms_constants.resources["idle"]
: (alfresco_xforms_constants.resources["loading"] +
(AjaxHelper._requests.length > 1
? " (" + AjaxHelper._requests.length + ")"
: "...")));
dojo.debug(ajaxLoader.innerHTML);
if (/* djConfig.isDebug && */ AjaxHelper._requests.length != 0)
{
dojo.html.show(ajaxLoader);
}
else
{
dojo.html.hide(ajaxLoader);
}
}
////////////////////////////////////////////////////////////////////////////////
// ajax event handlers
////////////////////////////////////////////////////////////////////////////////
AjaxHelper._sendHandler = function(req)
{
AjaxHelper._requests.push(req);
AjaxHelper._updateLoaderDisplay();
}
AjaxHelper._loadHandler = function(req)
{
var index = AjaxHelper._requests.indexOf(req);
if (index != -1)
{
AjaxHelper._requests.splice(index, 1);
}
else
{
var urls = [];
for (var i = 0; i < AjaxHelper._requests.length; i++)
{
urls.push(AjaxHelper._requests[i].url);
}
throw new Error("unable to find " + req.url +
" in [" + urls.join(", ") + "]");
}
AjaxHelper._updateLoaderDisplay();
}
////////////////////////////////////////////////////////////////////////////////
// DOM utilities
////////////////////////////////////////////////////////////////////////////////
function _findElementById(node, id)
{
// dojo.debug("looking for " + id +
// " in " + (node ? node.nodeName : null) +
// "(" + (node ? node.getAttribute("id") : null) + ")");
if (node.getAttribute("id") == id)
{
return node;
}
for (var i = 0; i < node.childNodes.length; i++)
{
if (node.childNodes[i].nodeType == dojo.dom.ELEMENT_NODE)
{
var n = _findElementById(node.childNodes[i], id);
if (n)
{
return n;
}
}
}
return null;
}
function _hasAttribute(node, name)
{
return (node == null
? false
: (node.hasAttribute
? node.hasAttribute(name)
: node.getAttribute(name) != null));
}
function _getElementsByTagNameNS(parentNode, ns, nsPrefix, tagName)
{
return (parentNode.getElementsByTagNameNS
? parentNode.getElementsByTagNameNS(ns, tagName)
: parentNode.getElementsByTagName(nsPrefix + ":" + tagName));
}
////////////////////////////////////////////////////////////////////////////////
// XPath wrapper
////////////////////////////////////////////////////////////////////////////////
function _evaluateXPath(xpath, contextNode, result_type)
{
var xmlDocument = contextNode.ownerDocument;
dojo.debug("evaluating xpath " + xpath +
" on node " + contextNode.nodeName +
" in document " + xmlDocument);
var result = null;
if (xmlDocument.evaluate)
{
var nsResolver = (xmlDocument.createNSResolver
? xmlDocument.createNSResolver(xmlDocument.documentElement)
: null);
result = xmlDocument.evaluate(xpath,
contextNode,
nsResolver,
result_type,
null);
if (result)
{
switch (result_type)
{
case XPathResult.FIRST_ORDERED_NODE_TYPE:
result = result.singleNodeValue;
break;
case XPathResult.BOOLEAN_TYPE:
result = result.booleanValue;
break;
case XPathResult.STRING_TYPE:
result = result.stringValue;
break;
}
}
}
else
{
xmlDocument.setProperty("SelectionLanguage", "XPath");
var namespaces = [];
for (var i = 0; i < xmlDocument.documentElement.attributes.length; i++)
{
var attr = xmlDocument.documentElement.attributes[i];
if (attr.nodeName.match(/^xmlns:/))
{
namespaces.push(attr.nodeName + "=\'" + attr.nodeValue + "\'");
}
}
dojo.debug("using namespaces " + namespaces.join(","));
xmlDocument.setProperty("SelectionNamespaces", namespaces.join(' '));
if (result_type == XPathResult.FIRST_ORDERED_NODE_TYPE)
{
result = xmlDocument.selectSingleNode(xpath);
}
else if (result_type == XPathResult.BOOLEAN_TYPE)
{
result = true;
}
}
dojo.debug("resolved xpath " + xpath + " to " + result);
return result;
}
if (!XPathResult)
{
var XPathResult = {};
XPathResult.ANY_TYPE = 0;
XPathResult.NUMBER_TYPE = 1;
XPathResult.STRING_TYPE = 2;
XPathResult.BOOEAN_TYPE = 3;
XPathResult.FIRST_ORDERED_NODE_TYPE = 9;
}
if (!Array.prototype.indexOf)
{
Array.prototype.indexOf = function(o)
{
for (var i = 0; i < this.length; i++)
{
if (this[i] == o)
{
return i;
}
}
return -1;
}
}
if (!Array.prototype.peek)
{
Array.prototype.peek = function(o)
{
return this[this.length - 1];
}
}
////////////////////////////////////////////////////////////////////////////////
// Custom widget implementations.
////////////////////////////////////////////////////////////////////////////////
/**
* The file picker widget.
*/
function FilePickerWidget(uploadId, node, value, readonly, change_callback, resize_callback)
{
this.uploadId = uploadId;
this.node = node;
this.value = value == null || value.length == 0 ? null : value;
this.readonly = readonly || false;
this.change_callback = change_callback;
this.resize_callback = resize_callback;
}
// static methods and properties
FilePickerWidget._uploads = [];
FilePickerWidget._handleUpload = function(id, fileInput, webappRelativePath, widget)
{
FilePickerWidget._uploads[id] =
{
widget:widget,
path: fileInput.value,
webappRelativePath: webappRelativePath
};
handle_upload_helper(fileInput,
id,
FilePickerWidget._upload_completeHandler,
alfresco_xforms_constants.WEBAPP_CONTEXT,
"/ajax/invoke/XFormsBean.uploadFile",
{ currentPath: webappRelativePath });
}
FilePickerWidget._upload_completeHandler = function(id, path, fileName, fileTypeImage, error)
{
var upload = FilePickerWidget._uploads[id];
upload.widget._upload_completeHandler(fileName,
upload.webappRelativePath,
fileTypeImage,
error);
}
// instance methods and properties
FilePickerWidget.prototype = {
getValue: function()
{
return this.value;
},
setValue: function(v)
{
this.value = (v == null || v.length == 0 ? null : v);
if (this.selectedPathInput)
{
this.selectedPathInput.value = v;
}
this.change_callback(this);
},
setReadonly: function(r)
{
this.readonly = r;
if (this.selectButton)
{
this.selectButton.disabled = this.readonly;
}
else if (this.readonly)
{
this._showSelectedValue();
}
},
render: function()
{
this._showSelectedValue();
},
_showStatus: function(text, isError)
{
var d = this.node.ownerDocument;
if (!this.statusDiv || !this.statusDiv.parentNode)
{
this.statusDiv = d.createElement("div");
this.statusDiv.setAttribute("id", this.uploadId + "-status");
this.statusDiv.widget = this;
this.node.insertBefore(this.statusDiv, this.node.firstChild);
dojo.html.setClass(this.statusDiv, "infoText xformsFilePickerStatus");
if (isError)
{
dojo.html.addClass(this.statusDiv, "statusErrorText");
}
this.statusDiv.appendChild(d.createTextNode(text));
this.node.style.height = (parseInt(this.node.style.height) +
dojo.html.getMargin(this.statusDiv).height +
this.statusDiv.offsetHeight) + "px";
this.resize_callback(this);
}
else
{
this.statusDiv.firstChild.nodeValue = text;
}
setTimeout("var _status = document.getElementById('" + this.uploadId +
"-status'); if (_status && _status) { _status.widget._hideStatus(); }", 5000);
},
_hideStatus: function()
{
if (this.statusDiv)
{
var anim = dojo.lfx.html.fadeOut(this.statusDiv, 500);
var _fp_widget = this;
anim.onEnd = function()
{
if (_fp_widget.statusDiv && _fp_widget.statusDiv.parentNode)
{
_fp_widget.node.style.height = (parseInt(_fp_widget.node.style.height) -
_fp_widget.statusDiv.offsetHeight) + "px";
dojo.dom.removeChildren(_fp_widget.statusDiv);
dojo.dom.removeNode(_fp_widget.statusDiv);
_fp_widget.resize_callback(_fp_widget);
_fp_widget.statusDiv = null;
}
};
anim.play();
}
},
_showSelectedValue: function()
{
var d = this.node.ownerDocument;
dojo.dom.removeChildren(this.node);
this.statusDiv = null;
this.contentDiv = null;
this.addContentDiv = null;
this.node.style.height = "20px";
this.node.style.lineHeight = this.node.style.height;
this.node.style.position = "relative";
this.node.style.whiteSpace = "nowrap";
this.resize_callback(this);
this.selectedPathInput = d.createElement("input");
this.selectedPathInput.type = "text";
this.selectedPathInput.value = this.value == null ? "" : this.value;
this.node.appendChild(this.selectedPathInput);
dojo.event.connect(this.selectedPathInput, "onblur", this, this._selectPathInput_changeHandler);
this.selectButton = d.createElement("input");
this.selectButton.filePickerWidget = this;
this.selectButton.type = "button";
this.selectButton.value = this.value == null ? "Select" : "Change";
this.selectButton.disabled = this.readonly;
this.selectButton.style.margin = "0px 10px 0px 10px";
this.node.appendChild(this.selectButton);
this.selectedPathInput.style.width = (1 -
((this.selectButton.offsetWidth +
dojo.html.getMargin(this.selectButton).width) /
dojo.html.getContentBox(this.node).width)) * 100 + "%";
dojo.event.browser.addListener(this.selectButton,
"click",
function(event)
{
var w = event.target.filePickerWidget;
w._navigateToNode(w.getValue() || "");
});
},
_selectPathInput_changeHandler: function(event)
{
this.setValue(event.target.value);
},
_navigateToNode: function(path)
{
var req = AjaxHelper.createRequest(this,
"getFilePickerData",
{},
function(type, data, evt)
{
this.target._showPicker(data.documentElement);
});
req.content.currentPath = path;
AjaxHelper.sendRequest(req);
},
_showPicker: function(data)
{
while (this.node.hasChildNodes() &&
this.node.lastChild != this.statusDiv)
{
this.node.removeChild(this.node.lastChild);
}
var d = this.node.ownerDocument;
this.node.style.height = (200 +
(this.statusDiv
? (parseInt(this.statusDiv.style.height) +
parseInt(this.statusDiv.style.marginTop) +
parseInt(this.statusDiv.style.marginBottom))
: 0) + "px");
this.resize_callback(this);
var currentPath = data.getElementsByTagName("current-node")[0];
currentPath = currentPath.getAttribute("webappRelativePath");
var currentPathName = currentPath.replace(/.*\/([^/]+)/, "$1")
var headerDiv = d.createElement("div");
dojo.html.setClass(headerDiv, "xformsFilePickerHeader");
this.node.appendChild(headerDiv);
headerDiv.appendChild(d.createTextNode("In: "));
this.headerMenuTriggerLink = d.createElement("a");
this.headerMenuTriggerLink.filePickerWidget = this;
this.headerMenuTriggerLink.style.textDecoration = "none";
this.headerMenuTriggerLink.setAttribute("href", "javascript:void(0)");
this.headerMenuTriggerLink.setAttribute("webappRelativePath", currentPath);
dojo.html.setClass(this.headerMenuTriggerLink, "xformsFilePickerHeaderMenuTrigger");
headerDiv.appendChild(this.headerMenuTriggerLink);
dojo.event.connect(this.headerMenuTriggerLink,
"onmouseover",
function(event)
{
event.currentTarget.style.backgroundColor = "#fefefe";
event.currentTarget.style.borderStyle = "inset";
});
dojo.event.connect(this.headerMenuTriggerLink,
"onmouseout",
function(event)
{
var w = event.currentTarget.filePickerWidget;
if (!w.parentPathMenu)
{
event.currentTarget.style.backgroundColor =
event.currentTarget.parentNode.style.backgroundColor;
event.currentTarget.style.borderStyle = "solid";
}
});
dojo.event.connect(this.headerMenuTriggerLink,
"onclick",
function(event)
{
var t = event.currentTarget;
var w = t.filePickerWidget;
if (w.parentPathMenu)
{
w._closeParentPathMenu();
}
else
{
w._openParentPathMenu(t, t.getAttribute("webappRelativePath"));
}
});
this.headerMenuTriggerLink.appendChild(d.createTextNode(currentPathName));
headerMenuTriggerImage = d.createElement("img");
this.headerMenuTriggerLink.appendChild(headerMenuTriggerImage);
this.headerMenuTriggerLink.image = headerMenuTriggerImage;
headerMenuTriggerImage.setAttribute("src", alfresco_xforms_constants.WEBAPP_CONTEXT + "/images/icons/menu.gif");
headerMenuTriggerImage.style.borderWidth = "0px";
headerMenuTriggerImage.style.marginLeft = "4px";
headerMenuTriggerImage.align = "absmiddle";
var headerRightDiv = d.createElement("div");
var addContentLink = d.createElement("a");
headerRightDiv.appendChild(addContentLink);
addContentLink.setAttribute("webappRelativePath", currentPath);
addContentLink.filePickerWidget = this;
addContentLink.setAttribute("href", "javascript:void(0)");
dojo.event.connect(addContentLink,
"onclick",
function(event)
{
var w = event.target.filePickerWidget;
if (w.addContentDiv)
{
w._hideAddContent();
}
else
{
w._showAddContent(event.target.getAttribute("webappRelativePath"));
}
});
var addContentImage = d.createElement("img");
addContentImage.style.borderWidth = "0px";
addContentImage.style.margin = "0px 2px 0px 2px";
addContentImage.align = "absmiddle";
addContentImage.setAttribute("src", alfresco_xforms_constants.WEBAPP_CONTEXT + "/images/icons/add.gif");
addContentLink.appendChild(addContentImage);
addContentLink.appendChild(d.createTextNode(alfresco_xforms_constants.resources["add_content"]));
var navigateToParentLink = d.createElement("a");
headerRightDiv.appendChild(navigateToParentLink);
navigateToParentLink.setAttribute("webappRelativePath", currentPath);
navigateToParentLink.filePickerWidget = this;
navigateToParentLink.setAttribute("href", "javascript:void(0)");
if (currentPathName != "/")
{
dojo.event.connect(navigateToParentLink,
"onclick",
function(event)
{
var w = event.target.filePickerWidget;
var parentPath = event.target.getAttribute("webappRelativePath");
parentPath = (parentPath.lastIndexOf("/") == 0
? "/"
: parentPath.substring(0, parentPath.lastIndexOf("/")));
w._navigateToNode(parentPath);
});
}
var navigateToParentNodeImage = d.createElement("img");
navigateToParentNodeImage.style.borderWidth = "0px";
dojo.html.setOpacity(navigateToParentNodeImage, (currentPathName == "/" ? .3 : 1));
navigateToParentNodeImage.style.margin = "0px 2px 0px 2px";
navigateToParentNodeImage.align = "absmiddle";
navigateToParentNodeImage.setAttribute("src", alfresco_xforms_constants.WEBAPP_CONTEXT + "/images/icons/up.gif");
navigateToParentLink.appendChild(navigateToParentNodeImage);
navigateToParentLink.appendChild(d.createTextNode(alfresco_xforms_constants.resources["go_up"]));
headerRightDiv.style.position = "absolute";
headerRightDiv.style.height = headerDiv.style.height;
headerRightDiv.style.lineHeight = headerRightDiv.style.height;
headerRightDiv.style.top = "0px";
headerRightDiv.style.right = "0px";
headerRightDiv.style.paddingRight = "2px";
headerDiv.appendChild(headerRightDiv);
this.contentDiv = d.createElement("div");
dojo.html.setClass(this.contentDiv, "xformsFilePickerFileList");
this.node.appendChild(this.contentDiv);
var footerDiv = d.createElement("div");
dojo.html.setClass(footerDiv, "xformsFilePickerFooter");
this.node.appendChild(footerDiv);
var cancelButton = d.createElement("input");
cancelButton.type = "button";
cancelButton.filePickerWidget = this;
cancelButton.value = alfresco_xforms_constants.resources["cancel"];
footerDiv.appendChild(cancelButton);
cancelButton.style.margin = ((.5 * footerDiv.offsetHeight) -
(.5 * cancelButton.offsetHeight)) + "px 0px";
dojo.event.connect(cancelButton, "onclick", function(event)
{
var w = event.target.filePickerWidget;
w._showSelectedValue();
});
this.contentDiv.style.height = (this.node.offsetHeight -
(this.statusDiv ? this.statusDiv.offsetHeight : 0) -
footerDiv.offsetHeight -
headerDiv.offsetHeight - 10) + "px";
var childNodes = data.getElementsByTagName("child-node");
for (var i = 0; i < childNodes.length; i++)
{
if (childNodes[i].nodeType != dojo.dom.ELEMENT_NODE)
{
continue;
}
var webappRelativePath = childNodes[i].getAttribute("webappRelativePath");
var fileName = webappRelativePath.replace(/.*\/([^/]+)/, "$1");
var row = this._createRow(fileName,
webappRelativePath,
childNodes[i].getAttribute("type") == "directory",
childNodes[i].getAttribute("image"),
"xformsRow" + (i % 2 ? "Even" : "Odd"));
this.contentDiv.appendChild(row);
}
},
_createRow: function(fileName, webappRelativePath, isDirectory, fileTypeImage, rowClass)
{
var d = this.contentDiv.ownerDocument;
var result = d.createElement("div");
result.setAttribute("id", fileName + "-row");
dojo.html.setClass(result, "xformsFilePickerRow " + rowClass);
dojo.event.browser.addListener(result,
"mouseover",
function(event)
{
var prevHover = event.currentTarget.parentNode.hoverNode;
if (prevHover)
{
dojo.html.removeClass(prevHover, "xformsRowHover");
}
event.currentTarget.parentNode.hoverNode = event.currentTarget;
dojo.html.addClass(event.currentTarget, "xformsRowHover");
},
true);
dojo.event.browser.addListener(result,
"mouseout",
function(event)
{
if (event.relatedTarget &&
event.relatedTarget.parentNode == event.currentTarget)
{
return true;
}
dojo.html.removeClass(event.currentTarget, "xformsRowHover");
},
true);
var e = d.createElement("img");
e.align = "absmiddle";
e.style.margin = "0px 4px 0px 4px";
e.setAttribute("src", alfresco_xforms_constants.WEBAPP_CONTEXT + fileTypeImage);
result.appendChild(e);
if (isDirectory)
{
e = d.createElement("a");
e.filePickerWidget = this;
e.setAttribute("href", "javascript:void(0)");
e.setAttribute("webappRelativePath", webappRelativePath);
dojo.event.connect(e, "onclick", function(event)
{
var w = event.target.filePickerWidget;
w._navigateToNode(event.target.getAttribute("webappRelativePath"));
return true;
});
e.appendChild(d.createTextNode(fileName));
result.appendChild(e);
}
else
{
result.appendChild(d.createTextNode(fileName));
}
e = d.createElement("input");
e.filePickerWidget = this;
e.type = "button";
e.name = webappRelativePath;
e.value = "Select";
result.appendChild(e);
e.style.position = "absolute";
e.style.right = "10px";
e.style.top = (.5 * result.offsetHeight) - (.5 * e.offsetHeight) + "px";
dojo.event.connect(e, "onclick", function(event)
{
var w = event.target.filePickerWidget;
w.setValue(event.target.name);
w._showSelectedValue();
});
return result;
},
_hideAddContent: function()
{
if (this.addContentDiv)
{
dojo.dom.removeChildren(this.addContentDiv);
dojo.dom.removeNode(this.addContentDiv);
this.addContentDiv = null;
}
},
_showAddContent: function(currentPath)
{
if (this.addContentDiv)
{
return;
}
var d = this.node.ownerDocument;
this.addContentDiv = d.createElement("div");
dojo.html.setClass(this.addContentDiv, "xformsFilePickerAddContent");
if (this.contentDiv.firstChild)
{
this.contentDiv.insertBefore(this.addContentDiv, this.contentDiv.firstChild);
}
else
{
this.contentDiv.appendChild(this.addContentDiv);
}
var e = d.createElement("div");
e.style.marginLeft = "4px";
this.addContentDiv.appendChild(e);
e.appendChild(d.createTextNode("Upload: "));
var fileInputDiv = d.createElement("div");
this.addContentDiv.appendChild(fileInputDiv);
var fileInput = d.createElement("input");
fileInput.type = "file";
fileInput.widget = this;
fileInput.name = this.uploadId + "_file_input";
fileInput.size = "35";
fileInput.setAttribute("webappRelativePath", currentPath);
fileInputDiv.appendChild(fileInput);
fileInputDiv.style.position = "absolute";
fileInputDiv.style.right = "10px";
fileInputDiv.style.top = (.5 * this.addContentDiv.offsetHeight) - (.5 * fileInputDiv.offsetHeight) + "px";
dojo.event.connect(fileInput,
"onchange",
function(event)
{
var w = event.target.widget;
if (w.addContentDiv)
{
var d = w.addContentDiv.ownerDocument;
dojo.dom.removeChildren(w.addContentDiv);
var fileName = event.target.value.replace(/.*[\/\\]([^\/\\]+)/, "$1");
w.addContentDiv.appendChild(d.createTextNode(alfresco_xforms_constants.resources["upload"] + ": " + fileName));
var img = d.createElement("img");
img.setAttribute("src", alfresco_xforms_constants.WEBAPP_CONTEXT +
"/images/icons/process_animation.gif");
img.style.position = "absolute";
img.style.right = "10px";
img.style.height = (.5 * w.addContentDiv.offsetHeight) + "px";
img.style.top = (.25 * w.addContentDiv.offsetHeight) + "px";
w.addContentDiv.appendChild(img);
}
FilePickerWidget._handleUpload(w.uploadId,
event.target,
event.target.getAttribute("webappRelativePath"),
w);
});
},
_upload_completeHandler: function(fileName, webappRelativePath, fileTypeImage, error)
{
if (error)
{
this._showStatus(error, true);
this._hideAddContent();
this._showAddContent(webappRelativePath);
}
else
{
var nextRow = dojo.dom.nextElement(this.addContentDiv);
var rowClass = (nextRow
? ("xformsRow" + (dojo.html.hasClass(nextRow, "xformsRowEven")
? "Odd"
: "Even"))
: "xformsRowEvent");
var row = this._createRow(fileName,
webappRelativePath == "/" ? "/" + fileName : webappRelativePath + "/" + fileName,
false,
fileTypeImage,
rowClass);
this.contentDiv.replaceChild(row, this.addContentDiv);
this.addContentDiv = null;
}
},
_closeParentPathMenu: function()
{
if (this.parentPathMenu)
{
dojo.dom.removeChildren(this.parentPathMenu);
dojo.dom.removeNode(this.parentPathMenu);
this.parentPathMenu = null;
}
this.headerMenuTriggerLink.style.borderStyle = "solid";
},
_openParentPathMenu: function(target, path)
{
var d = target.ownerDocument;
this.parentPathMenu = d.createElement("div");
this.parentPathMenu.filePickerWidget = this;
d.currentParentPathMenu = this.parentPathMenu;
// handler for closing the menu if the mouse is clicked
// outside of the menu
var parentPathMenu_documentClickHandler = function(event)
{
var t = event.target;
var d = event.target.ownerDocument;
// always remove - this handler only ever needs to handle a single click
d.removeEventListener("click", parentPathMenu_documentClickHandler, true);
while (t && t != d)
{
if (t == d.currentParentPathMenu ||
t == d.currentParentPathMenu.filePickerWidget.headerMenuTriggerLink)
{
// the click is coming from within the component - ignore it
return true;
}
t = t.parentNode;
}
d.currentParentPathMenu.filePickerWidget._closeParentPathMenu();
};
d.addEventListener("click", parentPathMenu_documentClickHandler, true);
dojo.html.setClass(this.parentPathMenu, "xformsFilePickerParentPathMenu");
var left = 0;
var top = 0;
var n = target;
do
{
left += n.offsetLeft;// + parseInt(n.style.marginLeft) + parseInt(n.style.borderLeft);
top += n.offsetTop;// + parseInt(n.style.marginTop) + parseInt(n.style.borderTop);
n = n.parentNode;
}
while (n != this.node);
this.parentPathMenu.style.top = top + target.offsetHeight + "px";
this.parentPathMenu.style.left = left + "px";
var parentNodes = null;
if (path == "/")
{
parentNodes = [ "/" ];
}
else
{
parentNodes = path.split("/");
parentNodes[0] = "/";
}
var pathTextDiv = d.createElement("div");
pathTextDiv.style.fontWeight = "bold";
pathTextDiv.style.paddingLeft = "5px";
pathTextDiv.appendChild(d.createTextNode(alfresco_xforms_constants.resources["path"]));
this.parentPathMenu.appendChild(pathTextDiv);
var currentPathNodes = [];
for (var i = 0; i < parentNodes.length; i++)
{
if (i != 0)
{
currentPathNodes.push(parentNodes[i]);
}
var path = i == 0 ? "/" : "/" + currentPathNodes.join("/");
var parentNodeDiv = d.createElement("div");
parentNodeDiv.setAttribute("webappRelativePath", path);
this.parentPathMenu.appendChild(parentNodeDiv);
parentNodeDiv.style.display = "block";
parentNodeDiv.style.paddingLeft = (i * 16) + parseInt(pathTextDiv.style.paddingLeft) + "px";
parentNodeDiv.style.paddingRight = parseInt(pathTextDiv.style.paddingLeft) + "px";
parentNodeDiv.style.whiteSpace = "nowrap";
var parentNodeImage = d.createElement("img");
parentNodeImage.align = "absmiddle";
parentNodeImage.style.marginRight = "4px";
parentNodeDiv.appendChild(parentNodeImage);
parentNodeImage.setAttribute("src", alfresco_xforms_constants.WEBAPP_CONTEXT +
"/images/icons/space_small.gif");
parentNodeDiv.appendChild(parentNodeImage);
parentNodeDiv.appendChild(d.createTextNode(path));
dojo.event.connect(parentNodeDiv,
"onclick",
function(event)
{
var w = event.currentTarget;
var path = w.getAttribute("webappRelativePath");
w = w.parentNode;
w.filePickerWidget._closeParentPathMenu();
w.filePickerWidget._navigateToNode(path);
});
}
this.node.appendChild(this.parentPathMenu);
}
};