forked from extern/egroupware
5af5594f60
- All attributes of the widgets are now parsed from XML before the widget itself is created. These attributes plus all default values are then added to an associative array. The associative array is passed as second parameter to the init function of et2_widget, but is also available as this.options *after* the constructor of the et2_widget baseclass has been called. The et2_widget constructor also calls a function parseArrayMgrAttrs(_attrs) - in this function widget implementations can read the values from e.g. the content and validation_errors array and merge it into the given _attrs associative array. After the complete internal widgettree is completely loaded and created the "loadingFinished" function gets called and invokes all given setter functions. After that it "glues" the DOM tree together. This should also (I didn't measure it) be a bit faster than before, when the DOM-Tree was created on the fly. Please have a look at the changes of the et2_textbox widget to see how this affects writing widgets. Note: The "id" property is copied to the object scope on the top of the et2_widget constructor. - When widgets are cloned the "options" array gets passed along to the newly created widget. This means that changes made on the widgets during runtime are not automatically copied to the clone - as this didn't happen anyhow it is not a really disadvantage. On the other side there should be no difference between widgets directly inside the "overlay" xet tag and widgets which are inside instanciated templates. - The selbox widget doesn't work anymore - it relied on the loadAttributes function which isn't available anymore. et2_selbox should use the parseArrayMgrAttrs function to access - I've commented out some of the "validator"-code in etemplate2.js as it created some error messages when destroying the widget tree.
538 lines
12 KiB
JavaScript
538 lines
12 KiB
JavaScript
/**
|
|
* 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$
|
|
*/
|
|
|
|
"use strict";
|
|
|
|
/*egw:uses
|
|
jquery.jquery;
|
|
et2_DOMWidget;
|
|
et2_xml;
|
|
*/
|
|
|
|
/**
|
|
* Class which implements the "grid" XET-Tag
|
|
*/
|
|
var 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"))
|
|
.addClass("et2_grid");
|
|
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() {
|
|
this._super.call(this, arguments);
|
|
|
|
// Delete all references to cells or widgets
|
|
this.cells = null;
|
|
this.managementArray = null;
|
|
this.table = null;
|
|
this.tbody = 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,
|
|
"autoColSpan": false,
|
|
"rowSpan": 1,
|
|
"autoRowSpan": false,
|
|
"x": x,
|
|
"y": y
|
|
};
|
|
}
|
|
}
|
|
|
|
return cells;
|
|
},
|
|
|
|
_getColDataEntry: function() {
|
|
return {
|
|
"width": "auto",
|
|
"class": "",
|
|
"align": "",
|
|
"span": "1"
|
|
};
|
|
},
|
|
|
|
_getRowDataEntry: function() {
|
|
return {
|
|
"height": "auto",
|
|
"class": "",
|
|
"valign": "",
|
|
"span": "1"
|
|
};
|
|
},
|
|
|
|
_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"] = et2_readAttrWithDefault(node, "width", "auto");
|
|
colDataEntry["class"] = et2_readAttrWithDefault(node, "class", "");
|
|
colDataEntry["align"] = et2_readAttrWithDefault(node, "align", "");
|
|
colDataEntry["span"] = et2_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"] = et2_readAttrWithDefault(node, "height", "auto");
|
|
rowDataEntry["class"] = et2_readAttrWithDefault(node, "class", "");
|
|
rowDataEntry["valign"] = et2_readAttrWithDefault(node, "valign", "");
|
|
rowDataEntry["span"] = et2_readAttrWithDefault(node, "span", "1");
|
|
}
|
|
else
|
|
{
|
|
rowDataEntry["span"] = "all";
|
|
}
|
|
rowData.push(rowDataEntry);
|
|
}, this);
|
|
},
|
|
|
|
_fillCells: function(cells, columns, rows) {
|
|
var h = cells.length;
|
|
var w = (h > 0) ? cells[0].length : 0;
|
|
|
|
// Read the elements inside the columns
|
|
var x = 0;
|
|
|
|
et2_filteredNodeIterator(columns, function(node, nodeName) {
|
|
|
|
function _readColNode(node, nodeName) {
|
|
if (y >= h)
|
|
{
|
|
et2_debug("warn", "Skipped grid cell in column, '" +
|
|
nodeName + "'");
|
|
return;
|
|
}
|
|
|
|
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"];
|
|
cell.autoRowSpan = true;
|
|
}
|
|
|
|
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) {
|
|
if (x >= w)
|
|
{
|
|
et2_debug("warn", "Skipped grid cell in row, '" +
|
|
nodeName + "'");
|
|
return;
|
|
}
|
|
|
|
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"];
|
|
cell.autoColSpan = true;
|
|
}
|
|
|
|
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);
|
|
},
|
|
|
|
_expandLastCells: function(_cells) {
|
|
var h = _cells.length;
|
|
var w = (h > 0) ? _cells[0].length : 0;
|
|
|
|
// Determine the last cell in each row and expand its span value if
|
|
// the span has not been explicitly set.
|
|
for (var y = 0; y < h; y++)
|
|
{
|
|
for (var x = w - 1; x >= 0; x--)
|
|
{
|
|
var cell = _cells[y][x];
|
|
|
|
if (cell.widget != null)
|
|
{
|
|
if (cell.autoColSpan)
|
|
{
|
|
cell.colSpan = w - x;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Determine the last cell in each column and expand its span value if
|
|
// the span has not been explicitly set.
|
|
for (var x = 0; x < w; x++)
|
|
{
|
|
for (var y = h - 1; y >= 0; y--)
|
|
{
|
|
var cell = _cells[y][x];
|
|
|
|
if (cell.widget != null)
|
|
{
|
|
if (cell.autoRowSpan)
|
|
{
|
|
cell.rowSpan = h - y;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
},
|
|
|
|
/**
|
|
* 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 = et2_directChildrenByTagName(_node, "rows");
|
|
var columnsElems = et2_directChildrenByTagName(_node, "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);
|
|
|
|
// Expand the span values of the last cells
|
|
this._expandLastCells(cells);
|
|
|
|
// 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
|
|
});
|
|
|
|
// Set the span values of the cell
|
|
var cs = (x == w - 1) ? w - x : Math.min(w - x, cell.colSpan);
|
|
var rs = (y == h - 1) ? h - y : 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++;
|
|
}
|
|
}
|
|
}
|
|
},
|
|
|
|
/**
|
|
* The grid needs its own assign function in order to fill the grid
|
|
* accordingly.
|
|
*/
|
|
assign: function(_obj) {
|
|
if (_obj instanceof et2_grid)
|
|
{
|
|
// Remember all widgets which have already been instanciated
|
|
var instances = [];
|
|
|
|
// Copy the cells array of the other grid and clone the widgets
|
|
// inside of it
|
|
var cells = new Array(_obj.cells.length);
|
|
|
|
for (var y = 0; y < _obj.cells.length; y++)
|
|
{
|
|
cells[y] = new Array(_obj.cells[y].length);
|
|
|
|
for (var x = 0; x < _obj.cells[y].length; x++)
|
|
{
|
|
var srcCell = _obj.cells[y][x];
|
|
|
|
var widget = null;
|
|
if (srcCell.widget)
|
|
{
|
|
// Search for the widget inside the instances array
|
|
for (var i = 0; i < instances.length; i++)
|
|
{
|
|
if (instances[i].srcWidget == srcCell.widget)
|
|
{
|
|
widget = instances[i].widget;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (widget == null)
|
|
{
|
|
widget = srcCell.widget.clone(this, srcCell.widget.type);
|
|
instances.push({
|
|
"widget": widget,
|
|
"srcWidget": srcCell.widget
|
|
});
|
|
}
|
|
}
|
|
|
|
cells[y][x] = {
|
|
"widget": widget,
|
|
"td": null,
|
|
"colSpan": srcCell.colSpan,
|
|
"rowSpan": srcCell.rowSpan
|
|
}
|
|
}
|
|
}
|
|
|
|
// Create the table
|
|
this.createTableFromCells(cells);
|
|
|
|
// Copy a reference to the content array manager
|
|
if (_obj._mgr)
|
|
{
|
|
this._mgr = _obj._mgr;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
throw("Invalid assign to grid!");
|
|
}
|
|
},
|
|
|
|
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;
|
|
},
|
|
|
|
set_id: function(_value) {
|
|
this._super.apply(this, arguments);
|
|
|
|
// Check whether a namespace exists for this element
|
|
this.checkCreateNamespace();
|
|
}
|
|
|
|
});
|
|
|
|
et2_register_widget(et2_grid, ["grid"]);
|
|
|
|
|