Implemented framework for dynamically inserting grid rows into the DOM tree and a very simple test for it

This commit is contained in:
Andreas Stöckel 2011-03-07 16:53:43 +00:00
parent b0b4c8bf6a
commit dd8a8eab45
5 changed files with 1064 additions and 5 deletions

View File

@ -104,9 +104,27 @@ function egwPreventSelect(e)
return false; return false;
} }
return true;
} }
function egwResetPreventSelect(elem) function egwResetPreventSelect(elem)
{ {
} }
function egwCallAbstract(_obj, _fn, _args)
{
if (_fn)
{
return _fn.apply(_obj, _args);
}
else
{
throw "egw_action Exception: Abstract function call in JS code.";
}
return false;
}

View File

@ -0,0 +1,792 @@
/**
* eGroupWare egw_action framework - egw action framework
*
* @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
* @package egw_action
* @version $Id$
*/
/**
* View Classes for the egw grid component.
*/
/**
* Common functions used in all classes
*/
function egwArea(_top, _height)
{
return {
"top": _top,
"bottom": _top + _height
}
}
function egwAreaIntersect(_ar1, _ar2)
{
return ! (_ar1.bottom < _ar2.top || _ar1.top > _ar2.bottom);
}
function egwAreaIntersectDir(_ar1, _ar2)
{
if (_ar1.bottom < _ar2.top)
{
return -1;
}
if (_ar1.top > _ar2.bottom)
{
return 1;
}
return 0;
}
/** -- egwGridViewOuter Class -- **/
/**
* TODO
*/
function egwGridViewOuter()
{
}
/** -- egwGridViewContainer Interface -- **/
/**
* Constructor for the abstract egwGridViewContainer class. A grid view container
* represents a chunk of data which is inserted into a grid. As the grid itself
* is a container, hirachical structures can be realised. All containers are inserted
* into the DOM tree directly after creation.
*
* @param object _grid is the parent grid this container is inserted into.
*/
function egwGridViewContainer(_grid, _heightChangeProc)
{
this.grid = _grid;
this.visible = true;
this.position = 0;
this.heightChangeProc = _heightChangeProc;
this.parentNode = null;
this.columns = [];
this.height = false;
this.index = 0;
this.viewArea = false;
this.doInsertIntoDOM = null;
this.doUpdateColumns = null;
this.doSetViewArea = null;
}
/**
* Calls the heightChangeProc (if set) in the context of the parent grid (if set)
*/
egwGridViewContainer.prototype.callHeightChangeProc = function()
{
if (this.heightChangeProc && this.grid)
{
// Pass this element as parameter
this.heightChangeProc.call(this.grid, this);
}
}
/**
* Sets the visibility of the container. Setting the visibility only takes place
* if the parentNode is set and the visible state has changed or the _force
* parameter is set to true.
*/
egwGridViewContainer.prototype.setVisible = function(_visible, _force)
{
// Default the _force parameter to force
if (typeof _force == "undefined")
{
_force = false;
}
if ((_visible != this.visible || _force) && this.parentNode)
{
$(this.parentNode).toggleClass("hidden", !_visible);
// While the element has been invisible, the viewarea might have changed,
// so check it now
this.checkViewArea();
// As the element is now (in)visible, its height has changed. Inform the
// parent about it.
this.callHeightChangeProc();
}
this.visible = _visible;
}
/**
* Returns whether the container is visible. The element is not visible as long
* as it isn't implemented into the DOM-Tree.
*/
egwGridViewContainer.prototype.getVisible = function()
{
return this.parentNode && this.visible;
}
/**
* Inserts the container into the given _parentNode. This method may only be
* called once after the creation of the container.
*
* @param object _parentNode is the parentDOM-Node into which the container should
* be inserted.
* @param array _columns is an array of columns which will be generated
*/
egwGridViewContainer.prototype.insertIntoDOM = function(_parentNode, _columns)
{
if (_parentNode && !this.parentNode)
{
// Copy the function arguments
this.columns = _columns;
this.parentNode = $(_parentNode);
// Call the interface function of the implementation which will insert its data
// into the parent node.
return egwCallAbstract(this, this.doInsertIntoDOM, arguments);
this.setVisible(this.visible);
}
else
{
throw "egw_action Exception: egwGridViewContainer::insertIntoDOM called more than once for a container object or parent node not specified.";
}
return false;
}
egwGridViewContainer.prototype.updateColumns = function(_columns)
{
this.columns = _columns;
if (_parentNode)
{
return egwCallAbstract(this, this.doUpdateColumns, arguments);
}
return false;
}
egwGridViewContainer.prototype.setViewArea = function(_area, _force)
{
// Calculate the relative coordinates and pass those to the implementation
var relArea = {
"top": _area.top - this.position,
"bottom": _area.bottom - this.position
};
this.viewArea = relArea;
this.checkViewArea(_force);
}
egwGridViewContainer.prototype.getViewArea = function()
{
if (this.viewArea && this.visible)
{
return this.viewArea;
}
return false;
}
egwGridViewContainer.prototype.setPosition = function(_top)
{
// Recalculate the relative view area
if (this.viewArea)
{
var at = this.position + this.viewArea.top;
this.viewArea = {
"top": at - _top,
"bottom": at - _top + (this.viewArea.bottom - this.viewArea.top)
};
this.checkViewArea();
}
this.position = _top;
}
/**
* Returns the height of the container in pixels and zero if the element is not
* visible. The height is clamped to positive values.
*/
egwGridViewContainer.prototype.getHeight = function()
{
if (this.visible && this.parentNode)
{
if (this.height === false)
{
this.height = this.parentNode.outerHeight();
}
return this.height;
}
else
{
return 0;
}
}
egwGridViewContainer.prototype.invalidateHeightCache = function()
{
this.height = false;
}
egwGridViewContainer.prototype.offsetPosition = function(_offset)
{
this.position += _offset;
// Offset the view area in the oposite direction
if (this.viewArea)
{
this.viewArea.top -= _offset;
this.viewArea.bottom -= _offset;
this.checkViewArea();
}
}
egwGridViewContainer.prototype.inArea = function(_area)
{
return egwAreaIntersect(this.getArea(), _area);
}
egwGridViewContainer.prototype.checkViewArea = function(_force)
{
if (typeof _force == "undefined")
{
_force = false;
}
if (this.visible && this.viewArea)
{
if (!this.grid || !this.grid.inUpdate || _force)
{
return egwCallAbstract(this, this.doSetViewArea, [this.viewArea]);
}
}
return false;
}
egwGridViewContainer.prototype.getArea = function()
{
return egwArea(this.position, this.getHeight());
}
/** -- egwGridViewGrid Class -- **/
/**
* egwGridViewGrid is the container for egwGridViewContainer objects, but itself
* implements the egwGridViewContainer interface.
*/
function egwGridViewGrid(_grid, _heightChangeProc, _scrollable)
{
if (typeof _scrollable == "undefined")
{
_scrollable = false;
}
var container = new egwGridViewContainer(_grid, _heightChangeProc);
// Introduce new functions to the container interface
container.outerNode = null;
container.innerNode = null;
container.scrollarea = null;
container.scrollable = _scrollable;
container.scrollHeight = 100;
container.scrollEvents = 0;
container.didUpdate = false;
container.setupContainer = egwGridViewGrid_setupContainer;
container.insertContainer = egwGridViewGrid_insertContainer;
container.removeContainer = egwGridViewGrid_removeContainer;
container.addContainer = egwGridViewGrid_addContainer;
container.heightChangeHandler = egwGridViewGrid_heightChangeHandler;
container.setScrollHeight = egwGridViewGrid_setScrollHeight;
container.scrollCallback = egwGridViewGrid_scrollCallback;
container.children = [];
// Overwrite the abstract container interface functions
container.invalidateHeightCache = egwGridViewGrid_invalidateHeightCache;
container.getHeight = egwGridViewGrid_getHeight;
container.doUpdateColumns = egwGridViewGrid_doUpdateColumns;
container.doInsertIntoDOM = egwGridViewGrid_doInsertIntoDOM;
container.doSetViewArea = egwGridViewGrid_doSetviewArea;
return container;
}
function egwGridViewGrid_setupContainer()
{
/*
Structure:
<td colspan="[columncount]">
[<div class="egwGridView_scrollarea">]
<table class="egwGridView_grid">
<tbody>
[Container 1]
[Container 2]
[...]
[Container n]
</tbody>
</table>
[</div>]
</td>
*/
this.outerNode = $(document.createElement("td"));
if (this.scrollable)
{
this.scrollarea = $(document.createElement("div"));
this.scrollarea.addClass("egwGridView_scrollarea");
this.scrollarea.css("height", this.scrollHeight + "px");
this.scrollarea.scroll(this, function(e) {
window.setTimeout(function() {
e.data.scrollEvents++;
e.data.scrollCallback(e.data.scrollEvents);
}, 50);
});
}
var table = $(document.createElement("table"));
table.addClass("egwGridView_grid");
this.innerNode = $(document.createElement("tbody"));
if (this.scrollable)
{
this.outerNode.append(this.scrollarea);
this.scrollarea.append(table);
}
else
{
this.outerNode.append(table);
}
table.append(this.innerNode);
}
function egwGridViewGrid_setScrollHeight(_value)
{
this.scrollHeight = _value;
if (this.scrollarea)
{
this.scrollarea.css("height", _value + "px");
this.scrollCallback();
}
}
var
EGW_GRID_VIEW_EXT = 50;
EGW_GRID_MAX_CYCLES = 10;
function egwGridViewGrid_scrollCallback(_event)
{
if ((typeof _event == "undefined" || _event == this.scrollEvents) && this.scrollarea)
{
var cnt = 0;
var area = egwArea(this.scrollarea.scrollTop() - EGW_GRID_VIEW_EXT,
this.scrollHeight + EGW_GRID_VIEW_EXT * 2);
do {
cnt++;
this.didUpdate = false;
this.setViewArea(area);
} while (this.didUpdate && cnt < EGW_GRID_MAX_CYCLES);
// console.log(cnt);
if (cnt == EGW_GRID_MAX_CYCLES)
{
if (this.console && this.console.info)
{
this.console.info("Too many update cycles. Aborting.")
}
}
this.scrollEvents = 0;
}
}
function egwGridViewGrid_insertContainer(_after, _class, _params)
{
this.didUpdate = true;
var container = new _class(this, this.heightChangeHandler, _params);
var idx = this.children.length;
if (typeof _after == "number")
{
idx = Math.max(-1, Math.min(this.children.length, _after)) + 1;
}
else if (typeof _after == "object" && _after)
{
idx = _after.index + 1;
}
// Insert the element at the given position
this.children.splice(idx, 0, container);
// Create a table row for that element
var tr = $(document.createElement("tr"));
// Insert the table row after the container specified in the _after parameter
// and set the top position of the node
container.index = idx;
if (idx == 0)
{
this.innerNode.prepend(tr);
container.setPosition(0);
}
else
{
tr.insertAfter(this.children[idx - 1].parentNode);
container.setPosition(this.children[idx - 1].getArea().bottom);
}
// Insert the container into the table row
container.insertIntoDOM(tr, this.columns);
// Offset the position of all following elements by the height of the container
// and move the index of those elements
var height = container.getHeight();
for (var i = idx + 1; i < this.children.length; i++)
{
this.children[i].offsetPosition(height);
this.children[i].index++;
}
return container;
}
function egwGridViewGrid_removeContainer(_container)
{
this.didUpdate = true;
var idx = _container.index;
// Offset the position of the folowing children back
var height = _container.getHeight();
for (var i = idx + 1; i < this.children.length; i++)
{
this.children[i].offsetPosition(-height);
this.children[i].index--;
}
// Delete the parent node of the container object
if (_container.parentNode)
{
_container.parentNode.remove();
_container.parentNode = null;
}
this.children.splice(idx, 1);
}
function egwGridViewGrid_addContainer(_class)
{
// Insert the container at the beginning of the list.
this.insertContainer(false, _class);
return container;
}
function egwGridViewGrid_invalidateHeightCache(_children)
{
if (typeof _children == "undefined")
{
_children = true;
}
this.height = false;
if (_children)
{
for (var i = 0; i < this.children.length; i++)
{
this.children[i].invalidateHeightCache();
}
}
}
function egwGridViewGrid_getHeight()
{
if (this.visible && this.parentNode)
{
if (this.height === false)
{
this.height = this.innerNode.outerHeight();
}
return this.height;
}
else
{
return 0;
}
}
function egwGridViewGrid_heightChangeHandler(_elem)
{
this.didUpdate = true;
// Get the height-change
var oldHeight = _elem.height === false ? 0 : _elem.height;
_elem.invalidateHeightCache(false);
var newHeight = _elem.getHeight();
var offs = newHeight - oldHeight;
// Set the offset of all elements succeding the given element correctly
for (var i = _elem.index + 1; i < this.children.length; i++)
{
this.children[i].offsetPosition(offs);
}
// As a result of the height of one of the children, the height of this element
// has changed too - inform the parent grid about it.
this.callHeightChangeProc();
}
function egwGridViewGrid_doInsertIntoDOM()
{
// Generate the DOM Nodes and append the outer node to the parent node
this.setupContainer();
this.parentNode.append(this.outerNode);
this.doUpdateColumns();
}
function egwGridViewGrid_doUpdateColumns(_columns)
{
this.outerNode.attr("colspan", this.columns.length);
for (var i = 0; i < this.children.length; i++)
{
this.children[i].doUpdateColumns(_columns)
}
}
function egwGridViewGrid_doSetviewArea(_area)
{
// Do a binary search for elements which are inside the given area
var elem = null;
var elems = [];
var bordertop = 0;
var borderbot = this.children.length - 1;
var idx = 0;
while ((borderbot - bordertop >= 0) && !elem)
{
idx = Math.round((borderbot + bordertop) / 2);
var ar = this.children[idx].getArea();
var dir = egwAreaIntersectDir(_area, ar);
if (dir == 0)
{
elem = this.children[idx];
}
else if (dir == -1)
{
borderbot = idx - 1;
}
else
{
bordertop = idx + 1;
}
}
if (elem)
{
elems.push(elem);
// Search upwards for elements in the area from the matched element on
for (var i = idx - 1; i >= 0; i--)
{
if (this.children[i].inArea(_area))
{
elems.unshift(this.children[i]);
}
else
{
break;
}
}
// Search downwards for elemwnts in the area from the matched element on
for (var i = idx + 1; i < this.children.length; i++)
{
if (this.children[i].inArea(_area))
{
elems.push(this.children[i]);
}
else
{
break;
}
}
}
this.inUpdate = true;
// Call the setViewArea function of visible child elements
// Imporant: The setViewArea function has to work on a copy of children,
// as the container may start to remove themselves or add new elements using
// the insertAfter function.
for (var i = 0; i < elems.length; i++)
{
elems[i].setViewArea(_area, true);
}
this.inUpdate = false;
}
/** -- egwGridViewRow Class -- **/
function egwGridViewRow(_grid, _heightChangeProc, _item)
{
var container = new egwGridViewContainer(_grid, _heightChangeProc);
// Copy the item parameter, which is used when fetching data from the data
// source
container.item = _item;
// Overwrite the inherited abstract functions
container.doInsertIntoDOM = egwGridViewRow_doInsertIntoDOM;
container.doSetViewArea = egwGridViewRow_doSetViewArea;
container.doUpdateColumns = egwGridViewRow_doUpdateColumns;
return container;
}
function egwGridViewRow_doInsertIntoDOM()
{
this.doUpdateColumns();
}
function egwGridViewRow_doUpdateColumns()
{
this.parentNode.empty();
for (var i = 0; i < this.columns.length; i++)
{
var td = $(document.createElement("td"));
td.text(this.item + ", col" + i);
this.parentNode.append(td);
}
this.checkViewArea();
}
function egwGridViewRow_doSetViewArea()
{
//TODO: Load the data for the columns and load it.
}
/** -- egwGridViewSpacer Class -- **/
function egwGridViewSpacer(_grid, _heightChangeProc, _itemHeight)
{
if (typeof _itemHeight == "undefined")
{
_itemHeight = 20;
}
var container = new egwGridViewContainer(_grid, _heightChangeProc);
// Add some new functions/properties to the container
container.itemHeight = _itemHeight;
container.domNode = null;
container.items = [];
container.setItemList = egwGridViewSpacer_setItemList;
// Overwrite the inherited functions
container.doInsertIntoDOM = egwGridViewSpacer_doInsertIntoDOM;
container.doSetViewArea = egwGridViewSpacer_doSetViewArea;
container.doUpdateColumns = egwGridViewSpacer_doUpdateColumns;
return container;
}
function egwGridViewSpacer_setItemList(_items)
{
this.items = _items;
if (this.domNode)
{
this.domNode.css("height", (this.items.length * this.itemHeight) + "px");
this.callHeightChangeProc();
}
}
function egwGridViewSpacer_doInsertIntoDOM()
{
this.domNode = $(document.createElement("td"));
this.domNode.addClass("egwGridView_spacer");
this.domNode.css("height", (this.items.length * this.itemHeight) + "px");
this.parentNode.append(this.domNode);
this.doUpdateColumns();
}
function egwGridViewSpacer_doSetViewArea()
{
// Get all items which are in the view area
var top = Math.max(0, Math.floor(this.viewArea.top / this.itemHeight));
var bot = Math.min(this.items.length, Math.ceil(this.viewArea.bottom / this.itemHeight));
// Split the item list into three parts
var it_top = this.items.slice(0, top);
var it_mid = this.items.slice(top, bot);
var it_bot = this.items.slice(bot, this.items.length);
this.items = [];
var idx = this.index;
// Insert the new rows in the parent grid in front of the spacer container
for (var i = it_mid.length - 1; i >= 0; i--)
{
this.grid.insertContainer(idx - 1, egwGridViewRow, it_mid[i]);
}
// If top was greater than 0, insert a new spacer in front of the
if (it_top.length > 0)
{
var spacer = this.grid.insertContainer(idx - 1, egwGridViewSpacer, this.itemHeight);
spacer.setItemList(it_top)
}
// If there are items left at the bottom of the spacer, set theese as items of this spacer
if (it_bot.length > 0)
{
this.setItemList(it_bot);
}
else
{
this.grid.removeContainer(this);
}
}
function egwGridViewSpacer_doUpdateColumns()
{
this.domNode.attr("colspan", this.columns.length);
}

View File

@ -1,6 +1,4 @@
body { body {
width: 100%;
height: 100%;
margin: 0; margin: 0;
padding: 0; padding: 0;
} }
@ -10,9 +8,101 @@ body, td, th {
font-size: 11px; font-size: 11px;
} }
.grid { table.egwGridView_grid {
border-spacing: 0;
border-collapse: collapse;
}
.egwGridView_scrollarea {
width: 100%; width: 100%;
border-spacing: 0px; overflow: auto;
}
.egwGridView_spacer {
display: block;
background-image: url(imgs/non_loaded_bg.png);
background-position: top left;
}
.egwGridView_outer {
border-spacing: 0;
border-collapse: collapse;
padding: 0;
margin: 5px;
}
.egwGridView_outer td, .egwGridView_outer tr {
padding: 0;
margin: 0;
}
.egwGridView_grid td, .egwGridView_grid tr {
padding: 2px;
margin: 0;
}
.egwGridView_outer thead th {
background-color: #E0E0E0;
font-weight: normal;
padding: 5px;
text-align: left;
border-left: 1px solid silver;
border-top: 1px solid silver;
border-right: 1px solid gray;
border-bottom: 1px solid gray;
background-image: url(imgs/header_overlay.png);
background-position: center;
background-repeat: repeat-x;
}
/*----------------------------------------------------------------------------*/
.grid_outer {
border-spacing: 0;
border-collapse: collapse;
padding: 0;
margin: 0;
}
.grid_outer div.scrollarea {
overflow: auto;
width:100%;
}
.grid_outer td, .grid_outer tr {
padding: 0;
margin: 0;
}
.horizontal_spacer {
display: block;
background-image: url(imgs/non_loaded_bg.png);
background-position: top left;
}
.selectcols {
display: inline-block;
width: 10px;
height: 9px;
margin: 0;
padding: 0;
vertical-align: middle;
background-image: url(imgs/selectcols.png);
background-position: center;
background-repeat: no-repeat;
}
.grid {
border-spacing: 0;
border-collapse: collapse;
}
.grid th.optcol {
width: 6px;
padding: 0;
text-align: center;
} }
.grid tr.hidden { .grid tr.hidden {
@ -66,10 +156,22 @@ body, td, th {
font-weight: normal; font-weight: normal;
padding: 5px; padding: 5px;
text-align: left; text-align: left;
border-left: 1px solid silver;
border-top: 1px solid silver;
border-right: 1px solid gray;
border-bottom: 1px solid gray;
background-image: url(imgs/header_overlay.png);
background-position: center;
background-repeat: repeat-x;
}
margin: 0;
padding: 0;
border: none;
} }
.grid td { .grid td {
padding: 0 5px 0 5px; padding: 0;
vertical-align: middle; vertical-align: middle;
} }

Binary file not shown.

After

Width:  |  Height:  |  Size: 408 B

View File

@ -0,0 +1,147 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
width="64"
height="64"
id="svg2"
version="1.1"
inkscape:version="0.48.0 r9654"
sodipodi:docname="non_loaded_bg.svg"
inkscape:export-filename="/home/andreas/source/egroupware/trunk/egroupware/phpgwapi/js/egw_action/test/imgs/non_loaded_bg.png"
inkscape:export-xdpi="90"
inkscape:export-ydpi="90">
<defs
id="defs4" />
<sodipodi:namedview
id="base"
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1.0"
inkscape:pageopacity="0.0"
inkscape:pageshadow="2"
inkscape:zoom="3.959798"
inkscape:cx="32.98408"
inkscape:cy="37.795705"
inkscape:document-units="px"
inkscape:current-layer="layer1"
showgrid="true"
inkscape:window-width="1600"
inkscape:window-height="823"
inkscape:window-x="0"
inkscape:window-y="24"
inkscape:window-maximized="1">
<inkscape:grid
type="xygrid"
id="grid2985"
empspacing="8"
visible="true"
enabled="true"
snapvisiblegridlinesonly="true" />
</sodipodi:namedview>
<metadata
id="metadata7">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title></dc:title>
</cc:Work>
</rdf:RDF>
</metadata>
<g
inkscape:label="Ebene 1"
inkscape:groupmode="layer"
id="layer1"
transform="translate(0,-988.36218)">
<path
style="fill:none;stroke:#f5f5f5;stroke-width:5;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;stroke-miterlimit:4;stroke-dasharray:none"
d="M -8,72 72,-8"
id="path3792"
inkscape:connector-curvature="0"
transform="translate(0,988.36218)"
inkscape:export-filename="/home/andreas/source/egroupware/trunk/egroupware/phpgwapi/js/egw_action/test/imgs/path3808.png"
inkscape:export-xdpi="90"
inkscape:export-ydpi="90" />
<path
style="fill:none;stroke:#f5f5f5;stroke-width:5;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;stroke-miterlimit:4;stroke-dasharray:none"
d="M -8,56 56,-8"
id="path3794"
inkscape:connector-curvature="0"
transform="translate(0,988.36218)"
inkscape:export-filename="/home/andreas/source/egroupware/trunk/egroupware/phpgwapi/js/egw_action/test/imgs/path3808.png"
inkscape:export-xdpi="90"
inkscape:export-ydpi="90" />
<path
style="fill:none;stroke:#f5f5f5;stroke-width:5;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;stroke-miterlimit:4;stroke-dasharray:none"
d="M 40,-8 -8,40"
id="path3796"
inkscape:connector-curvature="0"
transform="translate(0,988.36218)"
inkscape:export-filename="/home/andreas/source/egroupware/trunk/egroupware/phpgwapi/js/egw_action/test/imgs/path3808.png"
inkscape:export-xdpi="90"
inkscape:export-ydpi="90" />
<path
style="fill:none;stroke:#f5f5f5;stroke-width:5;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;stroke-miterlimit:4;stroke-dasharray:none"
d="M -8,24 24,-8"
id="path3798"
inkscape:connector-curvature="0"
transform="translate(0,988.36218)"
inkscape:export-filename="/home/andreas/source/egroupware/trunk/egroupware/phpgwapi/js/egw_action/test/imgs/path3808.png"
inkscape:export-xdpi="90"
inkscape:export-ydpi="90" />
<path
style="fill:none;stroke:#f5f5f5;stroke-width:5;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;stroke-miterlimit:4;stroke-dasharray:none"
d="M 8,-8 -8,8"
id="path3800"
inkscape:connector-curvature="0"
transform="translate(0,988.36218)"
inkscape:export-filename="/home/andreas/source/egroupware/trunk/egroupware/phpgwapi/js/egw_action/test/imgs/path3808.png"
inkscape:export-xdpi="90"
inkscape:export-ydpi="90" />
<path
style="fill:none;stroke:#f5f5f5;stroke-width:5;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;stroke-miterlimit:4;stroke-dasharray:none"
d="M 72,8 8,72"
id="path3802"
inkscape:connector-curvature="0"
transform="translate(0,988.36218)"
inkscape:export-filename="/home/andreas/source/egroupware/trunk/egroupware/phpgwapi/js/egw_action/test/imgs/path3808.png"
inkscape:export-xdpi="90"
inkscape:export-ydpi="90" />
<path
style="fill:none;stroke:#f5f5f5;stroke-width:5;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;stroke-miterlimit:4;stroke-dasharray:none"
d="M 24,72 72,24"
id="path3804"
inkscape:connector-curvature="0"
transform="translate(0,988.36218)"
inkscape:export-filename="/home/andreas/source/egroupware/trunk/egroupware/phpgwapi/js/egw_action/test/imgs/path3808.png"
inkscape:export-xdpi="90"
inkscape:export-ydpi="90" />
<path
style="fill:none;stroke:#f5f5f5;stroke-width:5;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;stroke-miterlimit:4;stroke-dasharray:none"
d="M 72,40 40,72"
id="path3806"
inkscape:connector-curvature="0"
transform="translate(0,988.36218)"
inkscape:export-filename="/home/andreas/source/egroupware/trunk/egroupware/phpgwapi/js/egw_action/test/imgs/path3808.png"
inkscape:export-xdpi="90"
inkscape:export-ydpi="90" />
<path
style="fill:none;stroke:#f5f5f5;stroke-width:5;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;stroke-miterlimit:4;stroke-dasharray:none"
d="M 56,72 72,56"
id="path3808"
inkscape:connector-curvature="0"
transform="translate(0,988.36218)"
inkscape:export-xdpi="90"
inkscape:export-ydpi="90" />
</g>
</svg>

After

Width:  |  Height:  |  Size: 6.1 KiB