diff --git a/etemplate/js/et2_description.js b/etemplate/js/et2_description.js
index 11cd82742f..999d1ba972 100644
--- a/etemplate/js/et2_description.js
+++ b/etemplate/js/et2_description.js
@@ -1,5 +1,5 @@
/**
- * eGroupWare eTemplate2 - JS Template base class
+ * eGroupWare eTemplate2 - JS Description object
*
* @license http://opensource.org/licenses/gpl-license.php GPL - GNU General Public License
* @package etemplate
@@ -42,6 +42,6 @@ et2_description = et2_DOMWidget.extend({
});
-et2_register_widget(et2_description, ["description"]);
+et2_register_widget(et2_description, ["description", "label"]);
diff --git a/etemplate/js/et2_grid.js b/etemplate/js/et2_grid.js
new file mode 100644
index 0000000000..db33adea90
--- /dev/null
+++ b/etemplate/js/et2_grid.js
@@ -0,0 +1,395 @@
+/**
+ * eGroupWare eTemplate2 - JS Description object
+ *
+ * @license http://opensource.org/licenses/gpl-license.php GPL - GNU General Public License
+ * @package etemplate
+ * @subpackage api
+ * @link http://www.egroupware.org
+ * @author Andreas Stöckel
+ * @copyright Stylite 2011
+ * @version $Id: et2_description.js 36016 2011-08-05 14:53:54Z igel457 $
+ */
+
+/*egw:uses
+ jquery.jquery;
+ et2_widget;
+ et2_xml;
+*/
+
+/**
+ * Class which implements the "grid" XET-Tag
+ */
+et2_grid = et2_DOMWidget.extend({
+
+ init: function(_parent) {
+ // Create the table body and the table
+ this.tbody = $j(document.createElement("tbody"));
+ this.table = $j(document.createElement("table"));
+ this.table.append(this.tbody);
+
+ // Call the parent constructor
+ this._super.apply(this, arguments);
+
+ // Counters for rows and columns
+ this.rowCount = 0;
+ this.columnCount = 0;
+
+ // 2D-Array which holds references to the DOM td tags
+ this.cells = [];
+ this.managementArray = [];
+ },
+
+ destroy: function() {
+ // Delete all references to cells or widgets
+ this.cells = null;
+ this.managementArray = null;
+ },
+
+ _initCells: function(_colData, _rowData) {
+ // Copy the width and height
+ var w = _colData.length;
+ var h = _rowData.length;
+
+ // Create the 2D-Cells array
+ var cells = new Array(h);
+ for (var y = 0; y < h; y++)
+ {
+ cells[y] = new Array(w);
+
+ // Initialize the cell description objects
+ for (var x = 0; x < w; x++)
+ {
+ cells[y][x] = {
+ "td": null,
+ "widget": null,
+ "colData": _colData[x],
+ "rowData": _rowData[y],
+ "colSpan": 1,
+ "rowSpan": 1,
+ "x": x,
+ "y": y
+ };
+ }
+ }
+
+ return cells;
+ },
+
+ _getColDataEntry: function() {
+ return {
+ "width": "auto",
+ "class": "",
+ "align": "",
+ "span": "1"
+ };
+ },
+
+ _getRowDataEntry: function() {
+ return {
+ "height": "auto",
+ "class": "",
+ "valign": "",
+ "span": "1"
+ };
+ },
+
+ _readAttrWithDefault: function(_node, _name, _default) {
+ var val = _node.getAttribute(_name);
+
+ return (val === null) ? _default : val;
+ },
+
+ _getCell: function(_cells, _x, _y) {
+ if ((0 <= _y) && (_y < _cells.length))
+ {
+ var row = _cells[_y];
+ if ((0 <= _x) && (_x < row.length))
+ {
+ return row[_x];
+ }
+ }
+
+ throw("Error while accessing grid cells, invalid element count or span value!");
+ },
+
+ _forceNumber: function(_val) {
+ if (isNaN(_val))
+ {
+ throw(_val + " is not a number!");
+ }
+
+ return parseInt(_val);
+ },
+
+ _fetchRowColData: function(columns, rows, colData, rowData) {
+ // Parse the columns tag
+ et2_filteredNodeIterator(columns, function(node, nodeName) {
+ var colDataEntry = this._getColDataEntry();
+ if (nodeName == "column")
+ {
+ colDataEntry["width"] = this._readAttrWithDefault(node, "width", "auto");
+ colDataEntry["class"] = this._readAttrWithDefault(node, "class", "");
+ colDataEntry["align"] = this._readAttrWithDefault(node, "align", "");
+ colDataEntry["span"] = this._readAttrWithDefault(node, "span", "1");
+ }
+ else
+ {
+ colDataEntry["span"] = "all";
+ }
+ colData.push(colDataEntry);
+ }, this);
+
+ // Parse the rows tag
+ et2_filteredNodeIterator(rows, function(node, nodeName) {
+ var rowDataEntry = this._getRowDataEntry();
+ if (nodeName == "row")
+ {
+ rowDataEntry["height"] = this._readAttrWithDefault(node, "height", "auto");
+ rowDataEntry["class"] = this._readAttrWithDefault(node, "class", "");
+ rowDataEntry["valign"] = this._readAttrWithDefault(node, "valign", "");
+ rowDataEntry["span"] = this._readAttrWithDefault(node, "span", "1");
+ }
+ else
+ {
+ rowDataEntry["span"] = "all";
+ }
+ rowData.push(rowDataEntry);
+ }, this);
+ },
+
+ _fillCells: function(cells, columns, rows) {
+ // Read the elements inside the columns
+ var x = 0;
+ et2_filteredNodeIterator(columns, function(node, nodeName) {
+
+ function _readColNode(node, nodeName) {
+ var cell = this._getCell(cells, x, y);
+
+ // Read the span value of the element
+ if (node.getAttribute("span"))
+ {
+ cell.rowSpan = node.getAttribute("span");
+ }
+ else
+ {
+ cell.rowSpan = cell.colData["span"];
+ }
+
+ if (cell.rowSpan == "all")
+ {
+ cell.rowSpan = cells.length;
+ }
+
+ var span = cell.rowSpan = this._forceNumber(cell.rowSpan);
+
+ // Create the widget
+ var widget = this.createElementFromNode(node, nodeName);
+
+ // Fill all cells the widget is spanning
+ for (var i = 0; i < span && y < cells.length; i++, y++)
+ {
+ this._getCell(cells, x, y).widget = widget;
+ }
+ };
+
+ // If the node is a column, create the widgets which belong into
+ // the column
+ var y = 0;
+ if (nodeName == "column")
+ {
+ et2_filteredNodeIterator(node, _readColNode, this);
+ }
+ else
+ {
+ _readColNode.call(this, node, nodeName);
+ }
+
+ x++;
+ }, this);
+
+ // Read the elements inside the rows
+ var y = 0;
+ et2_filteredNodeIterator(rows, function(node, nodeName) {
+
+ function _readRowNode(node, nodeName) {
+ var cell = this._getCell(cells, x, y);
+
+ // Read the span value of the element
+ if (node.getAttribute("span"))
+ {
+ cell.colSpan = node.getAttribute("span");
+ }
+ else
+ {
+ cell.colSpan = cell.rowData["span"];
+ }
+
+ if (cell.colSpan == "all")
+ {
+ cell.colSpan = cells[y].length;
+ }
+
+ var span = cell.colSpan = this._forceNumber(cell.colSpan);
+
+ // Create the element
+ var widget = this.createElementFromNode(node, nodeName);
+
+ // Fill all cells the widget is spanning
+ for (var i = 0; i < span && x < cells[y].length; i++, x++)
+ {
+ cell = this._getCell(cells, x, y);
+ if (cell.widget == null)
+ {
+ cell.widget = widget;
+ }
+ else
+ {
+ throw("Grid cell collision, two elements " +
+ "defined for cell (" + x + "," + y + ")!");
+ }
+ }
+ }
+
+ // If the node is a row, create the widgets which belong into
+ // the row
+ var x = 0;
+ if (nodeName == "row")
+ {
+ et2_filteredNodeIterator(node, _readRowNode, this);
+ }
+ else
+ {
+ _readRowNode.call(this, node, nodeName);
+ }
+
+ y++;
+ }, this);
+ },
+
+ /**
+ * As the does not fit very well into the default widget structure, we're
+ * overwriting the loadFromXML function and doing a two-pass reading -
+ * in the first step the
+ */
+ loadFromXML: function(_node) {
+ // Get the columns and rows tag
+ var rowsElems = _node.getElementsByTagName("rows");
+ var columnsElems = _node.getElementsByTagName("columns");
+
+ if (rowsElems.length == 1 && columnsElems.length == 1)
+ {
+ var columns = columnsElems[0];
+ var rows = rowsElems[0];
+ var colData = [];
+ var rowData = [];
+
+ // Fetch the column and row data
+ this._fetchRowColData(columns, rows, colData, rowData);
+
+ // Initialize the cells
+ var cells = this._initCells(colData, rowData);
+
+ // Create the widgets inside the cells and read the span values
+ this._fillCells(cells, columns, rows);
+
+ // Create the table rows
+ this.createTableFromCells(cells);
+ }
+ else
+ {
+ throw("Error while parsing grid, none or multiple rows or columns tags!");
+ }
+ },
+
+ createTableFromCells: function(_cells) {
+ // Set the rowCount and columnCount variables
+ var h = this.rowCount = _cells.length;
+ var w = this.columnCount = (h > 0) ? _cells[0].length : 0;
+
+ this.managementArray = [];
+ this.cells = _cells;
+
+ // Create the table rows.
+ for (var y = 0; y < h; y++)
+ {
+ var row = _cells[y];
+ var tr = $j(document.createElement("tr")).appendTo(this.tbody);
+
+ // Create the cells. x is incremented by the colSpan value of the
+ // cell.
+ for (var x = 0; x < w;)
+ {
+ // Fetch a cell from the cells
+ var cell = this._getCell(_cells, x, y);
+
+ if (cell.td == null && cell.widget != null)
+ {
+ // Create the cell
+ var td = $j(document.createElement("td")).appendTo(tr);
+
+ // Add the entry for the widget to the management array
+ this.managementArray.push({
+ "cell": td[0],
+ "widget": cell.widget
+ });
+
+ // Trigger the "onSetParent" event of the widget
+ cell.widget.onSetParent();
+
+ // Set the span values of the cell
+ var cs = Math.min(w - x, cell.colSpan);
+ var rs = Math.min(h - y, cell.rowSpan);
+
+ // Set the col and row span values
+ if (cs > 1) {
+ td.attr("colspan", cs);
+ }
+
+ if (rs > 1) {
+ td.attr("rowspan", rs);
+ }
+
+ // Assign the td to the cell
+ for (var sx = x; sx < x + cs; sx++)
+ {
+ for (var sy = y; sy < y + rs; sy++)
+ {
+ this._getCell(_cells, sx, sy).td = td;
+ }
+ }
+
+ x += cell.colSpan;
+ }
+ else
+ {
+ x++;
+ }
+ }
+ }
+ },
+
+ getDOMNode: function(_sender) {
+ // If the parent class functions are asking for the DOM-Node, return the
+ // outer table.
+ if (_sender == this)
+ {
+ return this.table[0];
+ }
+
+ // Check whether the _sender object exists inside the management array
+ for (var i = 0; i < this.managementArray.length; i++)
+ {
+ if (this.managementArray[i].widget == _sender)
+ {
+ return this.managementArray[i].cell;
+ }
+ }
+
+ return null;
+ }
+
+});
+
+et2_register_widget(et2_grid, ["grid"]);
+
+
diff --git a/etemplate/js/et2_template.js b/etemplate/js/et2_template.js
index 9a43b79f31..00eee9e3a0 100644
--- a/etemplate/js/et2_template.js
+++ b/etemplate/js/et2_template.js
@@ -44,7 +44,7 @@ et2_template = et2_DOMWidget.extend({
// Check whether the parent implements the et2_IDOMNode interface. If
// yes, grab the DOM node and create our own.
if (this._parent && this._parent.implements(et2_IDOMNode)) {
- var parentNode = this._parent.getDOMNode();
+ var parentNode = this._parent.getDOMNode(this);
if (parentNode)
{
diff --git a/etemplate/js/et2_widget.js b/etemplate/js/et2_widget.js
index 8969f377b3..79c50d30e8 100644
--- a/etemplate/js/et2_widget.js
+++ b/etemplate/js/et2_widget.js
@@ -132,22 +132,28 @@ et2_widget = Class.extend({
// Create the copy
var copy = new (this.constructor)(_parent, _type);
- // Create a clone of all child elements
- for (var i = 0; i < this._children.length; i++)
+ // Assign this element to the copy
+ copy.assign(this);
+
+ return copy;
+ },
+
+ assign: function(_obj) {
+
+ // Create a clone of all child elements of the given object
+ for (var i = 0; i < _obj._children.length; i++)
{
- this._children[i].clone(copy, this._children[i].type);
+ _obj._children[i].clone(this, _obj._children[i].type);
}
// Copy all properties for which a setter function exists
- for (var key in this)
+ for (var key in _obj)
{
- if (key != "id" && typeof copy["set_" + key] == "function")
+ if (key != "id" && typeof this["set_" + key] == "function")
{
- copy["set_" + key](this[key]);
+ this["set_" + key](_obj[key]);
}
}
-
- return copy;
},
/**
@@ -269,6 +275,24 @@ et2_widget = Class.extend({
return false;
},
+ createElementFromNode: function(_node, _nodeName) {
+ if (typeof _nodeName == "undefined")
+ {
+ _nodeName = _node.nodeName.toLowerCase();
+ }
+
+ // Check whether a widget with the given type is registered.
+ var constructor = typeof et2_registry[_nodeName] == "undefined" ?
+ et2_placeholder : et2_registry[_nodeName];
+
+ // Creates the new widget, passes this widget as an instance and
+ // passes the widgetType. Then it goes on loading the XML for it.
+ var widget = new constructor(this, _nodeName)
+ widget.loadFromXML(_node);
+
+ return widget;
+ },
+
/**
* Loads the widget tree from an XML node
*/
@@ -299,13 +323,8 @@ et2_widget = Class.extend({
continue;
}
- // Check whether a widget with the given type is registered.
- var constructor = typeof et2_registry[widgetType] == "undefined" ?
- et2_placeholder : et2_registry[widgetType];
-
- // Creates the new widget, passes this widget as an instance and
- // passes the widgetType. Then it goes on loading the XML for it.
- (new constructor(this, widgetType)).loadFromXML(node);
+ // Create the new element
+ this.createElementFromNode(node, widgetType);
}
},
@@ -370,7 +389,17 @@ et2_widget = Class.extend({
* Interface for all widget classes, which are based on a DOM node.
*/
et2_IDOMNode = new Interface({
- getDOMNode: function() {}
+ /**
+ * Returns the DOM-Node of the current widget.
+ *
+ * @param _sender The _sender parameter defines which widget is asking for
+ * the DOMNode.depending on that, the widget may return different nodes.
+ * This is used in the grid. Normally the _sender parameter can be omitted
+ * in most implementations of the getDOMNode function.
+ * However, you should always define the _sender parameter when calling
+ * getDOMNode!
+ */
+ getDOMNode: function(_sender) {}
});
/**
@@ -403,14 +432,14 @@ et2_DOMWidget = et2_widget.extend(et2_IDOMNode, {
// Check whether the parent implements the et2_IDOMNode interface. If
// yes, grab the DOM node and create our own.
if (this._parent && this._parent.implements(et2_IDOMNode)) {
- this.setParentDOMNode(this._parent.getDOMNode());
+ this.setParentDOMNode(this._parent.getDOMNode(this));
}
},
detatchFromDOM: function() {
if (this.parentNode)
{
- var node = this.getDOMNode();
+ var node = this.getDOMNode(this);
if (node)
{
this.parentNode.removeChild(node);
@@ -432,8 +461,8 @@ et2_DOMWidget = et2_widget.extend(et2_IDOMNode, {
this.parentNode = _node;
// Attach the DOM node of this widget (if existing) to the new parent
- var node = this.getDOMNode();
- if (node)
+ var node = this.getDOMNode(this);
+ if (node && this.parentNode)
{
this.parentNode.appendChild(node);
}
@@ -447,24 +476,13 @@ et2_DOMWidget = et2_widget.extend(et2_IDOMNode, {
set_id: function(_value) {
this._super(_value);
- var node = this.getDOMNode();
+ var node = this.getDOMNode(this);
if (node)
{
node.setAttribute("id", _value);
}
},
- set_visible: function(_value) {
- /*if (_value != this.visible)
- {
- var node = this.getDOMNode();
- if (node)
- {
- node.set
- }
- }*/
- }
-
});
/**
diff --git a/etemplate/js/et2_xml.js b/etemplate/js/et2_xml.js
index 86acefd8f7..a393355f84 100644
--- a/etemplate/js/et2_xml.js
+++ b/etemplate/js/et2_xml.js
@@ -82,3 +82,16 @@ function et2_loadXMLFromURL(_url, _callback, _context)
}
}
+function et2_filteredNodeIterator(_node, _callback, _context)
+{
+ for (var i = 0; i < _node.childNodes.length; i++)
+ {
+ var node = _node.childNodes[i];
+ var nodeName = node.nodeName.toLowerCase();
+ if (nodeName.charAt(0) != "#")
+ {
+ _callback.call(_context, node, nodeName);
+ }
+ }
+}
+
diff --git a/etemplate/js/test/et2_test_grid.xet b/etemplate/js/test/et2_test_grid.xet
new file mode 100644
index 0000000000..2472cc3b94
--- /dev/null
+++ b/etemplate/js/test/et2_test_grid.xet
@@ -0,0 +1,44 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/etemplate/js/test/test_xml.html b/etemplate/js/test/test_xml.html
index 62218a64ec..965ba88cac 100644
--- a/etemplate/js/test/test_xml.html
+++ b/etemplate/js/test/test_xml.html
@@ -9,6 +9,7 @@
+