diff --git a/etemplate/js/et2_dataview_model_dataProvider.js b/etemplate/js/et2_dataview_model_dataProvider.js
index 732be00e52..5567e4daca 100644
--- a/etemplate/js/et2_dataview_model_dataProvider.js
+++ b/etemplate/js/et2_dataview_model_dataProvider.js
@@ -20,7 +20,7 @@
var et2_dataview_dataProvider = Class.extend({
getCount: function() {
- return 10;
+ return 10000;
},
registerDataRow: function(_idx, _dataRow) {
diff --git a/etemplate/js/et2_dataview_view_grid.js b/etemplate/js/et2_dataview_view_grid.js
index a7ec6ef1be..7a8c670882 100644
--- a/etemplate/js/et2_dataview_view_grid.js
+++ b/etemplate/js/et2_dataview_view_grid.js
@@ -15,8 +15,8 @@
/*egw:uses
jquery.jquery;
et2_core_common;
+
et2_dataview_interfaces;
- et2_dataview_view_row;
et2_dataview_view_partitionTree;
*/
@@ -28,7 +28,9 @@ var ET2_GRID_VIEW_EXT = 25;
/**
* Determines the timeout after which the scroll-event is processed.
*/
-var ET2_GRID_SCROLL_TIMEOUT = 25;
+var ET2_GRID_SCROLL_TIMEOUT = 100;
+
+var partitionTree = null;
var et2_dataview_grid = Class.extend(et2_dataview_IViewRange, {
@@ -88,7 +90,7 @@ var et2_dataview_grid = Class.extend(et2_dataview_IViewRange, {
// Create the partition tree object which is used to organize the tree
// items.
- this._partitionTree = new et2_dataview_partitionTree(this._dataProvider,
+ partitionTree = this._partitionTree = new et2_dataview_partitionTree(this._dataProvider,
this._rowProvider, this._avgHeight, this.innerTbody);
// Setup the "rebuild" timer - it rebuilds the complete partition tree
@@ -138,7 +140,7 @@ var et2_dataview_grid = Class.extend(et2_dataview_IViewRange, {
// Deactivated the code below for testing purposes
// Calculate the range of the actually shown elements
- /*var displayTop = _range.top;
+ var displayTop = _range.top;
var displayBottom = _range.bottom;
if (nodes.length > 0)
@@ -149,18 +151,21 @@ var et2_dataview_grid = Class.extend(et2_dataview_IViewRange, {
// Hide everything except for _holdCount elements at the top and bottom
// of the viewrange
- var reduceHeight = this._partitionTree.getAverageHeight() * this._holdCount;
+ var ah = this._partitionTree.getAverageHeight();
+ var reduceHeight = ah * this._holdCount;
if (displayTop > reduceHeight)
{
- this._partitionTree.reduce(et2_bounds(0, displayTop - reduceHeight));
+ console.log("/\\");
+ this._partitionTree.reduceRange(et2_bounds(0, displayTop - reduceHeight));
}
if (displayBottom + reduceHeight < this._partitionTree.getHeight())
{
- this._partitionTree.reduce(et2_bounds(displayBottom + reduceHeight,
+ console.log("\\/");
+ this._partitionTree.reduceRange(et2_bounds(displayBottom + reduceHeight,
this._partitionTree.getHeight()));
- }*/
+ }
},
/**
@@ -202,7 +207,9 @@ var et2_dataview_grid = Class.extend(et2_dataview_IViewRange, {
// regarding to the count of elements managed in it
if (count < Math.pow(ET2_PARTITION_TREE_WIDTH, depth - 1))
{
+ et2_debug("info", "Rebuilding dataview partition tree");
this._partitionTree.rebuild();
+ et2_debug("info", "Done.");
}
// Reset the "treeChanged" function.
@@ -235,6 +242,9 @@ var et2_dataview_grid = Class.extend(et2_dataview_IViewRange, {
// Set a new timeout which calls the setViewArea
// function
e.data._scrollTimeout = window.setTimeout(function() {
+ if (typeof ET2_GRID_PAUSE != "undefined")
+ return;
+
e.data.setViewRange(et2_range(
e.data.scrollarea.scrollTop() - ET2_GRID_VIEW_EXT,
e.data._height + ET2_GRID_VIEW_EXT * 2
diff --git a/etemplate/js/et2_dataview_view_gridcontainer.js b/etemplate/js/et2_dataview_view_gridContainer.js
similarity index 99%
rename from etemplate/js/et2_dataview_view_gridcontainer.js
rename to etemplate/js/et2_dataview_view_gridContainer.js
index 1f8d23609b..294fe10cdc 100644
--- a/etemplate/js/et2_dataview_view_gridcontainer.js
+++ b/etemplate/js/et2_dataview_view_gridContainer.js
@@ -16,6 +16,8 @@
jquery.jquery;
et2_core_common;
et2_core_stylesheet;
+
+ et2_dataview_view_grid;
*/
/**
diff --git a/etemplate/js/et2_dataview_view_partitionContainerNodes.js b/etemplate/js/et2_dataview_view_partitionContainerNodes.js
new file mode 100644
index 0000000000..3e5fce8208
--- /dev/null
+++ b/etemplate/js/et2_dataview_view_partitionContainerNodes.js
@@ -0,0 +1,258 @@
+/**
+ * eGroupWare eTemplate2 - Class which contains an management tree for the grid rows
+ *
+ * @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_core_common; // for et2_range functions
+ et2_core_inheritance;
+ et2_dataview_interfaces;
+ et2_dataview_view_partitionNode;
+*/
+
+/**
+ * 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 spacer. Complete parts of the tree can be
+ * transformed into spacer nodes.
+ */
+var et2_dataview_partitionSpacerNode = et2_dataview_partitionContainerNode.extend({
+
+ 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
+ this._super(_root, container);
+
+ // Copy the count and average height parameters - this updates the height
+ // of the outer container
+ this.setParameters(_count, _avgHeight);
+ },
+
+ getCount: function() {
+ return this._count;
+ },
+
+ getAvgHeight: function() {
+ return this._avgHeight;
+ },
+
+ setParameters: function(_count, _avgHeight) {
+ if (_count != this._count || _avgHeight != this._avgHeight)
+ {
+
+ // Copy the given parameters
+ this._count = _count;
+ this._avgHeight = _avgHeight;
+
+ // Invalidate this element
+ this.invalidate();
+
+ // Call the container function to set the total height
+ this.container.setHeight(this._count * this._avgHeight);
+ }
+ },
+
+ /**
+ * Creates the nodes which fall in the given range and returns them
+ */
+ getRangeNodes: function(_range, _create) {
+
+ // If no new nodes should be created, simply return this node
+ if (!_create)
+ {
+ return this._super(_range);
+ }
+
+ var insertNodes = [];
+
+ // Copy parent and pidx as we'll have to access those objects after this
+ // one gets freed
+ var parent = this._parent;
+ var pidx = this._pidx;
+
+ // Get the top and bottom of this node
+ var t = this.getPosTop();
+ var b = this.getPosBottom();
+
+ // Get the start and stop index of the elements which have to be
+ // created.
+ var ah = this._avgHeight;
+ var startIdx = Math.max(0, Math.floor((_range.top - t) / ah));
+ var stopIdx = Math.min(this._count, Math.ceil((_range.bottom - t) / ah));
+
+ if (startIdx > 0 && startIdx < this._count)
+ {
+ // Create a spacer which contains the elements until startIdx
+ 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
+ for (var i = startIdx; i < stopIdx; i++)
+ {
+ var rowNode = new et2_dataview_partitionRowNode(this.getRoot(), ah);
+ insertNodes.push(rowNode);
+ }
+
+ if (stopIdx < this._count - 1 && stopIdx > 0)
+ {
+ // Create a spacer which contains the elements starting from
+ // stop index
+ var l = this._count - stopIdx;
+ insertNodes.push(new et2_dataview_partitionSpacerNode(this.getRoot(),
+ l, ah));
+ }
+
+ // Check whether insertNodes really has entrys - this is not the case
+ // if the given range is just outside the range of this element
+ if (insertNodes.length > 0)
+ {
+ // Free this element
+ this.free();
+
+ // Insert the newly created nodes at the original place of this node
+ 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 [];
+ },
+
+ getAvgHeightData: function(_data) {
+ // Do nothing here, as the spacers should not be inside the average
+ // height statistic.
+ },
+
+});
+
+var et2_dataview_partitionRowNode = et2_dataview_partitionContainerNode.extend({
+
+ init: function(_root, _avgHeight) {
+
+ var container = new et2_dataview_row(_root.getDataProvider(),
+ _root.getRowProvider(), this);
+
+ this._super(_root, container);
+
+ this._avgHeight = _avgHeight;
+ },
+
+ initializeContainer: function() {
+ this._super();
+
+ this.container.setIdx(this.getStartIndex());
+ },
+
+ getIdxNode: function(_node, _create) {
+ return this.node;
+ }
+
+});
+
+
diff --git a/etemplate/js/et2_dataview_view_partitionNode.js b/etemplate/js/et2_dataview_view_partitionNode.js
new file mode 100644
index 0000000000..9f5a532c59
--- /dev/null
+++ b/etemplate/js/et2_dataview_view_partitionNode.js
@@ -0,0 +1,317 @@
+/**
+ * eGroupWare eTemplate2 - Class which contains an management tree for the grid rows
+ *
+ * @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_core_common; // for et2_range functions
+ et2_core_inheritance;
+ et2_dataview_interfaces;
+*/
+
+/**
+ * The partition node tree manages all rows in a dataview. As a dataview may have
+ * many thousands of lines, the rows are organized in an a tree. The leafs of the
+ * tree represent the single rows, upper layers represent groups of nodes.
+ * Each node has a "height" value and is capable of calculate the exact position
+ * of a row and its top and bottom value.
+ * Additionaly, a leaf can represent an unlimited number of rows. In this way
+ * the partition tree is built dynamically and is also capable of "forgetting"
+ * information about the rows by simply reducing the tree nodes at a certain
+ * position.
+ */
+
+var et2_dataview_IPartitionHeight = new Interface({
+
+ calculateHeight: function() {}
+
+});
+
+/**
+ * Abstract base class for partition nodes - contains the code for calculating
+ * the top, bottom, height and (start) index of the node
+ */
+var et2_dataview_partitionNode = Class.extend([et2_dataview_IPartitionHeight,
+ et2_dataview_IInvalidatable], {
+
+ init: function(_root) {
+
+ this._root = _root;
+ this._parent = null;
+ this._pidx = 0;
+
+ // Initialize the temporary storage elements
+ this.doInvalidate();
+ this._invalid = true;
+
+ },
+
+ destroy: function() {
+
+ // Remove this element from the parent children list
+ if (this._parent)
+ {
+ this._parent.removePIdxNode(this._pidx);
+ }
+
+ },
+
+ setParent: function(_parent) {
+ if (this._parent != _parent)
+ {
+ this._parent = _parent;
+ this.invalidate();
+ }
+ },
+
+ setPIdx: function(_pidx) {
+ if (this._pidx != _pidx)
+ {
+ this._pidx = _pidx;
+ this.invalidate();
+ }
+ },
+
+ /**
+ * Invalidates cached values - override the "doInvalidate" function.
+ *
+ * @param _sender is the node wich originally triggerd the invalidation, can
+ * be ommited when calling this function.
+ */
+ invalidate: function(_sender) {
+
+ // If the _sender parameter is not given, assume that this element is
+ // the one which triggered the invalidation
+ var origin = typeof _sender == "undefined";
+
+ if ((origin || _sender != this) && !this._invalid)
+ {
+ this.doInvalidate();
+ this._invalid = true;
+
+ // Invalidate the parent node
+ if (this._parent)
+ {
+ this._parent.invalidate(origin ? this : _sender);
+ }
+ }
+ },
+
+ /**
+ * Performs the actual invalidation.
+ */
+ doInvalidate: function() {
+ this._height = false;
+ this._posTop = false;
+ this._posBottom = false;
+ this._startIdx = false;
+ this._stopIdx = false;
+ },
+
+ /**
+ * Returns the root node of the partition tree
+ */
+ getRoot: function() {
+ return this._root;
+ },
+
+ /**
+ * Returns the height of this node
+ */
+ getHeight: function() {
+ // Calculate the height value if it is currently invalid
+ if (this._height === false)
+ {
+ this._height = this.calculateHeight();
+
+ // Do a sanity check for the value - if the height wasn't a number
+ // it could easily destroy the posTop and posBottom values of the
+ // complete tree!
+ if (isNaN(this._height))
+ {
+ et2_debug("error", "calculateHeight returned a NaN value!");
+ this._height = 0;
+ }
+
+ this._invalid = false;
+ }
+
+ return this._height;
+ },
+
+ /**
+ * Returns the top position of the node in px
+ */
+ getPosTop: function() {
+ if (this._posTop === false)
+ {
+ this._posTop = this._accumulateValue(this.getPosTop, this.getPosBottom);
+
+ this._invalid = false;
+ }
+
+ return this._posTop;
+ },
+
+ /**
+ * Returns the bottom position of the node in px
+ */
+ getPosBottom: function() {
+ if (this._posBottom === false)
+ {
+ this._posBottom = this.getPosTop() + this.getHeight();
+
+ this._invalid = false;
+ }
+
+ return this._posBottom;
+ },
+
+ /**
+ * Returns an range object
+ */
+ getRange: function() {
+ return {
+ "top": this.getPosTop(),
+ "bottom": this.getPosBottom()
+ };
+ },
+
+
+ /**
+ * Returns true if the node intersects with the given range
+ */
+ inRange: function(_ar) {
+ return et2_rangeIntersect(this.getRange(), _ar);
+ },
+
+ /**
+ * Returns the overall start index of the node
+ */
+ getStartIndex: function() {
+ if (this._startIdx === false)
+ {
+ this._startIdx = this._accumulateValue(this.getStartIndex,
+ this.getStopIndex);
+
+ this._invalid = false;
+ }
+
+ return this._startIdx;
+ },
+
+ /**
+ * Returns the overall stop index of the node
+ */
+ getStopIndex: function() {
+ if (this._stopIdx === false)
+ {
+ this._stopIdx = this.getStartIndex() + this.getCount();
+
+ this._invalid = false;
+ }
+
+ return this._stopIdx;
+ },
+
+ /**
+ * Returns the index range object
+ */
+ getIdxRange: function() {
+ return {
+ "top": this.getStartIndex(),
+ "bottom": this.getStopIndex()
+ };
+ },
+
+ /**
+ * Checks whether this element is inside the given index range
+ */
+ inIdxRange: function(_idxRange) {
+ return et2_rangeIntersect(this.getIdxRange, _idxRange);
+ },
+
+ /**
+ * Returns the count of leafs which are below this node
+ */
+ getCount: function() {
+ return 1;
+ },
+
+ /**
+ * Returns the nodes which reside in the given range
+ */
+ getRangeNodes: function(_range, _create) {
+ if (this.inRange(_range))
+ {
+ return [this];
+ }
+
+ return [];
+ },
+
+ /**
+ * Returns the nodes which are inside the given index range
+ */
+ getIdxRangeNodes: function(_idxRange, _create) {
+ if (this.inIdxRange(_idxRange))
+ {
+ return [this];
+ }
+
+ return [];
+ },
+
+ /**
+ * Returns the (maximum) depth of the tree
+ */
+ getDepth: function() {
+ return 1;
+ },
+
+ getAvgHeightData: function(_data) {
+ _data.count++;
+ _data.height += this.getHeight();
+ },
+
+ getNodeIdx: function(_idx, _create) {
+ return null;
+ },
+
+ getRowProvider: function() {
+ return this.getRoot().getRowProvider();
+ },
+
+ getDataProvider: function() {
+ return this.getRoot().getDataProvider();
+ },
+
+ /* ---- PRIVATE FUNCTIONS ---- */
+
+ _accumulateValue: function(_f1, _f2) {
+ if (this._parent)
+ {
+ if (this._pidx == 0)
+ {
+ return _f1.call(this._parent);
+ }
+ else
+ {
+ return _f2.call(this._parent._children[this._pidx - 1]);
+ }
+ }
+
+ return 0;
+ }
+
+});
+
diff --git a/etemplate/js/et2_dataview_view_partitionOrganizationNode.js b/etemplate/js/et2_dataview_view_partitionOrganizationNode.js
new file mode 100644
index 0000000000..517e44dfd5
--- /dev/null
+++ b/etemplate/js/et2_dataview_view_partitionOrganizationNode.js
@@ -0,0 +1,642 @@
+/**
+ * eGroupWare eTemplate2 - Class which implements the organization node
+ *
+ * @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_core_common; // for et2_range functions
+ et2_core_inheritance;
+ et2_dataview_interfaces;
+ et2_dataview_view_partitionNode;
+*/
+
+/**
+ * The ET2_PARTITION_TREE_WIDTH defines the count of children a node will be
+ * created with.
+ */
+var ET2_PARTITION_TREE_WIDTH = 10;
+
+/**
+ * An partition tree organization node can contain child nodes and organizes
+ * those.
+ */
+var et2_dataview_partitionOrganizationNode = et2_dataview_partitionNode.extend(
+ /*et2_dataview_IIndexOperations, */{
+
+ init: function(_root, _parent, _pidx) {
+
+ if (typeof _parent == "undefined")
+ {
+ _parent = null;
+ }
+
+ if (typeof _pidx == "undefined")
+ {
+ _pidx = 0;
+ }
+
+ // Call the parent constructor
+ this._super(_root);
+
+ this._children = [];
+
+ // Set the given parent and parent-index
+ this.setParent(_parent);
+ this.setPIdx(_pidx);
+
+ },
+
+ destroy: function() {
+ // Free all child nodes
+ for (var i = this._children.length - 1; i >= 0; i--)
+ {
+ this._children[i].free();
+ }
+
+ this._super();
+ },
+
+ /**
+ * Delete the buffered element count
+ */
+ doInvalidate: function() {
+ this._super();
+
+ this._count = false;
+ this._depth = false;
+ this._avgHeightData = false;
+ },
+
+ /**
+ * Calculates the count of elements by accumulating the counts of the child
+ * elements.
+ */
+ getCount: function() {
+ if (this._count === false)
+ {
+ // Calculate the count of nodes
+ this._count = 0;
+ for (var i = 0; i < this._children.length; i++)
+ {
+ this._count += this._children[i].getCount();
+ }
+ }
+
+ return this._count;
+ },
+
+ /**
+ * Calculates the height of this node by accumulating the height of the
+ * child nodes.
+ */
+ calculateHeight: function() {
+ var result = 0;
+ for (var i = 0; i < this._children.length; i++)
+ {
+ result += this._children[i].getHeight();
+ }
+
+ return result;
+ },
+
+ /**
+ * Removes the given node from the tree
+ */
+ removeNode: function(_node) {
+ // Search the element on this level
+ for (var i = 0; i < this._children.length; i++)
+ {
+ if (this._children[i] == _node)
+ {
+ this.removePIdxNode(i);
+ return true;
+ }
+ }
+
+ // Search the element on a lower level
+ for (var i = 0; i < this._children.length; i++)
+ {
+ if (this._children[i] instanceof et2_dataview_partitionOrganizationNode &&
+ this._children[i].removeNode(_node))
+ {
+ return true;
+ }
+ }
+
+ return false;
+ },
+
+ /**
+ * Removes the child with the given index in the _children list
+ */
+ removePIdxNode: function(_pidx) {
+ // Invalidate this element
+ this.invalidate();
+
+ // Delete the element at the given pidx and remove the parent reference
+ this._children.splice(_pidx, 1)[0].setParent(null);
+
+ // Recalculate the pidx of the children behind the one removed
+ for (var i = _pidx; i < this._children.length; i++)
+ {
+ this._children[i]._pidx--;
+ }
+
+ return true;
+ },
+
+ /**
+ * Removes the child with the given overall index
+ */
+ removeIdxNode: function(_idx) {
+ return this._iterateToIndex(_idx, function(ei, bi, child) {
+ if (child.implements(et2_dataview_IIndexOperations))
+ {
+ return child.removeIdxNode(_idx);
+ }
+
+ return this.removePIdxNode(i);
+ }, false);
+ },
+
+ /**
+ * Returns the node with the given overall index and null if it is not found
+ */
+ getIdxNode: function(_idx) {
+ return this._iterateToIndex(_idx, function(ei, bi, child) {
+ if (child.implements(et2_dataview_IIndexOperations))
+ {
+ return child.getIdxNode()
+ }
+
+ if (idx == bi)
+ {
+ return child;
+ }
+ }, 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
+ */
+ getRangeNodes: function(_range, _create) {
+
+ if (typeof _create == "undefined")
+ {
+ _create = true;
+ }
+
+ var result = [];
+
+ // Create a copy of the children of this element, as the child list may
+ // change due to new children being inserted.
+ var children = this._copyChildren();
+
+ // We did not have a intersect in the range now
+ var hadIntersect = false;
+ for (var i = 0; i < children.length; i++)
+ {
+ if (children[i].inRange(_range))
+ {
+ hadIntersect = true;
+
+ var res = children[i].getRangeNodes(_range, _create);
+
+ if (res === false)
+ {
+ return this.getRangeNodes(_range, _create);
+ }
+
+ // Append the search results of the given element
+ result = result.concat(res);
+ }
+ else
+ {
+ // Abort as we are out of the range where intersects can happen
+ if (hadIntersect)
+ {
+ break;
+ }
+ }
+ }
+
+ return result;
+ },
+
+ /**
+ * Returns the nodes which are inside the given range
+ */
+ getIdxRangeNodes: function(_idxRange, _create) {
+
+ if (typeof _create == "undefined")
+ {
+ _create = true;
+ }
+
+ var result = [];
+
+ // Create a copy of the children of this element, as the child list may
+ // change due to new children being inserted.
+ var children = this._copyChildren();
+
+ // We did not have a intersect in the range now
+ var hadIntersect = false;
+ for (var i = 0; i < children.length; i++)
+ {
+ if (children[i].inIdxRange(_idxRange))
+ {
+ hadIntersect = true;
+
+ // Append the search results of the given element
+ var res = children[i].getIdxRangeNodes(_idxRange,
+ _create);
+
+ if (res === false)
+ {
+ return this.getIdxRangeNodes(_idxRange, _create);
+ }
+
+ result = result.concat(res);
+ }
+ else
+ {
+ // Abort as we are out of the range where intersects can happen
+ if (hadIntersect)
+ {
+ break;
+ }
+ }
+ }
+
+ return result;
+ },
+
+ /**
+ * Reduces the given range to a spacer
+ */
+ reduceRange: function(_range) {
+ this._reduce(this.getRangeNodes(_range, false))
+ },
+
+ /**
+ * Reduces the given index range to a spacer
+ */
+ reduceIdxRange: function(_range) {
+ this._reduce(this.getIdxRangeNodes(_range, false));
+ },
+
+ getDepth: function() {
+ if (this._depth === false)
+ {
+ this._depth = 0;
+
+ // Get the maximum depth and increase it by one
+ for (var i = 0; i < this._children.length; i++)
+ {
+ this._depth = Math.max(this._depth, this._children[i].getDepth());
+ }
+ this._depth++;
+ }
+
+ return this._depth;
+ },
+
+ _insertLeft: function(_idx, _nodes) {
+ // Check whether the node left to the given index can still take some
+ // nodes - if yes, insert the maximum amount of nodes into this node
+ if (_idx > 0 && this._children[_idx - 1] instanceof et2_dataview_partitionOrganizationNode
+ && this._children[_idx - 1]._children.length < ET2_PARTITION_TREE_WIDTH)
+ {
+ // Calculate how many children can be inserted into the left node
+ var child = this._children[_idx - 1];
+ var c = Math.min(ET2_PARTITION_TREE_WIDTH - child._children.length, _nodes.length);
+
+ // Insert the remaining children into the left node
+ if (c > 0)
+ {
+ var nodes = _nodes.splice(0, c);
+ child.insertNodes(child._children.length, nodes);
+ }
+ }
+ },
+
+ _insertRight: function(_idx, _nodes) {
+ // Check whether the node right to the given index can still take some
+ // nodes - if yes, insert the nodes there
+ if (_idx < this._children.length &&
+ this._children[_idx] instanceof et2_dataview_partitionOrganizationNode &&
+ this._children[_idx]._children.length < ET2_PARTITION_TREE_WIDTH)
+ {
+ var child = this._children[_idx];
+ var c = Math.min(ET2_PARTITION_TREE_WIDTH - child._children.length, _nodes.length);
+
+ // Insert the remaining children into the left node
+ if (c > 0)
+ {
+ var nodes = _nodes.splice(_nodes.length - c, c);
+ child.insertNodes(0, nodes);
+ }
+ }
+ },
+
+ /**
+ * Groups the nodes which should be inserted by packages of ten and insert
+ * those as children. If there are more than ET2_PARTITION_TREE_WIDTH
+ * children as a result of this action, this node gets destroyed and the
+ * children are given to the parent node.
+ */
+ insertNodes: function(_idx, _nodes) {
+ // Break if no nodes are to be inserted
+ if (_nodes.length == 0)
+ {
+ return;
+ }
+
+ // Invalidate this node
+ this.invalidate();
+
+ // Try to insert the given objects into an organization node at the left
+ // or right side of the given index
+ this._insertLeft(_idx, _nodes);
+ this._insertRight(_idx, _nodes);
+
+ // Update the pidx of the nodes after _idx
+ for (var i = _idx; i < this._children.length; i++)
+ {
+ this._children[i].setPIdx(i + _nodes.length);
+ }
+
+ // Set the parent and the pidx of the new nodes
+ for (var i = 0; i < _nodes.length; i++)
+ {
+ _nodes[i].setParent(this);
+ _nodes[i].setPIdx(_idx + i);
+ }
+
+ // Simply insert the nodes at the given position
+ this._children.splice.apply(this._children, [_idx, 0].concat(_nodes));
+
+ // Check whether the width of this element is greater than ET2_PARTITION_TREE_WIDTH
+ // If yes, split the children into groups of ET2_PARTITION_TREE_WIDTH and
+ // insert those into this node
+ /*if (this._children.length > ET2_PARTITION_TREE_WIDTH)
+ {
+ var insertNodes = [];
+
+ while (_nodes.length > 0)
+ {
+ var orgaNode = new et2_dataview_partitionOrganizationNode(this,
+ insertNodes.length);
+
+ // Get groups of ET2_PARTITION_TREE_WIDTH from the nodes while
+ // reading the first level of nodes from organization nodes
+ var newNodes = [];
+ var isPartial = false;
+ while (newNodes.length < ET2_PARTITION_TREE_WIDTH && _nodes.length > 0)
+ {
+ var node = _nodes[0];
+
+ if (!(node instanceof et2_dataview_partitionOrganizationNode))
+ {
+ newNodes.push(_nodes.shift());
+ isPartial = true;
+ }
+ else
+ {
+ if (node._children.length == 0)
+ {
+ // Remove the node from the list and free it
+ _nodes.shift().free();
+ }
+ else
+ {
+ if (!isPartial && node._children.length == ET2_PARTITION_TREE_WIDTH)
+ {
+ newNodes.push(_nodes.shift());
+ }
+ else
+ {
+ newNodes = newNodes.concat(node._children.splice(0,
+ ET2_PARTITION_TREE_WIDTH - newNodes.length));
+ isPartial = true;
+ }
+ }
+ }
+ }
+
+ orgaNode.insertNodes(0, newNodes);
+
+ insertNodes.push(orgaNode);
+ }
+
+ this._children = [];
+ this.insertNodes(0, insertNodes);
+ }*/
+ },
+
+ rebuild: function() {
+ // Get all leafs
+ var children = [];
+ this._getFlatList(children);
+
+ // Free all organization nodes
+ this._clear();
+
+ 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(this._avgHeightData);
+ }
+
+ this._invalid = false;
+ }
+
+ // Increment the data object entries by the buffered avg height data
+ _data.count += this._avgHeightData.count;
+ _data.height += this._avgHeightData.height;
+ },
+
+ debug: function() {
+ var children = [];
+ var offs = false;
+ this._getFlatList(children);
+
+ for (var i = 0; i < children.length; i++)
+ {
+ var idx = children[i].getStartIndex();
+ var node = children[i].getNodeAt(idx);
+
+ if (node)
+ {
+ if (offs === false)
+ {
+ offs = node.offset().top;
+ }
+
+ var actualTop = node.offset().top - offs;
+ var calculatedTop = children[i].getPosTop();
+
+ if (Math.abs(actualTop - calculatedTop) > 1)
+ {
+ et2_debug("warn", i, "Position missmatch at idx ", idx,
+ actualTop, calculatedTop, node);
+ }
+
+ var actualHeight = node.outerHeight(true);
+ var calculateHeight = children[i].getHeight();
+
+ if (Math.abs(actualHeight - calculateHeight) > 1)
+ {
+ et2_debug("warn", i, "Height missmatch at idx ", idx,
+ actualHeight, calculateHeight, node);
+ }
+ }
+ }
+ },
+
+ /* ---- PRIVATE FUNCTIONS ---- */
+
+ _copyChildren: function() {
+ // Copy the child array as querying the child nodes may change the tree
+ var children = new Array(this._children.length);
+ for (var i = 0; i < this._children.length; i++)
+ {
+ children[i] = this._children[i];
+ }
+
+ return children;
+ },
+
+ _iterateToIndex: function(_idx, _func, _res) {
+ for (var i = 0; i < this._children.length; i++)
+ {
+ var child = this._children[i];
+
+ var bi = child.getStartIndex();
+ var ei = child.getStopIndex();
+
+ if (bi > _idx)
+ {
+ return res;
+ }
+
+ if (bi <= _idx && ei > _idx)
+ {
+ return _func.call(this, bi, ei, child);
+ }
+ }
+
+ return res;
+ },
+
+ /**
+ * Reduces the given nodes to a single spacer
+ */
+ _reduce: function(_nodes) {
+/* if (_nodes.length == 0)
+ {
+ return;
+ }
+
+ // Check whether the first or last node is a spacer, if not create
+ // a new one
+ var ph;
+ if (_nodes[0] instanceof et2_dataview_partitionSpacerNode)
+ {
+ ph = _nodes[0]
+ }
+ else if (_nodes[_nodes.length - 1] instanceof et2_dataview_partitionSpacerNode)
+ {
+ ph = _nodes[_nodes.length - 1];
+ }
+ else
+ {
+ // Create a new spacer node and insert it at the place of the
+ // first node of the range
+ ph = new et2_dataview_partitionSpacerNode(this.getRoot(), 0, 0);
+ this.getRoot().insertNodes(_nodes[0].getStartIndex(), [ph]);
+ }
+
+ // Get the height of the resulting spacer
+ var height = _nodes[_nodes.length - 1].getPosBottom() - _nodes[0].getPosTop();
+
+ // Get the count of actual elements in the nodes
+ var count = 0;
+ for (var i = 0; i < _nodes.length; i++)
+ {
+ count += _nodes[i].getCount();
+ }
+
+ // Update the spacer parameters
+ et2_debug("log", "Spacer new height, count: ", height, count);
+ ph.setAvgHeight(height / count);
+ ph.setCount(count);
+
+ // Free all elements (except for the spacer)
+ for (var i = _nodes.length - 1; i >= 0; i--)
+ {
+ if (_nodes[i] != ph)
+ {
+ _nodes[i].free();
+ }
+ }*/
+ },
+
+ /**
+ * Used when rebuilding the tree
+ */
+ _getFlatList: function(_res) {
+ for (var i = 0; i < this._children.length; i++)
+ {
+ if (this._children[i] instanceof et2_dataview_partitionOrganizationNode)
+ {
+ this._children[i]._getFlatList(_res);
+ }
+ else
+ {
+ _res.push(this._children[i]);
+ }
+ }
+ },
+
+ _clear: function() {
+ for (var i = this._children.length - 1; i >= 0; i--)
+ {
+ if (this._children[i] instanceof et2_dataview_partitionOrganizationNode)
+ {
+ this._children[i].free();
+ }
+ }
+
+ this._children = [];
+ }
+});
+
diff --git a/etemplate/js/et2_dataview_view_partitionTree.js b/etemplate/js/et2_dataview_view_partitionTree.js
index d5582190a1..df79b6b50b 100644
--- a/etemplate/js/et2_dataview_view_partitionTree.js
+++ b/etemplate/js/et2_dataview_view_partitionTree.js
@@ -15,1101 +15,13 @@
/*egw:uses
et2_core_common; // for et2_range functions
et2_core_inheritance;
+
et2_dataview_interfaces;
+
+ et2_dataview_view_partitionOrganizationNode;
+ et2_dataview_view_partitionContainerNodes;
*/
-/**
- * The ET2_PARTITION_TREE_WIDTH defines the count of children a node will be
- * created with.
- */
-var ET2_PARTITION_TREE_WIDTH = 10;
-
-/**
- * The partition node tree manages all rows in a dataview. As a dataview may have
- * many thousands of lines, the rows are organized in an a tree. The leafs of the
- * tree represent the single rows, upper layers represent groups of nodes.
- * Each node has a "height" value and is capable of calculate the exact position
- * of a row and its top and bottom value.
- * Additionaly, a leaf can represent an unlimited number of rows. In this way
- * the partition tree is built dynamically and is also capable of "forgetting"
- * information about the rows by simply reducing the tree nodes at a certain
- * position.
- */
-
-var et2_dataview_IPartitionHeight = new Interface({
-
- calculateHeight: function() {}
-
-});
-
-/**
- * Abstract base class for partition nodes - contains the code for calculating
- * the top, bottom, height and (start) index of the node
- */
-var et2_dataview_partitionNode = Class.extend([et2_dataview_IPartitionHeight,
- et2_dataview_IInvalidatable], {
-
- init: function(_root) {
-
- this._root = _root;
- this._parent = null;
- this._pidx = 0;
-
- // Initialize the temporary storage elements
- this.doInvalidate();
- this._invalid = true;
-
- },
-
- destroy: function() {
-
- // Remove this element from the parent children list
- if (this._parent)
- {
- this._parent.removePIdxNode(this._pidx);
- }
-
- },
-
- setParent: function(_parent) {
- if (this._parent != _parent)
- {
- this._parent = _parent;
- this.invalidate();
- }
- },
-
- setPIdx: function(_pidx) {
- if (this._pidx != _pidx)
- {
- this._pidx = _pidx;
- this.invalidate();
- }
- },
-
- /**
- * Invalidates cached values - override the "doInvalidate" function.
- *
- * @param _sender is the node wich originally triggerd the invalidation, can
- * be ommited when calling this function.
- */
- invalidate: function(_sender) {
-
- // If the _sender parameter is not given, assume that this element is
- // the one which triggered the invalidation
- var origin = typeof _sender == "undefined";
-
- if ((origin || _sender != this) && !this._invalid)
- {
- this.doInvalidate();
- this._invalid = true;
-
- // Invalidate the parent node
- if (this._parent)
- {
- this._parent.invalidate(origin ? this : _sender);
- }
- }
- },
-
- /**
- * Performs the actual invalidation.
- */
- doInvalidate: function() {
- this._height = false;
- this._posTop = false;
- this._posBottom = false;
- this._startIdx = false;
- this._stopIdx = false;
- },
-
- /**
- * Returns the root node of the partition tree
- */
- getRoot: function() {
- return this._root;
- },
-
- /**
- * Returns the height of this node
- */
- getHeight: function() {
- // Calculate the height value if it is currently invalid
- if (this._height === false)
- {
- this._height = this.calculateHeight();
-
- // Do a sanity check for the value - if the height wasn't a number
- // it could easily destroy the posTop and posBottom values of the
- // complete tree!
- if (isNaN(this._height))
- {
- et2_debug("error", "calculateHeight returned a NaN value!");
- this._height = 0;
- }
-
- this._invalid = false;
- }
-
- return this._height;
- },
-
- /**
- * Returns the top position of the node in px
- */
- getPosTop: function() {
- if (this._posTop === false)
- {
- this._posTop = this._accumulateValue(this.getPosTop, this.getPosBottom);
-
- this._invalid = false;
- }
-
- return this._posTop;
- },
-
- /**
- * Returns the bottom position of the node in px
- */
- getPosBottom: function() {
- if (this._posBottom === false)
- {
- this._posBottom = this.getPosTop() + this.getHeight();
-
- this._invalid = false;
- }
-
- return this._posBottom;
- },
-
- /**
- * Returns an range object
- */
- getRange: function() {
- return {
- "top": this.getPosTop(),
- "bottom": this.getPosBottom()
- };
- },
-
-
- /**
- * Returns true if the node intersects with the given range
- */
- inRange: function(_ar) {
- return et2_rangeIntersect(this.getRange(), _ar);
- },
-
- /**
- * Returns the overall start index of the node
- */
- getStartIndex: function() {
- if (this._startIdx === false)
- {
- this._startIdx = this._accumulateValue(this.getStartIndex,
- this.getStopIndex);
-
- this._invalid = false;
- }
-
- return this._startIdx;
- },
-
- /**
- * Returns the overall stop index of the node
- */
- getStopIndex: function() {
- if (this._stopIdx === false)
- {
- this._stopIdx = this.getStartIndex() + this.getCount();
-
- this._invalid = false;
- }
-
- return this._stopIdx;
- },
-
- /**
- * Returns the index range object
- */
- getIdxRange: function() {
- return {
- "top": this.getStartIndex(),
- "bottom": this.getStopIndex()
- };
- },
-
- /**
- * Checks whether this element is inside the given index range
- */
- inIdxRange: function(_idxRange) {
- return et2_rangeIntersect(this.getIdxRange, _idxRange);
- },
-
- /**
- * Returns the count of leafs which are below this node
- */
- getCount: function() {
- return 1;
- },
-
- /**
- * Returns the nodes which reside in the given range
- */
- getRangeNodes: function(_range, _create) {
- if (this.inRange(_range))
- {
- return [this];
- }
-
- return [];
- },
-
- /**
- * Returns the nodes which are inside the given index range
- */
- getIdxRangeNodes: function(_idxRange, _create) {
- if (this.inIdxRange(_idxRange))
- {
- return [this];
- }
-
- return [];
- },
-
- /**
- * Returns the (maximum) depth of the tree
- */
- getDepth: function() {
- return 1;
- },
-
- getAvgHeightData: function(_data) {
- _data.cnt++;
- _data.height += this.getHeight();
- },
-
- getNodeIdx: function(_idx, _create) {
- return null;
- },
-
- getRowProvider: function() {
- return this.getRoot().getRowProvider();
- },
-
- getDataProvider: function() {
- return this.getRoot().getDataProvider();
- },
-
- /* ---- PRIVATE FUNCTIONS ---- */
-
- _accumulateValue: function(_f1, _f2) {
- if (this._parent)
- {
- if (this._pidx == 0)
- {
- return _f1.call(this._parent);
- }
- else
- {
- return _f2.call(this._parent._children[this._pidx - 1]);
- }
- }
-
- return 0;
- }
-
-});
-
-/*var et2_dataview_IIndexOperations = new Interface({
- getIdxNode: function(_idx, _create),
- removeIdxNode: function(_idx),
- insertNodes: function(_idx, _nodes)
-});*/
-
-/**
- * An partition tree organization node can contain child nodes and organizes
- * those.
- */
-var et2_dataview_partitionOrganizationNode = et2_dataview_partitionNode.extend(
- /*et2_dataview_IIndexOperations, */{
-
- init: function(_root, _parent, _pidx) {
-
- if (typeof _parent == "undefined")
- {
- _parent = null;
- }
-
- if (typeof _pidx == "undefined")
- {
- _pidx = 0;
- }
-
- // Call the parent constructor
- this._super(_root);
-
- this._children = [];
-
- // Set the given parent and parent-index
- this.setParent(_parent);
- this.setPIdx(_pidx);
-
- },
-
- destroy: function() {
- // Free all child nodes
- for (var i = this._children.length - 1; i >= 0; i--)
- {
- this._children[i].free();
- }
-
- this._super();
- },
-
- /**
- * Delete the buffered element count
- */
- doInvalidate: function() {
- this._super();
-
- this._count = false;
- this._depth = false;
- this._avgHeightData = false;
- },
-
- /**
- * Calculates the count of elements by accumulating the counts of the child
- * elements.
- */
- getCount: function() {
- if (this._count === false)
- {
- // Calculate the count of nodes
- this._count = 0;
- for (var i = 0; i < this._children.length; i++)
- {
- this._count += this._children[i].getCount();
- }
- }
-
- return this._count;
- },
-
- /**
- * Calculates the height of this node by accumulating the height of the
- * child nodes.
- */
- calculateHeight: function() {
- var result = 0;
- for (var i = 0; i < this._children.length; i++)
- {
- result += this._children[i].getHeight();
- }
-
- return result;
- },
-
- /**
- * Removes the given node from the tree
- */
- removeNode: function(_node) {
- // Search the element on this level
- for (var i = 0; i < this._children.length; i++)
- {
- if (this._children[i] == _node)
- {
- this.removePIdxNode(i);
- return true;
- }
- }
-
- // Search the element on a lower level
- for (var i = 0; i < this._children.length; i++)
- {
- if (this._children[i] instanceof et2_dataview_partitionOrganizationNode &&
- this._children[i].removeNode(_node))
- {
- return true;
- }
- }
-
- return false;
- },
-
- /**
- * Removes the child with the given index in the _children list
- */
- removePIdxNode: function(_pidx) {
- // Invalidate this element
- this.invalidate();
-
- // Delete the element at the given pidx and remove the parent reference
- this._children.splice(_pidx, 1)[0].setParent(null);
-
- // Recalculate the pidx of the children behind the one removed
- for (var i = _pidx; i < this._children.length; i++)
- {
- this._children[i]._pidx--;
- }
-
- return true;
- },
-
- /**
- * Removes the child with the given overall index
- */
- removeIdxNode: function(_idx) {
- return this._iterateToIndex(_idx, function(ei, bi, child) {
- if (child.implements(et2_dataview_IIndexOperations))
- {
- return child.removeIdxNode(_idx);
- }
-
- return this.removePIdxNode(i);
- }, false);
- },
-
- /**
- * Returns the node with the given overall index and null if it is not found
- */
- getIdxNode: function(_idx) {
- return this._iterateToIndex(_idx, function(ei, bi, child) {
- if (child.implements(et2_dataview_IIndexOperations))
- {
- return child.getIdxNode()
- }
-
- if (idx == bi)
- {
- return child;
- }
- }, 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
- */
- getRangeNodes: function(_range, _create) {
-
- if (typeof _create == "undefined")
- {
- _create = true;
- }
-
- var result = [];
-
- // Create a copy of the children of this element, as the child list may
- // change due to new children being inserted.
- var children = this._copyChildren();
-
- // We did not have a intersect in the range now
- var hadIntersect = false;
- for (var i = 0; i < children.length; i++)
- {
- if (children[i].inRange(_range))
- {
- hadIntersect = true;
-
- var res = children[i].getRangeNodes(_range, _create);
-
- if (res === false)
- {
- return this.getRangeNodes(_range, _create);
- }
-
- // Append the search results of the given element
- result = result.concat(res);
- }
- else
- {
- // Abort as we are out of the range where intersects can happen
- if (hadIntersect)
- {
- break;
- }
- }
- }
-
- return result;
- },
-
- /**
- * Returns the nodes which are inside the given range
- */
- getIdxRangeNodes: function(_idxRange, _create) {
-
- if (typeof _create == "undefined")
- {
- _create = true;
- }
-
- var result = [];
-
- // Create a copy of the children of this element, as the child list may
- // change due to new children being inserted.
- var children = this._copyChildren();
-
- // We did not have a intersect in the range now
- var hadIntersect = false;
- for (var i = 0; i < children.length; i++)
- {
- if (children[i].inIdxRange(_idxRange))
- {
- hadIntersect = true;
-
- // Append the search results of the given element
- var res = children[i].getIdxRangeNodes(_idxRange,
- _create);
-
- if (res === false)
- {
- return this.getIdxRangeNodes(_idxRange, _create);
- }
-
- result = result.concat(res);
- }
- else
- {
- // Abort as we are out of the range where intersects can happen
- if (hadIntersect)
- {
- break;
- }
- }
- }
-
- return result;
- },
-
- /**
- * Reduces the given range to a spacer
- */
- reduceRange: function(_range) {
- this._reduce(this.getRangeNodes(_range, false))
- },
-
- /**
- * Reduces the given index range to a spacer
- */
- reduceIdxRange: function(_range) {
- this._reduce(this.getIdxRangeNodes(_range, false));
- },
-
- getDepth: function() {
- if (this._depth === false)
- {
- this._depth = 0;
-
- // Get the maximum depth and increase it by one
- for (var i = 0; i < this._children.length; i++)
- {
- this._depth = Math.max(this._depth, this._children[i].getDepth());
- }
- this._depth++;
- }
-
- return this._depth;
- },
-
- _insertLeft: function(_idx, _nodes) {
- // Check whether the node left to the given index can still take some
- // nodes - if yes, insert the maximum amount of nodes into this node
- if (_idx > 0 && this._children[_idx - 1] instanceof et2_dataview_partitionOrganizationNode
- && this._children[_idx - 1]._children.length < ET2_PARTITION_TREE_WIDTH)
- {
- // Calculate how many children can be inserted into the left node
- var child = this._children[_idx - 1];
- var c = Math.min(ET2_PARTITION_TREE_WIDTH - child._children.length, _nodes.length);
-
- // Insert the remaining children into the left node
- if (c > 0)
- {
- var nodes = _nodes.splice(0, c);
- child.insertNodes(child._children.length, nodes);
- }
- }
- },
-
- _insertRight: function(_idx, _nodes) {
- // Check whether the node right to the given index can still take some
- // nodes - if yes, insert the nodes there
- if (_idx < this._children.length &&
- this._children[_idx] instanceof et2_dataview_partitionOrganizationNode &&
- this._children[_idx]._children.length < ET2_PARTITION_TREE_WIDTH)
- {
- var child = this._children[_idx];
- var c = Math.min(ET2_PARTITION_TREE_WIDTH - child._children.length, _nodes.length);
-
- // Insert the remaining children into the left node
- if (c > 0)
- {
- var nodes = _nodes.splice(_nodes.length - c, c);
- child.insertNodes(0, nodes);
- }
- }
- },
-
- /**
- * Groups the nodes which should be inserted by packages of ten and insert
- * those as children. If there are more than ET2_PARTITION_TREE_WIDTH
- * children as a result of this action, this node gets destroyed and the
- * children are given to the parent node.
- */
- insertNodes: function(_idx, _nodes) {
- // Break if no nodes are to be inserted
- if (_nodes.length == 0)
- {
- return;
- }
-
- // Invalidate this node
- this.invalidate();
-
- // Try to insert the given objects into an organization node at the left
- // or right side of the given index
- this._insertLeft(_idx, _nodes);
- this._insertRight(_idx, _nodes);
-
- // Update the pidx of the nodes after _idx
- for (var i = _idx; i < this._children.length; i++)
- {
- this._children[i].setPIdx(i + _nodes.length);
- }
-
- // Set the parent and the pidx of the new nodes
- for (var i = 0; i < _nodes.length; i++)
- {
- _nodes[i].setParent(this);
- _nodes[i].setPIdx(_idx + i);
- }
-
- // Simply insert the nodes at the given position
- this._children.splice.apply(this._children, [_idx, 0].concat(_nodes));
-
- // Check whether the width of this element is greater than ET2_PARTITION_TREE_WIDTH
- // If yes, split the children into groups of ET2_PARTITION_TREE_WIDTH and
- // insert those into this node
- /*if (this._children.length > ET2_PARTITION_TREE_WIDTH)
- {
- var insertNodes = [];
-
- while (_nodes.length > 0)
- {
- var orgaNode = new et2_dataview_partitionOrganizationNode(this,
- insertNodes.length);
-
- // Get groups of ET2_PARTITION_TREE_WIDTH from the nodes while
- // reading the first level of nodes from organization nodes
- var newNodes = [];
- var isPartial = false;
- while (newNodes.length < ET2_PARTITION_TREE_WIDTH && _nodes.length > 0)
- {
- var node = _nodes[0];
-
- if (!(node instanceof et2_dataview_partitionOrganizationNode))
- {
- newNodes.push(_nodes.shift());
- isPartial = true;
- }
- else
- {
- if (node._children.length == 0)
- {
- // Remove the node from the list and free it
- _nodes.shift().free();
- }
- else
- {
- if (!isPartial && node._children.length == ET2_PARTITION_TREE_WIDTH)
- {
- newNodes.push(_nodes.shift());
- }
- else
- {
- newNodes = newNodes.concat(node._children.splice(0,
- ET2_PARTITION_TREE_WIDTH - newNodes.length));
- isPartial = true;
- }
- }
- }
- }
-
- orgaNode.insertNodes(0, newNodes);
-
- insertNodes.push(orgaNode);
- }
-
- this._children = [];
- this.insertNodes(0, insertNodes);
- }*/
- },
-
- rebuild: function() {
- // Get all leafs
- var children = [];
- this._getFlatList(children);
-
- // Free all organization nodes
- this._clear();
-
- 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 ---- */
-
- _copyChildren: function() {
- // Copy the child array as querying the child nodes may change the tree
- var children = new Array(this._children.length);
- for (var i = 0; i < this._children.length; i++)
- {
- children[i] = this._children[i];
- }
-
- return children;
- },
-
- _iterateToIndex: function(_idx, _func, _res) {
- for (var i = 0; i < this._children.length; i++)
- {
- var child = this._children[i];
-
- var bi = child.getStartIndex();
- var ei = child.getStopIndex();
-
- if (bi > _idx)
- {
- return res;
- }
-
- if (bi <= _idx && ei > _idx)
- {
- return _func.call(this, bi, ei, child);
- }
- }
-
- return res;
- },
-
- /**
- * Reduces the given nodes to a single spacer
- */
- _reduce: function(_nodes) {
- if (_nodes.length == 0)
- {
- return;
- }
-
- // Check whether the first or last node is a spacer, if not create
- // a new one
- var ph;
- if (_nodes[0] instanceof et2_dataview_partitionSpacerNode)
- {
- ph = _nodes[0]
- }
- else if (_nodes[_nodes.length - 1] instanceof et2_dataview_partitionSpacerNode)
- {
- ph = _nodes[_nodes.length - 1];
- }
- else
- {
- // Create a new spacer node an insert it at the place of the
- // first node of the range
- ph = new et2_dataview_partitionSpacerNode(this.getRoot());
- this.getRoot().insertNodes(_nodes[0].getStartIndex(), [ph]);
- }
-
- // Get the height of the resulting spacer
- var height = _nodes[_nodes.length - 1].getBottom() - _nodes[0].getTop();
-
- // Get the count of actual elements in the nodes
- var count = 0;
- for (var i = 0; i < _nodes.length; i++)
- {
- count += _nodes[i].getCount();
- }
-
- // Update the spacer parameters
- ph.setAvgHeight(height / count);
- ph.setCount(count);
-
- // Free all elements (except for the spacer)
- for (var i = _nodes.length - 1; i >= 0; i--)
- {
- if (_nodes[i] != ph)
- {
- _nodes[i].free();
- }
- }
- },
-
- /**
- * Used when rebuilding the tree
- */
- _getFlatList: function(_res) {
- for (var i = 0; i < this._children.length; i++)
- {
- if (this._children[i] instanceof et2_dataview_partitionOrganizationNode)
- {
- this._children[i]._getFlatList(_res);
- }
- else
- {
- _res.push(this._children[i]);
- }
- }
- },
-
- _clear: function() {
- for (var i = this._children.length - 1; i >= 0; i--)
- {
- if (this._children[i] instanceof et2_dataview_partitionOrganizationNode)
- {
- this._children[i].free();
- }
- }
-
- 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 spacer. Complete parts of the tree can be
- * transformed into spacer nodes.
- */
-var et2_dataview_partitionSpacerNode = et2_dataview_partitionContainerNode.extend({
-
- 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
- this._super(_root, container);
-
- // Copy the count and average height parameters and update the height
- // of the container
- this._count = _count;
- this._avgHeight = _avgHeight;
- this.container.setHeight(_count * _avgHeight);
- },
-
- getCount: function() {
- return this._count;
- },
-
- setCount: function(_count) {
- if (_count != this._count)
- {
- this._count = _count;
- this.invalidate();
- this.container.setHeight(_count * _avgHeight);
- }
- },
-
- setAvgHeight: function(_height) {
- if (_height != this._avgHeight)
- {
- this._avgHeight = _height;
- this.invalidate();
- this.container.setHeight(_count * _avgHeight);
- }
- },
-
- calculateHeight: function() {
- return this._count * this._avgHeight;
- },
-
- /**
- * Creates the nodes which fall in the given range and returns them
- */
- getRangeNodes: function(_range) {
- var insertNodes = [];
-
- // Copy parent and pidx as we'll have to access those objects after this
- // one gets freed
- var parent = this._parent;
- var pidx = this._pidx;
-
- // Get the top and bottom of this node
- var t = this.getPosTop();
- var b = this.getPosBottom();
-
- // Get the start and stop index of the elements which have to be
- // created.
- var ah = this._avgHeight;
- var startIdx = Math.max(0, Math.floor((_range.top - t) / ah));
- var stopIdx = Math.min(this._count, Math.ceil((_range.bottom - t) / ah));
-
- if (startIdx > 0 && startIdx < this._count)
- {
- // Create a spacer which contains the elements until startIdx
- 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
- for (var i = startIdx; i < stopIdx; i++)
- {
- var rowNode = new et2_dataview_partitionRowNode(this.getRoot(), ah);
- insertNodes.push(rowNode);
- }
-
- if (stopIdx < this._count - 1 && stopIdx > 0)
- {
- // Create a spacer which contains the elements starting from
- // stop index
- var l = this._count - stopIdx;
- insertNodes.push(new et2_dataview_partitionSpacerNode(this.getRoot(),
- l, ah));
- }
-
- // Check whether insertNodes really has entrys - this is not the case
- // if the given range is just outside the range of this element
- if (insertNodes.length > 0)
- {
- // Free this element
- this.free();
-
- // Insert the newly created nodes at the original place of this node
- 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 [];
- },
-
- getAvgHeightData: function(_data) {
- // Do nothing here, as the spacers should not be inside the average
- // height statistic.
- },
-
-});
-
/**
* Main class for the usage of the partition tree
*/
@@ -1188,22 +100,4 @@ var et2_dataview_partitionTree = et2_dataview_partitionOrganizationNode.extend({
});
-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;
- }
-
-});
-
diff --git a/etemplate/js/et2_dataview_view_row.js b/etemplate/js/et2_dataview_view_row.js
index fcdbb2346c..15d5f64754 100644
--- a/etemplate/js/et2_dataview_view_row.js
+++ b/etemplate/js/et2_dataview_view_row.js
@@ -16,37 +16,22 @@
et2_dataview_interfaces;
*/
-var __cc = 0;
-
var et2_dataview_row = et2_dataview_container.extend(et2_dataview_IDataRow, {
- init: function(_dataProvider, _rowProvider, _invalidationElem, _idx) {
+ init: function(_dataProvider, _rowProvider, _invalidationElem, _avgHeight) {
this._super(_dataProvider, _rowProvider, _invalidationElem);
- __cc += 5;
+ this._avgHeight = _avgHeight;
this.tr = this.rowProvider.getPrototype("default");
- $j("div", this.tr).text("Blub" + (__cc / 5))
- .height(20 + Math.round(Math.random() * 100))
- .css("background-color", "rgb(" +
- (255 - __cc % 255) + "," +
- (__cc % 255) + "," +
- ((__cc * 2) % 255) +
- ")");
this.appendNode(this.tr);
-
- // Register this row in the dataprovider - if data is available for this
- // row the "updateData" function will be called immediately.
- //this.dataProvider.registerDataRow(_idx, this);
-
-// if (this.tr == null)
-// {
-// }
},
- destroy: function() {
- //this.dataProvider.unregisterDataRow(_idx);
+ setIdx: function(_idx) {
+ this._idx = _idx;
+
+ $j("div:first", this.tr).text(_idx + ":");
},
updateData: function(_data) {
diff --git a/etemplate/js/et2_extension_nextmatch.js b/etemplate/js/et2_extension_nextmatch.js
index 1d2b86ced4..cbe6c5806b 100644
--- a/etemplate/js/et2_extension_nextmatch.js
+++ b/etemplate/js/et2_extension_nextmatch.js
@@ -21,8 +21,8 @@
et2_widget_grid;
et2_widget_selectbox;
et2_extension_nextmatch_dynheight;
- et2_dataview_gridContainer;
- et2_dataview_dataProvider;
+ et2_dataview_view_gridContainer;
+ et2_dataview_model_dataProvider;
*/
/**
diff --git a/etemplate/js/test/test_xml.html b/etemplate/js/test/test_xml.html
index bea41795b1..78aca47788 100644
--- a/etemplate/js/test/test_xml.html
+++ b/etemplate/js/test/test_xml.html
@@ -40,12 +40,15 @@
+
+
+
-
+