First version of the dataview which does... well... 'something' at least (currently shows dummy rows with non-etemplate content) Need to cleanup code and test auto hiding of rows (code is actually there)

This commit is contained in:
Andreas Stöckel 2011-08-31 15:39:24 +00:00
parent 4088300b57
commit 035a4d369f
13 changed files with 879 additions and 131 deletions

View File

@ -10,14 +10,26 @@
* @version $Id$ * @version $Id$
*/ */
"use strict"
/*egw:uses
et2_core_inheritance;
*/
var et2_dataview_IInvalidatable = new Interface({ var et2_dataview_IInvalidatable = new Interface({
invalidate: function(); invalidate: function() {}
}); });
var et2_dataview_IDataRow = new Interface({ var et2_dataview_IDataRow = new Interface({
updateData: function(_data); updateData: function(_data) {}
});
var et2_dataview_IViewRange = new Interface({
setViewRange: function(_range) {}
}); });

View File

@ -1,14 +1,17 @@
/** /**
* eGroupWare egw_action framework - egw action framework * eGroupWare eTemplate2 - Class which contains a the columns model
* *
* @link http://www.egroupware.org
* @author Andreas Stöckel <as@stylite.de>
* @copyright 2011 by Andreas Stöckel
* @license http://opensource.org/licenses/gpl-license.php GPL - GNU General Public License * @license http://opensource.org/licenses/gpl-license.php GPL - GNU General Public License
* @package egw_action * @package etemplate
* @subpackage dataview
* @link http://www.egroupware.org
* @author Andreas Stöckel
* @copyright Stylite 2011
* @version $Id$ * @version $Id$
*/ */
"use strict"
/*egw:uses /*egw:uses
et2_inheritance; et2_inheritance;
*/ */

View File

@ -0,0 +1,53 @@
/**
* eGroupWare eTemplate2 - Class which contains a the data model
*
* @license http://opensource.org/licenses/gpl-license.php GPL - GNU General Public License
* @package etemplate
* @subpackage dataview
* @link http://www.egroupware.org
* @author Andreas Stöckel
* @copyright Stylite 2011
* @version $Id$
*/
"use strict"
/*egw:uses
et2_inheritance;
et2_dataview_interfaces;
*/
var et2_dataview_dataProvider = Class.extend({
getCount: function() {
return 1000;
},
registerDataRow: function(_idx, _dataRow) {
var row = {
"type": "dataRow",
"data": {
"ts_title": "Row " + _idx
}
};
// Get a random value which is used to simulate network latency and time
// it needs to load the data.
var rnd = Math.round(Math.random() * 1000);
if (rnd < 200)
{
_dataRow.updateData(row);
}
window.setTimeout(function() {_dataRow.updateData(row); },
Math.round(rnd / 2));
},
unregisterDataRow: function(_dataRow) {
//
}
});

View File

@ -13,32 +13,189 @@
"use strict" "use strict"
/*egw:uses /*egw:uses
jquery.jquery;
et2_dataview_interfaces; et2_dataview_interfaces;
*/ */
var et2_dataview_container = Class.extend({ var et2_dataview_container = Class.extend(et2_dataview_IInvalidatable, {
init: function(_data, _invalidationElem) { /**
this._dataProvider = _data; * Initializes the container object.
*
* @param _dataProvider is the data provider for the element
* @param _rowProvider is the "rowProvider" of the element
* @param _invalidationElem is the element of which the "invalidate" method
* will be called if the height of the elements changes. It has to
* implement the et2_dataview_IInvalidatable interface.
*/
init: function(_dataProvider, _rowProvider, _invalidationElem) {
this.dataProvider = _dataProvider;
this.rowProvider = _rowProvider;
this._invalidationElem = _invalidationElem; this._invalidationElem = _invalidationElem;
this._node = null; this._nodes = [];
this._inTree = false;
this._attachData = {"node": null, "prepend": false};
}, },
setJNode: function(_node) { destroy: function() {
// Replace the old node with the new one // Remove the nodes from the tree
if (this._node[0].parent) this.removeFromTree();
},
/**
* Setter function which can be used to update the invalidation element.
*
* @param _invalidationElem is the element of which the "invalidate" method
* will be called if the height of the elements changes. It has to
* implement the et2_dataview_IInvalidatable interface.
*/
setInvalidationElement: function(_invalidationElem) {
this._invalidationElem = _invalidationElem;
},
/**
* Inserts all container nodes into the DOM tree after the given element
*/
insertIntoTree: function(_afterNode, _prepend) {
if (!this._inTree && _afterNode != null)
{ {
this._node.replaceWith(_node); for (var i = 0; i < this._nodes.length; i++)
{
if (i == 0)
{
if (_prepend)
{
_afterNode.prepend(this._nodes[i]);
}
else
{
_afterNode.after(this._nodes[i]);
}
}
else
{
// Insert all following nodes after the previous node
this._nodes[i - 1].after(this._nodes[i]);
}
} }
this._node = _node; // Save the "attachData"
this._inTree = true;
this._attachData = {"node": _afterNode, "prepend": _prepend};
this.invalidate();
}
}, },
getJNode: function() { /**
return this._node; * Removes all container nodes from the tree.
*/
removeFromTree: function() {
if (this._inTree)
{
// Call the jQuery remove function to remove all nodes from the tree
// again.
for (var i = 0; i < this._nodes.length; i++)
{
this._nodes[i].remove();
}
// Reset the "attachData"
this._inTree = false;
this._attachData = {"node": null, "prepend": false};
}
}, },
/**
* Appends a jQuery node to the container
*/
appendNode: function(_node) {
// Add the given node to the "nodes" array
this._nodes.push(_node);
// If the container is already in the tree, attach the given node to the
// tree.
if (this._inTree)
{
if (this._nodes.length == 1)
{
if (_prepend)
{
this._attachData.node.prepend(this._nodes[0]);
}
else
{
this._attachData.node.after(this._nodes[0]);
}
}
else
{
this._nodes[_nodes.length - 2].after(_node);
}
this.invalidate();
}
},
/**
* Removes a jQuery node from the container
*/
removeNode: function(_node) {
// Get the index of the node in the nodes array
var idx = this._nodes.indexOf(_node);
if (idx >= 0)
{
// Remove the node if the container is currently attached
if (this._inTree)
{
_node.remove();
}
// Remove the node from the nodes array
this._nodes.splice(idx, 1);
}
},
/**
* Returns the last node of the container - new nodes have to be appended
* after it.
*/
getLastNode: function() {
if (this._nodes.length > 0)
{
return this._nodes[this._nodes.length - 1];
}
return null;
},
/**
* Returns the accumulated height of all container nodes. Only visible nodes
* (without "display: none") are taken into account.
*/
getHeight: function() {
var height = 0;
if (this._inTree)
{
// Increment the height value for each visible container node
var self = this;
$j(this._nodes, ":visible").each(function() {
height += self._nodeHeight(this);
});
}
console.log(height);
return height;
},
/**
* Calls the "invalidate" function of the connected invalidation element.
*/
invalidate: function() { invalidate: function() {
this._invalidationElem.invalidate(); this._invalidationElem.invalidate();
} }
@ -54,41 +211,33 @@ var et2_dataview_container = Class.extend({
*/ */
if ($j.browser.mozilla) if ($j.browser.mozilla)
{ {
et2_dataview_container.prototype.getHeight = function() et2_dataview_container.prototype._nodeHeight = function(_node)
{
if (this.node)
{ {
var height = 0;
// Firefox sometimes provides fractional pixel values - we are // Firefox sometimes provides fractional pixel values - we are
// forced to use those - we can obtain the fractional pixel height // forced to use those - we can obtain the fractional pixel height
// by using the window.getComputedStyle function // by using the window.getComputedStyle function
var compStyle = getComputedStyle(this._node, null); var compStyle = getComputedStyle(_node, null);
if (compStyle) if (compStyle)
{ {
var styleHeightStr = compStyle.getPropertyValue("height"); var styleHeightStr = compStyle.getPropertyValue("height");
var height = parseFloat(styleHeightStr.substr(0, styleHeightStr.length - 2)); height = parseFloat(styleHeightStr.substr(0,
styleHeightStr.length - 2));
if (isNaN(height) || height < 1) if (isNaN(height) || height < 1)
{ {
height = false; height = 0;
} }
} }
return height; return height;
} }
return 0;
}
} }
else else
{ {
et2_dataview_container.prototype.getHeight = function() et2_dataview_container.prototype._nodeHeight = function(_node)
{ {
if (this.node) return $j(_node).outerHeight(true);
{
return this._node.offsetHeight;
}
return 0;
} }
} }

View File

@ -13,24 +13,236 @@
"use strict"; "use strict";
/*egw:uses /*egw:uses
jquery.jquery;
et2_core_common;
et2_dataview_interfaces;
et2_dataview_view_row; et2_dataview_view_row;
et2_dataview_view_partitionTree; et2_dataview_view_partitionTree;
*/ */
var et2_dataview_grid = Class.extend({ var et2_dataview_grid = Class.extend(et2_dataview_IViewRange, {
/**
* Creates the grid.
*
* @param _parent is the parent grid class - if null, this means that this
* is the outer grid which manages the scrollarea. If not null, all other
* parameters are ignored and copied from the given grid instance.
* @param _outerId is the id of the grid container it uses for the css
* classes.
* @param _columnIds is the id of the individual columns used for the css
* classes.
* @param _avgHeight is the starting average height of the column rows.
*/
init: function(_parent, _outerId, _columnIds, _dataProvider, _avgHeight) {
// If the parent is given, copy all other parameters from it
if (_parent != null)
{
this._outerId = _parent._outerId;
this._columnIds = _parent._columnIds;
this._dataProvider = _parent._dataProvider;
this._avgHeight = _parent._partitionTree.getAverageHeight();
this._rowProvider = _parent._rowProvider;
}
else
{
// Otherwise copy the given parameters
this._outerId = _outerId;
this._columnIds = _columnIds;
this._dataProvider = _dataProvider;
this._avgHeight = _avgHeight;
// Create the row provider
this._rowProvider = new et2_dataview_rowProvider(_outerId,
_columnIds);
this._scrollHeight = 0;
this._scrollTimeout = null;
}
// The "treeChanged" variable is called whenever the viewrange has been
// set - changing the viewrange is the function which causes new elements
// to be generated and thus the partition tree to degenerate
this._treeChanged = false;
// Count of elements which are buffered at top and bottom of the viewrange
// before they get replaced with placeholders
this._holdCount = 50;
// The current range holds the currently visible nodes
this._currentRange = et2_range(0, 0);
// Build the grid outer nodes
this._createNodes();
init: function(_dataProvider, _count, _avgHeight) {
// Create the partition tree object which is used to organize the tree // Create the partition tree object which is used to organize the tree
// items. // items.
this._partitionTree = new et2_dataview_partitionTree(_dataProvider, this._partitionTree = new et2_dataview_partitionTree(this._dataProvider,
_count, _avgHeight); this._rowProvider, this._avgHeight, this.innerTbody);
// Setup the "rebuild" timer - it rebuilds the complete partition tree
// if any change has been done to it. Rebuilding the partition tree is
// necessary as the tree structure happens to degenerate.
var self = this;
this._rebuildTimer = window.setInterval(function() {
self._checkTreeRebuild();
}, 10 * 1000);
}, },
destroy: function() { destroy: function() {
// Free the partition tree // Stop the scroll timeout
if (this._scrollTimeout)
{
window.clearTimeout(this._scrollTimeout);
}
// Stop the rebuild timer
window.clearInterval(this._rebuildTimer);
// Free the partition tree and the row provider
this._partitionTree.free(); this._partitionTree.free();
this._rowProvider.free();
}, },
/**
* The setViewRange function updates the range in which columns are shown.
*/
setViewRange: function(_range) {
this._treeChanged = true;
// Copy the given range
this._currentRange = et2_bounds(_range.top, _range.bottom);
// Display all elements in the given range
var nodes = this._partitionTree.getRangeNodes(_range);
for (var i = 0; i < nodes.length; i++)
{
if (nodes[i].implements(et2_dataview_IViewRange))
{
nodes[i].setViewRange(_range);
}
}
// Deactivated the code below for testing purposes
// Calculate the range of the actually shown elements
/*var displayTop = _range.top;
var displayBottom = _range.bottom;
if (nodes.length > 0)
{
displayTop = nodes[0].getPosTop();
displayBottom = nodes[nodes.length - 1].getPosBottom();
}
// Hide everything except for _holdCount elements at the top and bottom
// of the viewrange
var reduceHeight = this._partitionTree.getAverageHeight() * this._holdCount;
if (displayTop > reduceHeight)
{
this._partitionTree.reduce(et2_bounds(0, displayTop - reduceHeight));
}
if (displayBottom + reduceHeight < this._partitionTree.getHeight())
{
this._partitionTree.reduce(et2_bounds(displayBottom + reduceHeight,
this._partitionTree.getHeight()));
}*/
},
/**
* Updates the scrollheight
*/
setScrollHeight: function(_height) {
this._height = _height;
// Update the height of the outer container
if (this.scrollarea)
{
this.scrollarea.height(_height);
}
// Update the viewing range
this.setViewRange(et2_range(this._currentRange.top, this._height));
},
/**
* Returns the JQuery outer DOM-Node
*/
getJNode: function() {
return this.outerCell;
},
/* ---- PRIVATE FUNCTIONS ---- */
/**
* Checks whether the partition tree has to be rebuilt and if yes, does
* that.
*/
_checkTreeRebuild: function() {
if (this._treeChanged)
{
var depth = this._partitionTree.getDepth();
var count = this._partitionTree.getManagedCount();
// Check whether the depth of the tree is very unproportional
// regarding to the count of elements managed in it
if (count < Math.pow(ET2_PARTITION_TREE_WIDTH, depth - 1))
{
this._partitionTree.rebuild();
}
// Reset the "treeChanged" function.
this._treeChanged = false;
}
},
/**
* Creates the grid DOM-Nodes
*/
_createNodes: function() {
this.outerCell = $j(document.createElement("td"))
.addClass("frame")
.attr("colspan", this._columnIds.length + (this._parent ? 0 : 1));
// Create the scrollarea div if this is the outer grid
this.scrollarea = null;
if (this._parent == null)
{
this.scrollarea = $j(document.createElement("div"))
.addClass("egwGridView_scrollarea")
.scroll(this, function(e) {
// Clear any older scroll timeout
if (e.data._scrollTimeout)
{
window.clearTimeout(e.data._scrollTimeout);
}
// Set a new timeout which calls the setViewArea
// function
e.data._scrollTimeout = window.setTimeout(function() {
e.data.setViewRange(et2_range(
e.data.scrollarea.scrollTop(),
e.data._height
));
}, 25);
})
.height(this._scrollHeight)
.appendTo(this.outerCell);
}
// Create the inner table
var table = $j(document.createElement("table"))
.addClass("egwGridView_grid")
.appendTo(this.scrollarea ? this.scrollarea : this.outerCell);
this.innerTbody = $j(document.createElement("tbody"))
.appendTo(table);
}
}); });

View File

@ -40,16 +40,19 @@ var et2_dataview_gridContainer = Class.extend({
* Constructor for the grid container * Constructor for the grid container
* @param object _parentNode is the DOM-Node into which the grid view will be inserted * @param object _parentNode is the DOM-Node into which the grid view will be inserted
*/ */
init: function(_parentNode) { init: function(_parentNode, _dataProvider) {
// Copy the parent node parameter // Copy the arguments
this.parentNode = $j(_parentNode); this.parentNode = $j(_parentNode);
this.dataProvider = _dataProvider;
// Initialize some variables // Initialize some variables
this.columnNodes = []; // Array with the header containers this.columnNodes = []; // Array with the header containers
this.columns = []; this.columns = [];
this.columnMgr = null; this.columnMgr = null;
this.grid = null;
this.width = 0; this.width = 0;
this.height = 0; this.height = 0;
@ -69,6 +72,12 @@ var et2_dataview_gridContainer = Class.extend({
// Clear the columns // Clear the columns
this._clearHeader(); this._clearHeader();
// Free the grid
if (this.grid)
{
this.grid.free();
}
// Detatch the outer element // Detatch the outer element
this.table.remove(); this.table.remove();
}, },
@ -99,10 +108,13 @@ var et2_dataview_gridContainer = Class.extend({
this.columnMgr = new et2_dataview_columns(_columnData); this.columnMgr = new et2_dataview_columns(_columnData);
// Create the stylesheets // Create the stylesheets
this._updateColumns(); this.updateColumns();
// Build the header row // Build the header row
this._buildHeader(); this._buildHeader();
// Build the grid
this._buildGrid();
}, },
/** /**
@ -115,14 +127,20 @@ var et2_dataview_gridContainer = Class.extend({
// Rebuild the column stylesheets // Rebuild the column stylesheets
this.columnMgr.setTotalWidth(_w - this.scrollbarWidth); this.columnMgr.setTotalWidth(_w - this.scrollbarWidth);
et2_debug("log", _w - this.scrollbarWidth);
this._updateColumns(); this._updateColumns();
} }
if (this.height != _h) if (this.height != _h)
{ {
this.height = _h; this.height = _h;
// Set the height of the grid. // Set the height of the grid.
// TODO if (this.grid)
{
this.grid.setScrollHeight(this.height -
this.headTr.outerHeight(true));
}
} }
}, },
@ -236,14 +254,12 @@ var et2_dataview_gridContainer = Class.extend({
vis_col++; vis_col++;
this.visibleColumnCount++; this.visibleColumnCount++;
// Update the visibility of the column
styleSheet.updateRule("." + col.tdClass, styleSheet.updateRule("." + col.tdClass,
"display: " + (col.visible ? "table-cell" : "none") + "; " + "display: table-cell; " +
((vis_col == total_cnt) ? "border-right-width: 0 " : "border-right-width: 1px ") + ((vis_col == total_cnt) ? "border-right-width: 0 " : "border-right-width: 1px ") +
"!important;"); "!important;");
styleSheet.updateRule(".egwGridView_outer ." + col.divClass,
"width: " + (col.width - this.headerBorderWidth) + "px;");
// Ugly browser dependant code - each browser seems to treat the // Ugly browser dependant code - each browser seems to treat the
// right (collapsed) border of the row differently // right (collapsed) border of the row differently
var addBorder = 0; var addBorder = 0;
@ -270,10 +286,15 @@ var et2_dataview_gridContainer = Class.extend({
addBorder += 1; addBorder += 1;
} }
var width = Math.max(0, (col.width - this.columnBorderWidth - addBorder)); // Write the width of the body-columns
var columnWidth = Math.max(0, (col.width - this.columnBorderWidth - addBorder));
styleSheet.updateRule(".egwGridView_grid ." + col.divClass, styleSheet.updateRule(".egwGridView_grid ." + col.divClass,
"width: " + width + "px;"); "width: " + columnWidth + "px;");
// Write the width of the header columns
var headerWidth = Math.max(0, (col.width - this.headerBorderWidth));
styleSheet.updateRule(".egwGridView_outer ." + col.divClass,
"width: " + headerWidth + "px;");
totalWidth += col.width; totalWidth += col.width;
@ -281,8 +302,7 @@ var et2_dataview_gridContainer = Class.extend({
} }
else else
{ {
styleSheet.updateRule("." + col.tdClass, styleSheet.updateRule("." + col.tdClass, "display: none;");
"display: " + (col.visible ? "table-cell" : "none") + ";");
} }
} }
@ -338,6 +358,24 @@ var et2_dataview_gridContainer = Class.extend({
+ this.selectCol.width() + 1); + this.selectCol.width() + 1);
}, },
/**
* Builds the inner grid class
*/
_buildGrid: function() {
// Create the collection of column ids
var colIds = new Array(this.columns.length);
for (var i = 0; i < this.columns.length; i++)
{
colIds[i] = this.columns[i].id;
}
// Create the grid class and pass "19" as the starting average row height
this.grid = new et2_dataview_grid(null, this.uniqueId, colIds,
this.dataProvider, 19);
// Insert the grid into the DOM-Tree
this.containerTr.append(this.grid.getJNode());
},
/* --- Code for calculating the browser/css depending widths --- */ /* --- Code for calculating the browser/css depending widths --- */

View File

@ -49,8 +49,9 @@ var et2_dataview_IPartitionHeight = new Interface({
var et2_dataview_partitionNode = Class.extend([et2_dataview_IPartitionHeight, var et2_dataview_partitionNode = Class.extend([et2_dataview_IPartitionHeight,
et2_dataview_IInvalidatable], { et2_dataview_IInvalidatable], {
init: function() { init: function(_root) {
this._root = _root;
this._parent = null; this._parent = null;
this._pidx = 0; this._pidx = 0;
@ -126,12 +127,7 @@ var et2_dataview_partitionNode = Class.extend([et2_dataview_IPartitionHeight,
* Returns the root node of the partition tree * Returns the root node of the partition tree
*/ */
getRoot: function() { getRoot: function() {
if (this.parent != null) return this._root;
{
return this.parent.getRoot();
}
return this;
}, },
/** /**
@ -293,6 +289,18 @@ var et2_dataview_partitionNode = Class.extend([et2_dataview_IPartitionHeight,
_data.height += this.getHeight(); _data.height += this.getHeight();
}, },
getNodeIdx: function(_idx, _create) {
return null;
},
getRowProvider: function() {
return this.getRoot().getRowProvider();
},
getDataProvider: function() {
return this.getRoot().getDataProvider();
},
/* ---- PRIVATE FUNCTIONS ---- */ /* ---- PRIVATE FUNCTIONS ---- */
_accumulateValue: function(_f1, _f2) { _accumulateValue: function(_f1, _f2) {
@ -314,7 +322,7 @@ var et2_dataview_partitionNode = Class.extend([et2_dataview_IPartitionHeight,
}); });
/*var et2_dataview_IIndexOperations = new Interface({ /*var et2_dataview_IIndexOperations = new Interface({
getIdxNode: function(_idx), getIdxNode: function(_idx, _create),
removeIdxNode: function(_idx), removeIdxNode: function(_idx),
insertNodes: function(_idx, _nodes) insertNodes: function(_idx, _nodes)
});*/ });*/
@ -326,7 +334,7 @@ var et2_dataview_partitionNode = Class.extend([et2_dataview_IPartitionHeight,
var et2_dataview_partitionOrganizationNode = et2_dataview_partitionNode.extend( var et2_dataview_partitionOrganizationNode = et2_dataview_partitionNode.extend(
/*et2_dataview_IIndexOperations, */{ /*et2_dataview_IIndexOperations, */{
init: function(_parent, _pidx) { init: function(_root, _parent, _pidx) {
if (typeof _parent == "undefined") if (typeof _parent == "undefined")
{ {
@ -339,7 +347,7 @@ var et2_dataview_partitionOrganizationNode = et2_dataview_partitionNode.extend(
} }
// Call the parent constructor // Call the parent constructor
this._super(); this._super(_root);
this._children = []; this._children = [];
@ -351,7 +359,7 @@ var et2_dataview_partitionOrganizationNode = et2_dataview_partitionNode.extend(
destroy: function() { destroy: function() {
// Free all child nodes // Free all child nodes
for (var i = this._children.length - 1; i >= 0; i++) for (var i = this._children.length - 1; i >= 0; i--)
{ {
this._children[i].free(); this._children[i].free();
} }
@ -367,6 +375,7 @@ var et2_dataview_partitionOrganizationNode = et2_dataview_partitionNode.extend(
this._count = false; this._count = false;
this._depth = false; this._depth = false;
this._avgHeightData = false;
}, },
/** /**
@ -451,7 +460,7 @@ var et2_dataview_partitionOrganizationNode = et2_dataview_partitionNode.extend(
* Removes the child with the given overall index * Removes the child with the given overall index
*/ */
removeIdxNode: function(_idx) { removeIdxNode: function(_idx) {
this._iterateToIndx(_idx, function(ei, bi, child) { return this._iterateToIndex(_idx, function(ei, bi, child) {
if (child.implements(et2_dataview_IIndexOperations)) if (child.implements(et2_dataview_IIndexOperations))
{ {
return child.removeIdxNode(_idx); return child.removeIdxNode(_idx);
@ -465,7 +474,7 @@ var et2_dataview_partitionOrganizationNode = et2_dataview_partitionNode.extend(
* Returns the node with the given overall index and null if it is not found * Returns the node with the given overall index and null if it is not found
*/ */
getIdxNode: function(_idx) { getIdxNode: function(_idx) {
this._iterateToIndx(_idx, function(ei, bi, child) { return this._iterateToIndex(_idx, function(ei, bi, child) {
if (child.implements(et2_dataview_IIndexOperations)) if (child.implements(et2_dataview_IIndexOperations))
{ {
return child.getIdxNode() return child.getIdxNode()
@ -478,6 +487,15 @@ var et2_dataview_partitionOrganizationNode = et2_dataview_partitionNode.extend(
}, null); }, null);
}, },
/**
* getNodeAt returns the DOM node at the given index
*/
getNodeAt: function(_idx) {
return this._iterateToIndex(_idx, function(ei, bi, child) {
return child.getNodeAt(_idx);
}, null);
},
/** /**
* Returns all nodes in the given range * Returns all nodes in the given range
*/ */
@ -574,14 +592,14 @@ var et2_dataview_partitionOrganizationNode = et2_dataview_partitionNode.extend(
}, },
/** /**
* Reduces the given range to a placeholder * Reduces the given range to a spacer
*/ */
reduceRange: function(_range) { reduceRange: function(_range) {
this._reduce(this.getRangeNodes(_range, false)) this._reduce(this.getRangeNodes(_range, false))
}, },
/** /**
* Reduces the given index range to a placeholder * Reduces the given index range to a spacer
*/ */
reduceIdxRange: function(_range) { reduceIdxRange: function(_range) {
this._reduce(this.getIdxRangeNodes(_range, false)); this._reduce(this.getIdxRangeNodes(_range, false));
@ -747,6 +765,25 @@ var et2_dataview_partitionOrganizationNode = et2_dataview_partitionNode.extend(
this.insertNodes(0, children); this.insertNodes(0, children);
}, },
/**
* Accumulates the "average height" data
*/
getAvgHeightData: function(_data) {
if (this._avgHeightData == false)
{
this._avgHeightData = {"count": 0, "height": 0};
for (var i = 0; i < this._children.length; i++)
{
this._children[i].getAvgHeightData(_data);
}
}
// Increment the data object entries by the buffered avg height data
_data.count += this._avgHeightData.count;
_data.height += this._avgHeightData.height;
},
/* ---- PRIVATE FUNCTIONS ---- */ /* ---- PRIVATE FUNCTIONS ---- */
_copyChildren: function() { _copyChildren: function() {
@ -760,7 +797,7 @@ var et2_dataview_partitionOrganizationNode = et2_dataview_partitionNode.extend(
return children; return children;
}, },
_iterateToIndx: function(_idx, _func, _res) { _iterateToIndex: function(_idx, _func, _res) {
for (var i = 0; i < this._children.length; i++) for (var i = 0; i < this._children.length; i++)
{ {
var child = this._children[i]; var child = this._children[i];
@ -783,7 +820,7 @@ var et2_dataview_partitionOrganizationNode = et2_dataview_partitionNode.extend(
}, },
/** /**
* Reduces the given nodes to a single placeholder * Reduces the given nodes to a single spacer
*/ */
_reduce: function(_nodes) { _reduce: function(_nodes) {
if (_nodes.length == 0) if (_nodes.length == 0)
@ -791,26 +828,26 @@ var et2_dataview_partitionOrganizationNode = et2_dataview_partitionNode.extend(
return; return;
} }
// Check whether the first or last node is a placeholder, if not create // Check whether the first or last node is a spacer, if not create
// a new one // a new one
var ph; var ph;
if (_nodes[0] instanceof et2_dataview_partitionPlaceholderNode) if (_nodes[0] instanceof et2_dataview_partitionSpacerNode)
{ {
ph = _nodes[0] ph = _nodes[0]
} }
else if (_nodes[_nodes.length - 1] instanceof et2_dataview_partitionPlaceholderNode) else if (_nodes[_nodes.length - 1] instanceof et2_dataview_partitionSpacerNode)
{ {
ph = _nodes[_nodes.length - 1]; ph = _nodes[_nodes.length - 1];
} }
else else
{ {
// Create a new placeholder node an insert it at the place of the // Create a new spacer node an insert it at the place of the
// first node of the range // first node of the range
ph = new et2_dataview_partitionPlaceholderNode(); ph = new et2_dataview_partitionSpacerNode(this.getRoot());
this.getRoot().insertNodes(_nodes[0].getStartIndex(), [ph]); this.getRoot().insertNodes(_nodes[0].getStartIndex(), [ph]);
} }
// Get the height of the resulting placeholder // Get the height of the resulting spacer
var height = _nodes[_nodes.length - 1].getBottom() - _nodes[0].getTop(); var height = _nodes[_nodes.length - 1].getBottom() - _nodes[0].getTop();
// Get the count of actual elements in the nodes // Get the count of actual elements in the nodes
@ -820,11 +857,11 @@ var et2_dataview_partitionOrganizationNode = et2_dataview_partitionNode.extend(
count += _nodes[i].getCount(); count += _nodes[i].getCount();
} }
// Update the placeholder parameters // Update the spacer parameters
ph.setAvgHeight(height / count); ph.setAvgHeight(height / count);
ph.setCount(count); ph.setCount(count);
// Free all elements (except for the placeholder) // Free all elements (except for the spacer)
for (var i = _nodes.length - 1; i >= 0; i--) for (var i = _nodes.length - 1; i >= 0; i--)
{ {
if (_nodes[i] != ph) if (_nodes[i] != ph)
@ -862,22 +899,112 @@ var et2_dataview_partitionOrganizationNode = et2_dataview_partitionNode.extend(
this._children = []; this._children = [];
} }
});
/**
* The partition container node base class implements most functionality for
* nodes which have a container.
*/
var et2_dataview_partitionContainerNode = et2_dataview_partitionNode.extend({
/**
* Copies the given container object and inserts the container into the tree
* - if it is not already in there.
*/
init: function(_root, _container, _args) {
this._super(_root);
// Copy the container parameter and set its "invalidationElement" to
// this node
this.container = _container;
this.container.setInvalidationElement(this);
},
/**
* Inserts the container into the tree if it is not already inserted.
*/
initializeContainer: function() {
// Obtain the node index and insert the container nodes at the node
// returned by getNodeAt. If idx is zero, the returned node will be
// the outer container, so the container nodes have to be prepended
// to it.
var idx = this.getStartIndex();
this.container.insertIntoTree(this.getRoot().getNodeAt(idx - 1),
idx == 0);
this.invalidate();
},
/**
* Destroys the container if it is still set - e.g. the spacer node
* sets the container to null before "free" ist called in some cases, in
* order to pass the container object to another spacer node.
*/
destroy: function() {
if (this.container)
{
this.container.free();
}
this._super();
},
/**
* Returns the height of the container
*/
calculateHeight: function() {
return this.container.getHeight();
},
/**
* Calls the "insertNodeAfter" function of the container to insert the node.
*/
insertNodeAfter: function(_node) {
this.container.insertNodeAfter(_node);
},
/**
* Returns the "lastNode" of the container
*/
getNodeAt: function(_idx) {
if (_idx >= this.getStartIndex() && _idx < this.getStopIndex())
{
return this.container.getLastNode();
}
return null;
}
}); });
/** /**
* Node which represents a placeholder. Complete parts of the tree can be * Node which represents a spacer. Complete parts of the tree can be
* transformed into placeholder nodes. * transformed into spacer nodes.
*/ */
var et2_dataview_partitionPlaceholderNode = et2_dataview_partitionNode.extend({ var et2_dataview_partitionSpacerNode = et2_dataview_partitionContainerNode.extend({
init: function(_count, _avgHeight) { init: function(_root, _count, _avgHeight, _container) {
// Create the container if it has not been passed as a third parameter
var container;
if (typeof _container != "undefined")
{
container = _container;
}
else
{
container = new et2_dataview_spacer(_root.getDataProvider(),
_root.getRowProvider(), this);
}
// Call the inherited constructor // Call the inherited constructor
this._super(); this._super(_root, container);
// Copy the count and average height parameters and update the height
// of the container
this._count = _count; this._count = _count;
this._avgHeight = _avgHeight; this._avgHeight = _avgHeight;
this.container.setHeight(_count * _avgHeight);
}, },
getCount: function() { getCount: function() {
@ -889,6 +1016,7 @@ var et2_dataview_partitionPlaceholderNode = et2_dataview_partitionNode.extend({
{ {
this._count = _count; this._count = _count;
this.invalidate(); this.invalidate();
this.container.setHeight(_count * _avgHeight);
} }
}, },
@ -897,6 +1025,7 @@ var et2_dataview_partitionPlaceholderNode = et2_dataview_partitionNode.extend({
{ {
this._avgHeight = _height; this._avgHeight = _height;
this.invalidate(); this.invalidate();
this.container.setHeight(_count * _avgHeight);
} }
}, },
@ -927,23 +1056,29 @@ var et2_dataview_partitionPlaceholderNode = et2_dataview_partitionNode.extend({
if (startIdx > 0 && startIdx < this._count) if (startIdx > 0 && startIdx < this._count)
{ {
// Create a placeholder which contains the elements until startIdx // Create a spacer which contains the elements until startIdx
insertNodes.push(new et2_dataview_partitionPlaceholderNode(startIdx, ah)); insertNodes.push(new et2_dataview_partitionSpacerNode(this.getRoot(),
startIdx, ah, this.container));
this.container = null;
} }
// Calculate the current average height
ah = this.getRoot().getAverageHeight();
// Create the elements from start to stop index // Create the elements from start to stop index
for (var i = startIdx; i < stopIdx; i++) for (var i = startIdx; i < stopIdx; i++)
{ {
var rowNode = new et2_dataview_partitionRowNode(ah); var rowNode = new et2_dataview_partitionRowNode(this.getRoot(), ah);
insertNodes.push(rowNode); insertNodes.push(rowNode);
} }
if (stopIdx < this._count - 1 && stopIdx > 0) if (stopIdx < this._count - 1 && stopIdx > 0)
{ {
// Create a placeholder which contains the elements starting from // Create a spacer which contains the elements starting from
// stop index // stop index
var l = this._count - stopIdx; var l = this._count - stopIdx;
insertNodes.push(new et2_dataview_partitionPlaceholderNode(l, ah)); insertNodes.push(new et2_dataview_partitionSpacerNode(this.getRoot(),
l, ah));
} }
// Check whether insertNodes really has entrys - this is not the case // Check whether insertNodes really has entrys - this is not the case
@ -956,6 +1091,12 @@ var et2_dataview_partitionPlaceholderNode = et2_dataview_partitionNode.extend({
// Insert the newly created nodes at the original place of this node // Insert the newly created nodes at the original place of this node
parent.insertNodes(pidx, insertNodes); parent.insertNodes(pidx, insertNodes);
// Insert the newly created elements into the DOM-Tree
for (var i = 0; i < insertNodes.length; i++)
{
insertNodes[i].initializeContainer();
}
return false; return false;
} }
@ -963,7 +1104,7 @@ var et2_dataview_partitionPlaceholderNode = et2_dataview_partitionNode.extend({
}, },
getAvgHeightData: function(_data) { getAvgHeightData: function(_data) {
// Do nothing here, as the placeholders should not be inside the average // Do nothing here, as the spacers should not be inside the average
// height statistic. // height statistic.
}, },
@ -974,33 +1115,95 @@ var et2_dataview_partitionPlaceholderNode = et2_dataview_partitionNode.extend({
*/ */
var et2_dataview_partitionTree = et2_dataview_partitionOrganizationNode.extend({ var et2_dataview_partitionTree = et2_dataview_partitionOrganizationNode.extend({
init: function(_count, _avgHeight) { init: function(_dataProvider, _rowProvider, _avgHeight, _node) {
this._super(); this._super(this);
// Append a placeholder node to the children this._avgHeight = _avgHeight;
var ph = new et2_dataview_partitionPlaceholderNode(_count, _avgHeight); this._node = _node;
this._count = _dataProvider.getCount();
this._dataProvider = _dataProvider;
this._rowProvider = _rowProvider;
et2_debug("Creating partition tree with ", this._count,
" elements of avgHeight ", this._avgHeight);
// Append a spacer node to the children
var ph = new et2_dataview_partitionSpacerNode(this, this._count,
this._avgHeight);
ph.setParent(this); ph.setParent(this);
ph.initializeContainer();
this._children = [ph]; this._children = [ph];
}
});
var et2_dataview_partitionRowNode = et2_dataview_partitionNode.extend({
init: function(_avgHeight) {
this._avgHeight = _avgHeight;
}, },
calculateHeight: function() { getAverageHeight: function() {
var data = {"count": 0, "height": 0};
this.getAvgHeightData(data);
if (data.count == 0)
{
return this._avgHeight; return this._avgHeight;
} }
return data.height / data.count;
},
/**
* Returns the actual count of managed objects inside of the tree - getCount
* in contrast returns the count of "virtual" objects including the
* spacers.
*/
getManagedCount: function() {
var data = {"count": 0, "height": 0};
this.getAvgHeightData(data);
return data.count;
},
/**
* Returns the node after which new nodes have to be inserted for the given
* index.
*/
getNodeAt: function(_idx) {
// Insert the given node to the top of the parent container
if (_idx < 0)
{
return this._node;
}
// Otherwise search for the tree node with that index
return this._super(_idx);
},
getRowProvider: function() {
return this._rowProvider;
},
getDataProvider: function() {
return this._dataProvider;
}
});
var et2_dataview_partitionRowNode = et2_dataview_partitionContainerNode.extend({
init: function(_root, _avgHeight) {
var container = new et2_dataview_row(_root.getDataProvider(),
_root.getRowProvider(), this, 0);
this._super(_root, container);
this._avgHeight = _avgHeight;
},
getIdxNode: function(_node, _create) {
return this.node;
}
}); });
/*
var tree = new et2_dataview_partitionTree(1000, 20);
tree.getRangeNodes(et2_range(0, 100));
//tree.getRangeNodes(et2_range(0, 1000));
*/

View File

@ -16,29 +16,31 @@
et2_dataview_interfaces; et2_dataview_interfaces;
*/ */
var et2_dataview_row = et2_dataview_container.extend({ var et2_dataview_row = et2_dataview_container.extend(et2_dataview_IDataRow, {
init: function(_dataProvider, _rowProvider, _idx) { init: function(_dataProvider, _rowProvider, _invalidationElem, _idx) {
this._dataProvider = _dataProvider;
this._rowProvider = _rowProvider; this._super(_dataProvider, _rowProvider, _invalidationElem);
this._idx = _idx;
this._node = null; this.tr = this.rowProvider.getPrototype("default");
this._rowImpl = null; $j("div", this.tr).text("Blub");
this.appendNode(this.tr);
// Register this row in the dataprovider - if data is available for this // Register this row in the dataprovider - if data is available for this
// row the "updateData" function will be called immediately. // row the "updateData" function will be called immediately.
this._dataProvider.registerDataRow(_idx, this); //this.dataProvider.registerDataRow(_idx, this);
if (this._node == null) // if (this.tr == null)
{ // {
} // }
}, },
destroy: function() { destroy: function() {
this._dataProvider.unregisterDataRow(_idx); //this.dataProvider.unregisterDataRow(_idx);
}, },
updateData: function(_data) { updateData: function(_data) {
} }
}); });

View File

@ -21,16 +21,16 @@
*/ */
var et2_dataview_rowProvider = Class.extend({ var et2_dataview_rowProvider = Class.extend({
init: function(_gridId, _columnIds) { init: function(_outerId, _columnIds) {
// Copy the given parameters // Copy the given parameters
this._dataProvider = _dataProvider; this._outerId = _outerId;
this._gridId = _gridId;
this._columnIds = _columnIds; this._columnIds = _columnIds;
this._prototypes = {}; this._prototypes = {};
// Create the default row "prototypes" // Create the default row "prototypes"
this._createFullRowPrototype(); this._createFullRowPrototype();
this._createDefaultPrototype(); this._createDefaultPrototype();
this._createEmptyPrototype();
}, },
/** /**
@ -43,8 +43,8 @@ var et2_dataview_rowProvider = Class.extend({
{ {
if (typeof _generator != "undefined") if (typeof _generator != "undefined")
{ {
this._prototypes[_name] = _generator.call(_context, _gridId, this._prototypes[_name] = _generator.call(_context, this._outerId,
_columnIds); this._columnIds);
} }
else else
{ {
@ -63,7 +63,7 @@ var et2_dataview_rowProvider = Class.extend({
.attr("span", this._columnIds.length) .attr("span", this._columnIds.length)
.appendTo(tr); .appendTo(tr);
var div = $j(document.createElement("div")) var div = $j(document.createElement("div"))
.addClass(this._gridId + "_div_fullRow") .addClass(this._outerId + "_div_fullRow")
.appendTo(td); .appendTo(td);
this._prototypes["fullRow"] = tr; this._prototypes["fullRow"] = tr;
@ -76,14 +76,18 @@ var et2_dataview_rowProvider = Class.extend({
for (var i = 0; i < this._columnIds.length; i++) for (var i = 0; i < this._columnIds.length; i++)
{ {
var td = $j(document.createElement("td")) var td = $j(document.createElement("td"))
.addClass(this._gridId + "_td_" + this._columnIds[i]) .addClass(this._outerId + "_td_" + this._columnIds[i])
.appendTo(tr); .appendTo(tr);
var div = $j(document.createElement("div")) var div = $j(document.createElement("div"))
.addClass(this._gridId + "_div_" + this._columnIds[i]) .addClass(this._outerId + "_div_" + this._columnIds[i])
.appendTo(td); .appendTo(td);
} }
this._prototypes["default"] = tr; this._prototypes["default"] = tr;
},
_createEmptyPrototype: function() {
this._prototypes["empty"] = $j(document.createElement("tr"));
} }
}); });

View File

@ -0,0 +1,53 @@
/**
* eGroupWare eTemplate2 - Class which contains the spacer container
*
* @license http://opensource.org/licenses/gpl-license.php GPL - GNU General Public License
* @package etemplate
* @subpackage dataview
* @link http://www.egroupware.org
* @author Andreas Stöckel
* @copyright Stylite 2011
* @version $Id$
*/
"use strict";
/*egw:uses
jquery.jquery;
et2_dataview_view_container;
*/
var et2_dataview_spacer = et2_dataview_container.extend({
init: function(_dataProvider, _rowProvider, _invalidationElem) {
// Call the inherited container constructor
this._super(_dataProvider, _rowProvider, _invalidationElem);
// Get the spacer row and append it to the container
this.spacerNode = this.rowProvider.getPrototype("spacer",
this._createSpacerPrototype, this);
this._phDiv = $j("td", this.spacerNode);
this.appendNode(this.spacerNode);
},
setHeight: function(_height) {
this._phDiv.height(_height);
},
/* ---- PRIVATE FUNCTIONS ---- */
_createSpacerPrototype: function(_outerId, _columnIds) {
var tr = $j(document.createElement("tr"));
var td = $j(document.createElement("td"))
.addClass("egwGridView_spacer")
.addClass(_outerId + "_spacer_fullRow")
.attr("colspan", _columnIds.length)
.appendTo(tr);
return tr;
}
});

View File

@ -21,6 +21,8 @@
et2_widget_grid; et2_widget_grid;
et2_widget_selectbox; et2_widget_selectbox;
et2_extension_nextmatch_dynheight; et2_extension_nextmatch_dynheight;
et2_dataview_gridContainer;
et2_dataview_dataProvider;
*/ */
/** /**
@ -67,8 +69,13 @@ var et2_nextmatch = et2_DOMWidget.extend(et2_IResizeable, {
// container. // container.
this.dynheight = new et2_dynheight(null, this.div, 150); this.dynheight = new et2_dynheight(null, this.div, 150);
// Create the data provider which cares about streaming the row data
// efficiently to the rows
this.dataProvider = new et2_dataview_dataProvider();
// Create the outer grid container // Create the outer grid container
this.dataviewContainer = new et2_dataview_gridContainer(this.div); this.dataviewContainer = new et2_dataview_gridContainer(this.div,
this.dataProvider);
this.activeFilters = {}; this.activeFilters = {};
}, },
@ -78,6 +85,7 @@ var et2_nextmatch = et2_DOMWidget.extend(et2_IResizeable, {
*/ */
destroy: function() { destroy: function() {
this.dataviewContainer.free(); this.dataviewContainer.free();
this.dataProvider.free();
this.dynheight.free(); this.dynheight.free();
this._super.apply(this, arguments); this._super.apply(this, arguments);

View File

@ -32,6 +32,8 @@
.egwGridView_scrollarea { .egwGridView_scrollarea {
width: 100%; width: 100%;
overflow: auto; overflow: auto;
background-color: white;
} }
.egwGridView_spacer { .egwGridView_spacer {

View File

@ -35,7 +35,16 @@
<script src="../et2_extension_nextmatch.js"></script> <script src="../et2_extension_nextmatch.js"></script>
<script src="../et2_extension_nextmatch_dynheight.js"></script> <script src="../et2_extension_nextmatch_dynheight.js"></script>
<script src="../et2_dataview_interfaces.js"></script>
<script src="../et2_dataview_model_dataProvider.js"></script>
<script src="../et2_dataview_model_columns.js"></script> <script src="../et2_dataview_model_columns.js"></script>
<script src="../et2_dataview_view_rowProvider.js"></script>
<script src="../et2_dataview_view_partitionTree.js"></script>
<script src="../et2_dataview_view_grid.js"></script>
<script src="../et2_dataview_view_container.js"></script>
<script src="../et2_dataview_view_row.js"></script>
<script src="../et2_dataview_view_spacer.js"></script>
<script src="../et2_dataview_view_gridcontainer.js"></script> <script src="../et2_dataview_view_gridcontainer.js"></script>
<script src="../etemplate2.js"></script> <script src="../etemplate2.js"></script>