Moved et2 core interfaces to own file; implemented Class.free which cares about calling 'destroy', removing all references the object may hold and rendering the object unusable after 'free' has been called; added 'getMem_freeMem_trace' which helps hunting down memory leaks with objects derriving from Class; added first implementation of the nextmatch widget - currently does nothing else but rendering the header and clicking on the nextmatch_sortheader labels
@ -13,32 +13,10 @@
|
||||
"use strict";
|
||||
|
||||
/*egw:uses
|
||||
et2_core_interfaces;
|
||||
et2_core_widget;
|
||||
*/
|
||||
|
||||
/**
|
||||
* Interface for all widget classes, which are based on a DOM node.
|
||||
*/
|
||||
var et2_IDOMNode = new Interface({
|
||||
/**
|
||||
* Returns the DOM-Node of the current widget. The return value has to be
|
||||
* a plain DOM node. If you want to return an jQuery object as you receive
|
||||
* it with
|
||||
*
|
||||
* obj = $j(node);
|
||||
*
|
||||
* simply return obj[0];
|
||||
*
|
||||
* @param _sender The _sender parameter defines which widget is asking for
|
||||
* the DOMNode. Depending on that, the widget may return different nodes.
|
||||
* This is used in the grid. Normally the _sender parameter can be omitted
|
||||
* in most implementations of the getDOMNode function.
|
||||
* However, you should always define the _sender parameter when calling
|
||||
* getDOMNode!
|
||||
*/
|
||||
getDOMNode: function(_sender) {}
|
||||
});
|
||||
|
||||
/**
|
||||
* Abstract widget class which can be inserted into the DOM. All widget classes
|
||||
* deriving from this class have to care about implementing the "getDOMNode"
|
||||
@ -110,7 +88,7 @@ var et2_DOMWidget = et2_widget.extend(et2_IDOMNode, {
|
||||
|
||||
if (this._surroundingsMgr)
|
||||
{
|
||||
this._surroundingsMgr.destroy();
|
||||
this._surroundingsMgr.free();
|
||||
this._surroundingsMgr = null;
|
||||
}
|
||||
|
||||
|
@ -594,3 +594,13 @@ function et2_hasChild(_nodes, _child)
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates a localy unique id and returns it
|
||||
*/
|
||||
var _et2_uniqueId = 0;
|
||||
|
||||
function et2_uniqueId()
|
||||
{
|
||||
return _et2_uniqueId++;
|
||||
}
|
||||
|
||||
|
@ -72,7 +72,14 @@
|
||||
*/
|
||||
// Inspired by base2 and Prototype
|
||||
(function(){
|
||||
var initializing = false
|
||||
var initializing = false;
|
||||
|
||||
/**
|
||||
* Turn this to "true" to track creation and destruction of elements
|
||||
*/
|
||||
var getMem_freeMem_trace = false;
|
||||
|
||||
var tracedObjects = {};
|
||||
|
||||
// Check whether "function decompilation" works - fnTest is normally used to
|
||||
// check whether a
|
||||
@ -257,6 +264,18 @@
|
||||
}
|
||||
}
|
||||
|
||||
// Do some tracing of the getMem_freeMem_trace is activated
|
||||
if (getMem_freeMem_trace)
|
||||
{
|
||||
this.__OBJ_UID = "obj_" + et2_uniqueId();
|
||||
var className = this.className();
|
||||
tracedObjects[this.__OBJ_UID] = {
|
||||
"created": new Date().getTime(),
|
||||
"class": className
|
||||
}
|
||||
et2_debug("log", "*" + this.__OBJ_UID + " (" + className + ")");
|
||||
}
|
||||
|
||||
if (this.init)
|
||||
{
|
||||
this.init.apply(this, arguments);
|
||||
@ -289,6 +308,61 @@
|
||||
|
||||
// Add the basic functions
|
||||
|
||||
/**
|
||||
* Destructor function - it calls "destroy" if it has been defined and then
|
||||
* deletes all keys of this element, so that any access to this element will
|
||||
* eventually throw an exception, making it easier to hunt down memory leaks.
|
||||
*/
|
||||
Class.prototype.free = function() {
|
||||
if (this.destroy)
|
||||
{
|
||||
this.destroy();
|
||||
}
|
||||
|
||||
// Trace the freeing of the object
|
||||
if (getMem_freeMem_trace)
|
||||
{
|
||||
delete(tracedObjects[this.__OBJ_UID]);
|
||||
et2_debug("log", "-" + this.__OBJ_UID);
|
||||
}
|
||||
|
||||
// Delete every object entry
|
||||
for (var key in this)
|
||||
{
|
||||
delete(this[key]);
|
||||
}
|
||||
|
||||
// Don't raise an exception when attempting to free an element multiple
|
||||
// times.
|
||||
this.free = function() {};
|
||||
};
|
||||
|
||||
// Some debug functions for memory leak hunting
|
||||
if (getMem_freeMem_trace)
|
||||
{
|
||||
/**
|
||||
* Prints a list of all objects UIDs which have not been freed yet.
|
||||
*/
|
||||
Class.prototype.showTrace = function() {
|
||||
console.log(tracedObjects);
|
||||
},
|
||||
|
||||
/**
|
||||
* VERY slow - for debugging only!
|
||||
*/
|
||||
Class.prototype.className = function() {
|
||||
for (var key in window)
|
||||
{
|
||||
if (key.substr(0, 3) == "et2" && this.constructor == window[key])
|
||||
{
|
||||
return key;
|
||||
}
|
||||
}
|
||||
|
||||
return "?";
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the value of the given attribute. If the property does not
|
||||
* exist, an error message is issued.
|
||||
@ -310,7 +384,7 @@
|
||||
{
|
||||
et2_debug("error", this, "Attribute '" + _name + "' does not exist!");
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* The setAttribute function sets the attribute with the given name to
|
||||
@ -345,7 +419,7 @@
|
||||
{
|
||||
et2_debug("warn", this, "Attribute '" + _name + "' does not exist!");
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* generateAttributeSet sanitizes the given associative array of attributes
|
||||
@ -392,7 +466,7 @@
|
||||
}
|
||||
|
||||
return _attrs;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* The initAttributes function sets the attributes to their default
|
||||
@ -408,22 +482,22 @@
|
||||
this.setAttribute(key, _attrs[key], false);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* The implements function can be used to check whether the object
|
||||
* implements the given interface.
|
||||
*/
|
||||
Class.prototype.implements = function(_iface) {
|
||||
for (var key in _iface)
|
||||
for (var key in _iface)
|
||||
{
|
||||
if (this._ifacefuncs.indexOf(key) < 0)
|
||||
{
|
||||
if (this._ifacefuncs.indexOf(key) < 0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return true;
|
||||
};
|
||||
|
||||
/**
|
||||
* The instanceOf function can be used to check for both - classes and
|
||||
@ -431,15 +505,15 @@
|
||||
* affects IE and Opera support.
|
||||
*/
|
||||
Class.prototype.instanceOf = function(_obj) {
|
||||
if (_obj instanceof Interface)
|
||||
{
|
||||
if (_obj instanceof Interface)
|
||||
{
|
||||
return this.implements(_obj);
|
||||
}
|
||||
else
|
||||
{
|
||||
return this instanceof _obj;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
}).call(window);
|
||||
|
||||
|
@ -14,30 +14,10 @@
|
||||
|
||||
/*egw:uses
|
||||
jquery.jquery;
|
||||
et2_core_interfaces;
|
||||
et2_core_valueWidget;
|
||||
*/
|
||||
|
||||
/**
|
||||
* Interface for all widgets which support returning a value
|
||||
*/
|
||||
var et2_IInput = new Interface({
|
||||
/**
|
||||
* getValue has to return the value of the input widget
|
||||
*/
|
||||
getValue: function() {},
|
||||
|
||||
/**
|
||||
* Is dirty returns true if the value of the widget has changed since it
|
||||
* was loaded.
|
||||
*/
|
||||
isDirty: function() {},
|
||||
|
||||
/**
|
||||
* Causes the dirty flag to be reseted.
|
||||
*/
|
||||
resetDirty: function() {}
|
||||
});
|
||||
|
||||
/**
|
||||
* et2_inputWidget derrives from et2_simpleWidget and implements the IInput
|
||||
* interface. When derriving from this class, call setDOMNode with an input
|
||||
|
74
etemplate/js/et2_core_interfaces.js
Normal file
@ -0,0 +1,74 @@
|
||||
/**
|
||||
* eGroupWare eTemplate2 - File which contains all interfaces
|
||||
*
|
||||
* @license http://opensource.org/licenses/gpl-license.php GPL - GNU General Public License
|
||||
* @package etemplate
|
||||
* @subpackage api
|
||||
* @link http://www.egroupware.org
|
||||
* @author Andreas Stöckel
|
||||
* @copyright Stylite 2011
|
||||
* @version $Id$
|
||||
*/
|
||||
|
||||
"use strict"
|
||||
|
||||
/*egw:uses
|
||||
et2_core_inheritance;
|
||||
*/
|
||||
|
||||
/**
|
||||
* Interface for all widget classes, which are based on a DOM node.
|
||||
*/
|
||||
var et2_IDOMNode = new Interface({
|
||||
/**
|
||||
* Returns the DOM-Node of the current widget. The return value has to be
|
||||
* a plain DOM node. If you want to return an jQuery object as you receive
|
||||
* it with
|
||||
*
|
||||
* obj = $j(node);
|
||||
*
|
||||
* simply return obj[0];
|
||||
*
|
||||
* @param _sender The _sender parameter defines which widget is asking for
|
||||
* the DOMNode. Depending on that, the widget may return different nodes.
|
||||
* This is used in the grid. Normally the _sender parameter can be omitted
|
||||
* in most implementations of the getDOMNode function.
|
||||
* However, you should always define the _sender parameter when calling
|
||||
* getDOMNode!
|
||||
*/
|
||||
getDOMNode: function(_sender) {}
|
||||
});
|
||||
|
||||
/**
|
||||
* Interface for all widgets which support returning a value
|
||||
*/
|
||||
var et2_IInput = new Interface({
|
||||
/**
|
||||
* getValue has to return the value of the input widget
|
||||
*/
|
||||
getValue: function() {},
|
||||
|
||||
/**
|
||||
* Is dirty returns true if the value of the widget has changed since it
|
||||
* was loaded.
|
||||
*/
|
||||
isDirty: function() {},
|
||||
|
||||
/**
|
||||
* Causes the dirty flag to be reseted.
|
||||
*/
|
||||
resetDirty: function() {}
|
||||
});
|
||||
|
||||
/**
|
||||
* Interface for widgets which should be automatically resized
|
||||
*/
|
||||
var et2_IResizeable = new Interface({
|
||||
/**
|
||||
* Called whenever the window is resized
|
||||
*/
|
||||
resize: function() {}
|
||||
});
|
||||
|
||||
|
||||
|
95
etemplate/js/et2_core_stylesheet.js
Normal file
@ -0,0 +1,95 @@
|
||||
/**
|
||||
* eGroupWare eTemplate2 - Stylesheet class
|
||||
*
|
||||
* @license http://opensource.org/licenses/gpl-license.php GPL - GNU General Public License
|
||||
* @package etemplate
|
||||
* @subpackage api
|
||||
* @link http://www.egroupware.org
|
||||
* @author Andreas Stöckel
|
||||
* @copyright Stylite 2011
|
||||
* @version $Id$
|
||||
*/
|
||||
|
||||
"use strict"
|
||||
|
||||
/**
|
||||
* Contains the egwDynStyleSheet class which allows dynamic generation of stylesheet
|
||||
* rules - updating a single stylesheet rule is way more efficient than updating
|
||||
* the element style of many objects.
|
||||
*/
|
||||
var EGW_DYNAMIC_STYLESHEET = null;
|
||||
|
||||
/**
|
||||
* Main egwDynStyleSheet class - all egwDynStyleSheets share the same stylesheet
|
||||
* which is dynamically inserted into the head section of the DOM-Tree.
|
||||
* This stylesheet is created with the first egwDynStyleSheet class.
|
||||
*/
|
||||
function et2_dynStyleSheet()
|
||||
{
|
||||
// Check whether the EGW_DYNAMIC_STYLESHEET has already be created
|
||||
if (!EGW_DYNAMIC_STYLESHEET)
|
||||
{
|
||||
var style = document.createElement("style");
|
||||
document.getElementsByTagName("head")[0].appendChild(style);
|
||||
|
||||
this.styleSheet = style.sheet ? style.sheet : style.styleSheet;
|
||||
this.selectors = {};
|
||||
this.selectorCount = 0;
|
||||
|
||||
EGW_DYNAMIC_STYLESHEET = this;
|
||||
|
||||
return this;
|
||||
}
|
||||
else
|
||||
{
|
||||
return EGW_DYNAMIC_STYLESHEET;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates/Updates the given stylesheet rule. Example call:
|
||||
*
|
||||
* styleSheet.updateRule("#container", "background-color: blue; font-family: sans;")
|
||||
*
|
||||
* @param string _selector is the css selector to which the given rule should apply
|
||||
* @param string _rule is the rule which is bound to the selector.
|
||||
*/
|
||||
et2_dynStyleSheet.prototype.updateRule = function (_selector, _rule)
|
||||
{
|
||||
var ruleObj = {
|
||||
"index": this.selectorCount
|
||||
}
|
||||
|
||||
// Remove any existing rule first
|
||||
if (typeof this.selectors[_selector] !== "undefined")
|
||||
{
|
||||
var ruleObj = this.selectors[_selector];
|
||||
if (typeof this.styleSheet.removeRule !== "undefined")
|
||||
{
|
||||
this.styleSheet.removeRule(ruleObj.index);
|
||||
}
|
||||
else
|
||||
{
|
||||
this.styleSheet.deleteRule(ruleObj.index);
|
||||
}
|
||||
|
||||
delete (this.selectors[_selector]);
|
||||
}
|
||||
else
|
||||
{
|
||||
this.selectorCount++;
|
||||
}
|
||||
|
||||
// Add the rule to the stylesheet
|
||||
if (typeof this.styleSheet.addRule !== "undefined")
|
||||
{
|
||||
this.styleSheet.addRule(_selector, _rule, ruleObj.index);
|
||||
}
|
||||
else
|
||||
{
|
||||
this.styleSheet.insertRule(_selector + "{" + _rule + "}", ruleObj.index);
|
||||
}
|
||||
|
||||
this.selectors[_selector] = ruleObj;
|
||||
}
|
||||
|
@ -220,7 +220,7 @@ var et2_widget = Class.extend({
|
||||
// Call the destructor of all children
|
||||
for (var i = this._children.length - 1; i >= 0; i--)
|
||||
{
|
||||
this._children[i].destroy();
|
||||
this._children[i].free();
|
||||
}
|
||||
|
||||
// Remove this element from the parent
|
||||
@ -322,7 +322,13 @@ var et2_widget = Class.extend({
|
||||
// Check whether the node is one of the supported widget classes.
|
||||
if (this.isOfSupportedWidgetClass(_node))
|
||||
{
|
||||
_node.parent = this;
|
||||
// Remove the node from its original parent
|
||||
if (_node._parent)
|
||||
{
|
||||
_node._parent.removeChild(_node);
|
||||
}
|
||||
|
||||
_node._parent = this;
|
||||
this._children.splice(_idx, 0, _node);
|
||||
}
|
||||
else
|
||||
|
439
etemplate/js/et2_dataview_view_gridcontainer.js
Normal file
@ -0,0 +1,439 @@
|
||||
/**
|
||||
* eGroupWare eTemplate2 - Class which generates the outer container for the grid
|
||||
*
|
||||
* @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_core_common;
|
||||
et2_core_stylesheet;
|
||||
*/
|
||||
|
||||
/**
|
||||
* Base view class which is responsible for displaying a grid view element.
|
||||
*/
|
||||
var et2_dataview_gridContainer = Class.extend({
|
||||
|
||||
/**
|
||||
* Constant which regulates the column padding.
|
||||
*/
|
||||
columnPadding: 2,
|
||||
|
||||
/**
|
||||
* Some browser dependant variables which will be calculated on creation of
|
||||
* the first gridContainer object.
|
||||
*/
|
||||
scrollbarWidth: false,
|
||||
headerBorderWidth: false,
|
||||
columnBorderWidth: false,
|
||||
|
||||
/**
|
||||
* Constructor for the grid container
|
||||
* @param object _parentNode is the DOM-Node into which the grid view will be inserted
|
||||
*/
|
||||
init: function(_parentNode) {
|
||||
|
||||
// Copy the parent node parameter
|
||||
this.parentNode = $j(_parentNode);
|
||||
|
||||
// Initialize some variables
|
||||
this.columnNodes = []; // Array with the header containers
|
||||
this.columns = [];
|
||||
this.columnMgr = null;
|
||||
|
||||
this.width = 0;
|
||||
this.height = 0;
|
||||
|
||||
// Build the base nodes
|
||||
this._createElements();
|
||||
|
||||
// Read the browser dependant variables
|
||||
this._getDepVars();
|
||||
},
|
||||
|
||||
/**
|
||||
* Destroys the object, removes all dom nodes and clears all references.
|
||||
*/
|
||||
destroy: function() {
|
||||
// Clear the columns
|
||||
this._clearHeader();
|
||||
|
||||
// Detatch the outer element
|
||||
this.table.remove();
|
||||
},
|
||||
|
||||
/**
|
||||
* Returns the column container node for the given column index
|
||||
*
|
||||
* @param _columnIdx the integer column index
|
||||
*/
|
||||
getHeaderContainerNode: function(_columnIdx) {
|
||||
if (typeof this.columnNodes[_columnIdx] != "undefined")
|
||||
{
|
||||
return this.columnNodes[_columnIdx].container[0];
|
||||
}
|
||||
|
||||
return null;
|
||||
},
|
||||
|
||||
/**
|
||||
* Sets the column descriptors and creates the column header according to it.
|
||||
* The inner grid will be emptied if it has already been built.
|
||||
*/
|
||||
setColumns: function(_columnData) {
|
||||
// Free all column objects which have been created till this moment
|
||||
this.headTr.empty();
|
||||
|
||||
// Create the column manager and pass the _columnData to it
|
||||
this.columns = _columnData; //XXX
|
||||
//this.columnMgr = new et2_dataview_columnsMgr(_columnData);
|
||||
|
||||
// Build the header row
|
||||
this._buildHeader();
|
||||
},
|
||||
|
||||
/**
|
||||
* Resizes the grid
|
||||
*/
|
||||
resize: function(_w, _h) {
|
||||
if (this.width != _w)
|
||||
{
|
||||
this.width = _w;
|
||||
|
||||
// Rebuild the column stylesheets
|
||||
this._updateColumns();
|
||||
}
|
||||
|
||||
if (this.height != _h)
|
||||
{
|
||||
// Set the height of the grid.
|
||||
// TODO
|
||||
}
|
||||
},
|
||||
|
||||
|
||||
/* --- PRIVATE FUNCTIONS --- */
|
||||
|
||||
/* --- Code for building the grid container DOM-Tree elements ---- */
|
||||
|
||||
/**
|
||||
* Builds the base DOM-Tree elements
|
||||
*/
|
||||
_createElements: function() {
|
||||
/*
|
||||
Structure:
|
||||
<table class="egwGridView_outer">
|
||||
<thead>
|
||||
<tr> [HEAD] </tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr> [GRID CONTAINER] </tr>
|
||||
</tbody>
|
||||
</table>
|
||||
*/
|
||||
|
||||
this.containerTr = $j(document.createElement("tr"));
|
||||
this.headTr = $j(document.createElement("tr"));
|
||||
|
||||
this.thead = $j(document.createElement("thead"))
|
||||
.append(this.headTr);
|
||||
this.tbody = $j(document.createElement("tbody"))
|
||||
.append(this.containerTr);
|
||||
|
||||
this.table = $j(document.createElement("table"))
|
||||
.addClass("egwGridView_outer")
|
||||
.append(this.thead, this.tbody)
|
||||
.appendTo(this.parentNode);
|
||||
},
|
||||
|
||||
|
||||
/* --- Code for building the header row --- */
|
||||
|
||||
/**
|
||||
* Clears the header row
|
||||
*/
|
||||
_clearHeader: function() {
|
||||
// Destroy the column manager if it had been created
|
||||
if (this.columnMgr)
|
||||
{
|
||||
this.columnMgr.free();
|
||||
this.columnMgr = null;
|
||||
}
|
||||
|
||||
// Reset the headerColumns array and empty the table row
|
||||
this.columnNodes = [];
|
||||
this.headTr.empty();
|
||||
},
|
||||
|
||||
/**
|
||||
* Builds the containers for the header row
|
||||
*/
|
||||
_buildHeader: function() {
|
||||
for (var i = 0; i < this.columns.length; i++)
|
||||
{
|
||||
var col = this.columns[i];
|
||||
|
||||
// Create the column header and the container element
|
||||
var cont = $j(document.createElement("div"))
|
||||
.addClass("innerContainer")
|
||||
.addClass(col.divClass);
|
||||
var column = $j(document.createElement("th"))
|
||||
.addClass(col.tdClass)
|
||||
.append(cont)
|
||||
.appendTo(this.headTr);
|
||||
|
||||
// Store both nodes in the columnNodes array
|
||||
this.columnNodes.push({
|
||||
"column": column,
|
||||
"container": cont
|
||||
});
|
||||
}
|
||||
|
||||
this._buildSelectCol();
|
||||
},
|
||||
|
||||
/**
|
||||
* Builds the select cols column
|
||||
*/
|
||||
_buildSelectCol: function() {
|
||||
// Build the "select columns" icon
|
||||
this.selectColIcon = $j(document.createElement("span"))
|
||||
.addClass("selectcols");
|
||||
|
||||
// Build the option column
|
||||
this.selectCol = $j(document.createElement("th"))
|
||||
.addClass("optcol")
|
||||
.append(this.selectColIcon)
|
||||
.appendTo(this.headTr);
|
||||
|
||||
this.selectCol.css("width", this.scrollbarWidth - this.selectCol.outerWidth()
|
||||
+ this.selectCol.width() + 1);
|
||||
},
|
||||
|
||||
|
||||
/* --- Code for calculating the browser/css depending widths --- */
|
||||
|
||||
/**
|
||||
* Reads the browser dependant variables
|
||||
*/
|
||||
_getDepVars: function() {
|
||||
if (this.scrollbarWidth === false)
|
||||
{
|
||||
// Clone the table and attach it to the outer body tag
|
||||
var clone = this.table.clone();
|
||||
$j(window.top.document.getElementsByTagName("body")[0])
|
||||
.append(clone);
|
||||
|
||||
// Read the scrollbar width
|
||||
this.scrollbarWidth = this.constructor.prototype.scrollbarWidth =
|
||||
this._getScrollbarWidth(clone);
|
||||
|
||||
// Read the header border width
|
||||
this.headerBorderWidth = this.constructor.prototype.headerBorderWidth =
|
||||
this._getHeaderBorderWidth(clone);
|
||||
|
||||
// Read the column border width
|
||||
this.columnBorderWidth = this.constructor.prototype.columnBorderWidth =
|
||||
this._getColumnBorderWidth(clone);
|
||||
|
||||
// Remove the cloned DOM-Node again from the outer body
|
||||
clone.remove();
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Reads the scrollbar width
|
||||
*/
|
||||
_getScrollbarWidth: function(_table) {
|
||||
// Create a temporary td and two divs, which are inserted into the
|
||||
// DOM-Tree. The outer div has a fixed size and "overflow" set to auto.
|
||||
// When the second div is inserted, it will be forced to display a scrollbar.
|
||||
var div_inner = $j(document.createElement("div"))
|
||||
.css("height", "1000px");
|
||||
var div_outer = $j(document.createElement("div"))
|
||||
.css("height", "100px")
|
||||
.css("width", "100px")
|
||||
.css("overflow", "auto")
|
||||
.append(div_inner);
|
||||
var td = $j(document.createElement("td"))
|
||||
.append(div_outer);
|
||||
|
||||
// Store the scrollbar width statically.
|
||||
$j("tbody tr", _table).append(td);
|
||||
var width = Math.max(10, div_outer.outerWidth() - div_inner.outerWidth());
|
||||
|
||||
// Remove the elements again
|
||||
div_outer.remove();
|
||||
|
||||
return width;
|
||||
},
|
||||
|
||||
/**
|
||||
* Calculates the total width of the header column border
|
||||
*/
|
||||
_getHeaderBorderWidth: function(_table) {
|
||||
// Create a temporary th which is appended to the outer thead row
|
||||
var cont = $j(document.createElement("div"))
|
||||
.addClass("innerContainer");
|
||||
|
||||
var th = $j(document.createElement("th"))
|
||||
.append(cont);
|
||||
|
||||
// Insert the th into the document tree
|
||||
$j("thead tr", _table).append(th);
|
||||
|
||||
// Calculate the total border width
|
||||
var width = th.outerWidth(true) - cont.width();
|
||||
|
||||
// Remove the appended element again
|
||||
th.remove();
|
||||
|
||||
return width;
|
||||
},
|
||||
|
||||
/**
|
||||
* Calculates the total width of the column border
|
||||
*/
|
||||
_getColumnBorderWidth : function(_table) {
|
||||
// Create a temporary th which is appended to the outer thead row
|
||||
var cont = $j(document.createElement("div"))
|
||||
.addClass("innerContainer");
|
||||
|
||||
var td = $j(document.createElement("td"))
|
||||
.append(cont);
|
||||
|
||||
// Insert the th into the document tree
|
||||
$j("tbody tr", _table).append(td);
|
||||
|
||||
// Calculate the total border width
|
||||
_table.addClass("egwGridView_grid");
|
||||
var width = td.outerWidth(true) - cont.width();
|
||||
|
||||
// Remove the appended element again
|
||||
td.remove();
|
||||
|
||||
return width;
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
|
||||
|
||||
|
||||
/*
|
||||
* Sets the column data which is retrieved by calling egwGridColumns.getColumnData.
|
||||
* The columns will be updated.
|
||||
*/
|
||||
/*egwGridViewOuter.prototype.updateColumns = function(_columns)
|
||||
{
|
||||
// Copy the columns data
|
||||
this.columns = _columns;
|
||||
|
||||
var first = true;
|
||||
|
||||
// Count the visible rows
|
||||
var total_cnt = 0;
|
||||
for (var i = 0; i < this.columns.length; i++)
|
||||
{
|
||||
if (this.columns[i].visible)
|
||||
{
|
||||
total_cnt++;
|
||||
}
|
||||
}
|
||||
|
||||
var vis_col = this.visibleColumnCount = 0;
|
||||
var totalWidth = 0;
|
||||
|
||||
// Set the grid column styles
|
||||
for (var i = 0; i < this.columns.length; i++)
|
||||
{
|
||||
var col = this.columns[i];
|
||||
|
||||
col.tdClass = this.uniqueId + "_td_" + col.id;
|
||||
col.divClass = this.uniqueId + "_div_" + col.id;
|
||||
|
||||
if (col.visible)
|
||||
{
|
||||
vis_col++;
|
||||
this.visibleColumnCount++;
|
||||
|
||||
this.styleSheet.updateRule("." + col.tdClass,
|
||||
"display: " + (col.visible ? "table-cell" : "none") + "; " +
|
||||
((vis_col == total_cnt) ? "border-right-width: 0 " : "border-right-width: 1px ") +
|
||||
"!important;");
|
||||
|
||||
this.styleSheet.updateRule(".egwGridView_outer ." + col.divClass,
|
||||
"width: " + (col.width - this.headerBorderWidth) + "px;");
|
||||
|
||||
// Ugly browser dependant code - each browser seems to treat the
|
||||
// right (collapsed) border of the row differently
|
||||
addBorder = 0;
|
||||
if ($j.browser.mozilla)
|
||||
{
|
||||
var maj = $j.browser.version.split(".")[0];
|
||||
if (maj < 2) {
|
||||
addBorder = 1; // Versions <= FF 3.6
|
||||
}
|
||||
}
|
||||
if ($j.browser.webkit && !first)
|
||||
{
|
||||
addBorder = 1;
|
||||
}
|
||||
if (($j.browser.msie || $j.browser.opera) && first)
|
||||
{
|
||||
addBorder = -1;
|
||||
}
|
||||
|
||||
// Make the last columns one pixel smaller, to prevent a horizontal
|
||||
// scrollbar from showing up
|
||||
if (vis_col == total_cnt)
|
||||
{
|
||||
addBorder += 1;
|
||||
}
|
||||
|
||||
var width = (col.width - this.columnBorderWidth - addBorder);
|
||||
|
||||
this.styleSheet.updateRule(".egwGridView_grid ." + col.divClass,
|
||||
"width: " + width + "px;");
|
||||
|
||||
totalWidth += col.width;
|
||||
|
||||
first = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
this.styleSheet.updateRule("." + col.tdClass,
|
||||
"display: " + (col.visible ? "table-cell" : "none") + ";");
|
||||
}
|
||||
}
|
||||
|
||||
// Add the full row and spacer class
|
||||
this.styleSheet.updateRule(".egwGridView_grid ." + this.uniqueId + "_div_fullRow",
|
||||
"width: " + (totalWidth - this.columnBorderWidth - 1) + "px; border-right-width: 0 !important;");
|
||||
this.styleSheet.updateRule(".egwGridView_outer ." + this.uniqueId + "_spacer_fullRow",
|
||||
"width: " + (totalWidth - 1) + "px; border-right-width: 0 !important;");
|
||||
|
||||
// Build the header if this hasn't been done yet
|
||||
this.buildBaseHeader();
|
||||
|
||||
// Update the grid
|
||||
this.grid.updateColumns(this.columns);
|
||||
}
|
||||
|
||||
|
||||
|
||||
egwGridViewOuter.prototype.setHeight = function(_h)
|
||||
{
|
||||
this.grid.setScrollHeight(_h - this.outer_thead.outerHeight());
|
||||
}
|
||||
|
||||
});*/
|
351
etemplate/js/et2_extension_nextmatch.js
Normal file
@ -0,0 +1,351 @@
|
||||
/**
|
||||
* eGroupWare eTemplate2 - JS Nextmatch object
|
||||
*
|
||||
* @license http://opensource.org/licenses/gpl-license.php GPL - GNU General Public License
|
||||
* @package etemplate
|
||||
* @subpackage api
|
||||
* @link http://www.egroupware.org
|
||||
* @author Andreas Stöckel
|
||||
* @copyright Stylite 2011
|
||||
* @version $Id$
|
||||
*/
|
||||
|
||||
"use strict";
|
||||
|
||||
/*egw:uses
|
||||
jquery.jquery;
|
||||
et2_widget;
|
||||
et2_core_interfaces;
|
||||
et2_core_DOMWidget;
|
||||
et2_widget_template;
|
||||
et2_widget_grid;
|
||||
et2_widget_selectbox;
|
||||
*/
|
||||
|
||||
/**
|
||||
* Interface all special nextmatch header elements have to implement.
|
||||
*/
|
||||
var et2_INextmatchHeader = new Interface({
|
||||
|
||||
/**
|
||||
* The 'setNextmatch' function is called by the parent nextmatch widget
|
||||
* and tells the nextmatch header widgets which widget they should direct
|
||||
* their 'sort', 'search' or 'filter' calls to.
|
||||
*/
|
||||
setNextmatch: function(_nextmatch) {}
|
||||
});
|
||||
|
||||
var et2_INextmatchSortable = new Interface({
|
||||
|
||||
setSortmode: function(_mode) {}
|
||||
|
||||
});
|
||||
|
||||
/**
|
||||
* Class which implements the "nextmatch" XET-Tag
|
||||
*/
|
||||
var et2_nextmatch = et2_DOMWidget.extend({
|
||||
|
||||
attributes: {
|
||||
"template": {
|
||||
"name": "Template",
|
||||
"type": "string",
|
||||
"description": "The id of the template which contains the grid layout."
|
||||
}
|
||||
},
|
||||
|
||||
legacyOptions: ["template"],
|
||||
|
||||
init: function() {
|
||||
this._super.apply(this, arguments);
|
||||
|
||||
this.div = $j(document.createElement("div"))
|
||||
.addClass("et2_nextmatch");
|
||||
|
||||
// Create the outer grid container
|
||||
this.dataviewContainer = new et2_dataview_gridContainer(this.div);
|
||||
|
||||
this.columns = [];
|
||||
this.activeFilters = {};
|
||||
},
|
||||
|
||||
destroy: function() {
|
||||
// Destroy the dataview objects
|
||||
this.dataviewContainer.free();
|
||||
|
||||
this._super.apply(this, arguments);
|
||||
},
|
||||
|
||||
/**
|
||||
* Sorts the nextmatch widget by the given ID.
|
||||
*
|
||||
* @param _id is the id of the data entry which should be sorted.
|
||||
* @param _asc if true, the elements are sorted ascending, otherwise
|
||||
* descending. If not set, the sort direction will be determined
|
||||
* automatically.
|
||||
*/
|
||||
sortBy: function(_id, _asc) {
|
||||
// Create the "sort" entry in the active filters if it did not exist
|
||||
// yet.
|
||||
if (typeof this.activeFilters["sort"] == "undefined")
|
||||
{
|
||||
this.activeFilters["sort"] = {
|
||||
"id": null,
|
||||
"asc": true
|
||||
};
|
||||
}
|
||||
|
||||
// Determine the sort direction automatically if it is not set
|
||||
if (typeof _asc == "undefined")
|
||||
{
|
||||
if (this.activeFilters["sort"].id == _id)
|
||||
{
|
||||
_asc = !this.activeFilters["sort"].asc;
|
||||
}
|
||||
}
|
||||
|
||||
// Update the entry in the activeFilters object
|
||||
this.activeFilters["sort"] = {
|
||||
"id": _id,
|
||||
"asc": _asc
|
||||
}
|
||||
|
||||
// Set the sortmode display
|
||||
this.iterateOver(function(_widget) {
|
||||
_widget.setSortmode((_widget.id == _id) ? (_asc ? "asc": "desc") : "none");
|
||||
}, this, et2_INextmatchSortable);
|
||||
|
||||
et2_debug("info", "Sorting nextmatch by '" + _id + "' in direction '" +
|
||||
(_asc ? "asc" : "desc") + "'");
|
||||
},
|
||||
|
||||
resetSort: function() {
|
||||
// Check whether the nextmatch widget is currently sorted
|
||||
if (typeof this.activeFilters["sort"] != "undefined")
|
||||
{
|
||||
// Reset the sortmode
|
||||
this.iterateOver(function(_widget) {
|
||||
_widget.setSortmode("none");
|
||||
}, this, et2_INextmatchSortable);
|
||||
|
||||
// If yes, delete the "sort" filter
|
||||
delete(this.activeFilters["sort"]);
|
||||
this.applyFilters();
|
||||
}
|
||||
},
|
||||
|
||||
applyFilters: function() {
|
||||
et2_debug("info", "Changing nextmatch filters to ", this.activeFilters);
|
||||
},
|
||||
|
||||
/**
|
||||
* Generates the column name for the given column widget
|
||||
*/
|
||||
_genColumnName: function(_widget) {
|
||||
var result = null;
|
||||
|
||||
_widget.iterateOver(function(_widget) {
|
||||
if (!result)
|
||||
{
|
||||
result = _widget.options.label;
|
||||
}
|
||||
else
|
||||
{
|
||||
result += ", " + _widget.options.label;
|
||||
}
|
||||
}, this, et2_INextmatchHeader);
|
||||
|
||||
return result;
|
||||
},
|
||||
|
||||
_parseHeaderRow: function(_row) {
|
||||
// Go over the header row and create the column entries
|
||||
this.columns = new Array(_row.length);
|
||||
for (var x = 0; x < _row.length; x++)
|
||||
{
|
||||
this.columns[x] = {
|
||||
"widget": _row[x].widget,
|
||||
"name": this._genColumnName(_row[x].widget),
|
||||
"disabled": false,
|
||||
"canDisable": true,
|
||||
"width": "auto"
|
||||
}
|
||||
|
||||
// Append the widget to this container
|
||||
this.addChild(_row[x].widget);
|
||||
}
|
||||
|
||||
this.dataviewContainer.setColumns(this.columns);
|
||||
},
|
||||
|
||||
_parseGrid: function(_grid) {
|
||||
// Search the rows for a header-row - if one is found, parse it
|
||||
for (var y = 0; y < _grid.rowData.length; y++)
|
||||
{
|
||||
if (_grid.rowData[y]["class"] == "th")
|
||||
{
|
||||
this._parseHeaderRow(_grid.cells[y]);
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* When the template attribute is set, the nextmatch widget tries to load
|
||||
* that template and to fetch the grid which is inside of it. It then calls
|
||||
* _parseGrid in order to get the information for the column headers etc.
|
||||
*/
|
||||
set_template: function(_value) {
|
||||
if (!this.template)
|
||||
{
|
||||
// Load the template
|
||||
var template = et2_createWidget("template", {"id": _value}, this);
|
||||
|
||||
if (!template.proxiedTemplate)
|
||||
{
|
||||
et2_debug("error", "Error while loading definition template for" +
|
||||
"nextmatch widget.");
|
||||
return;
|
||||
}
|
||||
|
||||
// Fetch the grid element and parse it
|
||||
var definitionGrid = template.proxiedTemplate.getChildren()[0];
|
||||
if (definitionGrid && definitionGrid instanceof et2_grid)
|
||||
{
|
||||
this._parseGrid(definitionGrid);
|
||||
}
|
||||
else
|
||||
{
|
||||
et2_debug("error", "Nextmatch widget expects a grid to be the " +
|
||||
"first child of the defined template.");
|
||||
return;
|
||||
}
|
||||
|
||||
// Free the template again
|
||||
template.free();
|
||||
|
||||
// Call the "setNextmatch" function of all registered
|
||||
// INextmatchHeader widgets.
|
||||
this.iterateOver(function (_node) {
|
||||
_node.setNextmatch(this);
|
||||
}, this, et2_INextmatchHeader);
|
||||
}
|
||||
},
|
||||
|
||||
getDOMNode: function(_sender) {
|
||||
if (_sender == this)
|
||||
{
|
||||
return this.div[0];
|
||||
}
|
||||
|
||||
for (var i = 0; i < this.columns.length; i++)
|
||||
{
|
||||
if (_sender == this.columns[i].widget)
|
||||
{
|
||||
return this.dataviewContainer.getHeaderContainerNode(i);
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
et2_register_widget(et2_nextmatch, ["nextmatch"]);
|
||||
|
||||
|
||||
/**
|
||||
* Classes for the nextmatch sortheaders etc.
|
||||
*/
|
||||
var et2_nextmatch_header = et2_baseWidget.extend(et2_INextmatchHeader, {
|
||||
|
||||
attributes: {
|
||||
"label": {
|
||||
"name": "Caption",
|
||||
"type": "string",
|
||||
"description": "Caption for the nextmatch header",
|
||||
"translate": true
|
||||
}
|
||||
},
|
||||
|
||||
init: function() {
|
||||
this._super.apply(this, arguments);
|
||||
|
||||
this.labelNode = $j(document.createElement("span"));
|
||||
this.nextmatch = null;
|
||||
|
||||
this.setDOMNode(this.labelNode[0]);
|
||||
},
|
||||
|
||||
destroy: function() {
|
||||
this._super.apply(this, arguments);
|
||||
},
|
||||
|
||||
/**
|
||||
* Set nextmatch is the function which has to be implemented for the
|
||||
* et2_INextmatchHeader interface.
|
||||
*/
|
||||
setNextmatch: function(_nextmatch) {
|
||||
this.nextmatch = _nextmatch;
|
||||
},
|
||||
|
||||
set_label: function(_value) {
|
||||
this.label = _value;
|
||||
|
||||
this.labelNode.text(_value);
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
et2_register_widget(et2_nextmatch_header, ['nextmatch-header',
|
||||
'nextmatch-accountfilter', 'nextmatch-customfilter', 'nextmatch-customfields']);
|
||||
|
||||
var et2_nextmatch_sortheader = et2_nextmatch_header.extend(et2_INextmatchSortable, {
|
||||
|
||||
init: function() {
|
||||
this._super.apply(this, arguments);
|
||||
|
||||
this.sortmode = "none";
|
||||
|
||||
this.labelNode.addClass("nextmatch_sortheader none");
|
||||
},
|
||||
|
||||
click: function() {
|
||||
if (this.nextmatch && this._super.apply(this, arguments))
|
||||
{
|
||||
this.nextmatch.sortBy(this.id);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
},
|
||||
|
||||
/**
|
||||
* Function which implements the et2_INextmatchSortable function.
|
||||
*/
|
||||
setSortmode: function(_mode) {
|
||||
// Remove the last sortmode class and add the new one
|
||||
this.labelNode.removeClass(this.sortmode)
|
||||
.addClass(_mode);
|
||||
|
||||
this.sortmode = _mode;
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
et2_register_widget(et2_nextmatch_sortheader, ['nextmatch-sortheader']);
|
||||
|
||||
|
||||
var et2_nextmatch_filterheader = et2_selectbox.extend(et2_INextmatchHeader, {
|
||||
|
||||
/**
|
||||
* Set nextmatch is the function which has to be implemented for the
|
||||
* et2_INextmatchHeader interface.
|
||||
*/
|
||||
setNextmatch: function(_nextmatch) {
|
||||
this.nextmatch = _nextmatch;
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
et2_register_widget(et2_nextmatch_filterheader, ['nextmatch-filterheader']);
|
||||
|
@ -14,7 +14,7 @@
|
||||
|
||||
/*egw:uses
|
||||
jquery.jquery;
|
||||
et2_core_inputWidget;
|
||||
et2_core_interfaces;
|
||||
et2_core_baseWidget;
|
||||
*/
|
||||
|
||||
|
@ -39,17 +39,12 @@ var et2_grid = et2_DOMWidget.extend({
|
||||
|
||||
// 2D-Array which holds references to the DOM td tags
|
||||
this.cells = [];
|
||||
this.rowData = [];
|
||||
this.managementArray = [];
|
||||
},
|
||||
|
||||
destroy: function() {
|
||||
this._super.call(this, arguments);
|
||||
|
||||
// Delete all references to cells or widgets
|
||||
this.cells = null;
|
||||
this.managementArray = null;
|
||||
this.table = null;
|
||||
this.tbody = null;
|
||||
},
|
||||
|
||||
_initCells: function(_colData, _rowData) {
|
||||
@ -72,6 +67,7 @@ var et2_grid = et2_DOMWidget.extend({
|
||||
"colData": _colData[x],
|
||||
"rowData": _rowData[y],
|
||||
"disabled": _colData[x].disabled || _rowData[y].disabled,
|
||||
"class": _colData[x]["class"],
|
||||
"colSpan": 1,
|
||||
"autoColSpan": false,
|
||||
"rowSpan": 1,
|
||||
@ -369,7 +365,7 @@ var et2_grid = et2_DOMWidget.extend({
|
||||
this._expandLastCells(cells);
|
||||
|
||||
// Create the table rows
|
||||
this.createTableFromCells(cells);
|
||||
this.createTableFromCells(cells, rowData);
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -377,20 +373,26 @@ var et2_grid = et2_DOMWidget.extend({
|
||||
}
|
||||
},
|
||||
|
||||
createTableFromCells: function(_cells) {
|
||||
createTableFromCells: function(_cells, _rowData) {
|
||||
// Set the rowCount and columnCount variables
|
||||
var h = this.rowCount = _cells.length;
|
||||
var w = this.columnCount = (h > 0) ? _cells[0].length : 0;
|
||||
|
||||
this.managementArray = [];
|
||||
this.cells = _cells;
|
||||
this.rowData = _rowData;
|
||||
|
||||
// Create the table rows.
|
||||
for (var y = 0; y < h; y++)
|
||||
{
|
||||
var row = _cells[y];
|
||||
var tr = $j(document.createElement("tr")).appendTo(this.tbody);
|
||||
var row_hidden = true;
|
||||
var tr = $j(document.createElement("tr")).appendTo(this.tbody)
|
||||
.addClass(this.rowData[y]["class"]);
|
||||
|
||||
if (this.rowData[y].disabled)
|
||||
{
|
||||
tr.hide();
|
||||
}
|
||||
|
||||
// Create the cells. x is incremented by the colSpan value of the
|
||||
// cell.
|
||||
@ -402,16 +404,12 @@ var et2_grid = et2_DOMWidget.extend({
|
||||
if (cell.td == null && cell.widget != null)
|
||||
{
|
||||
// Create the cell
|
||||
var td = $j(document.createElement("td")).appendTo(tr);
|
||||
var td = $j(document.createElement("td")).appendTo(tr)
|
||||
.addClass(cell["class"]);
|
||||
|
||||
if (cell.disabled)
|
||||
{
|
||||
td.hide();
|
||||
//td.css("border", "2px solid red");
|
||||
}
|
||||
else
|
||||
{
|
||||
row_hidden = false;
|
||||
}
|
||||
|
||||
// Add the entry for the widget to the management array
|
||||
@ -450,11 +448,6 @@ var et2_grid = et2_DOMWidget.extend({
|
||||
x++;
|
||||
}
|
||||
}
|
||||
|
||||
if (row_hidden)
|
||||
{
|
||||
tr.hide();
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
@ -468,6 +461,16 @@ var et2_grid = et2_DOMWidget.extend({
|
||||
// Remember all widgets which have already been instanciated
|
||||
var instances = [];
|
||||
|
||||
// Copy the some data from the rowData array
|
||||
var rowData = new Array(_obj.rowData.length);
|
||||
for (var y = 0; y < _obj.rowData.length; y++)
|
||||
{
|
||||
rowData[y] = {
|
||||
"disabled": _obj.rowData[y].disabled,
|
||||
"class": _obj.rowData[y]["class"]
|
||||
}
|
||||
}
|
||||
|
||||
// Copy the cells array of the other grid and clone the widgets
|
||||
// inside of it
|
||||
var cells = new Array(_obj.cells.length);
|
||||
@ -508,13 +511,14 @@ var et2_grid = et2_DOMWidget.extend({
|
||||
"td": null,
|
||||
"colSpan": srcCell.colSpan,
|
||||
"rowSpan": srcCell.rowSpan,
|
||||
"disabled": srcCell.disabled
|
||||
"disabled": srcCell.disabled,
|
||||
"class": srcCell["class"]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Create the table
|
||||
this.createTableFromCells(cells);
|
||||
this.createTableFromCells(cells, rowData);
|
||||
|
||||
// Copy a reference to the content array manager
|
||||
if (_obj._mgr)
|
||||
|
@ -29,6 +29,8 @@
|
||||
et2_widget_tabs;
|
||||
et2_widget_hrule;
|
||||
|
||||
et2_extension_nextmatch;
|
||||
|
||||
// Requirements for the etemplate2 object
|
||||
et2_core_xml;
|
||||
et2_core_arrayMgr;
|
||||
@ -67,7 +69,7 @@ etemplate2.prototype.clear = function()
|
||||
if (this.widgetContainer != null)
|
||||
{
|
||||
// $j(':input',this.DOMContainer).validator().data("validator").destroy();
|
||||
this.widgetContainer.destroy();
|
||||
this.widgetContainer.free();
|
||||
this.widgetContainer = null;
|
||||
}
|
||||
}
|
||||
|
75
etemplate/js/test/et2_test_nextmatch.xet
Normal file
@ -0,0 +1,75 @@
|
||||
<?xml version="1.0"?>
|
||||
<overlay>
|
||||
<template id="grid_defn">
|
||||
<grid>
|
||||
<columns>
|
||||
<column width="2%"/>
|
||||
<column/>
|
||||
<column disabled="@no_customfields"/>
|
||||
<column/>
|
||||
<column width="120"/>
|
||||
<column/>
|
||||
<column/>
|
||||
<column width="8%" disabled="@no_info_owner_info_responsible"/>
|
||||
<column width="12%"/>
|
||||
<column width="3%" disabled="@no_actions"/>
|
||||
<column width="3%" disabled="@no_actions"/>
|
||||
</columns>
|
||||
<rows>
|
||||
<row class="th">
|
||||
<vbox options="0,0">
|
||||
<nextmatch-filterheader id="info_type" no_lang="1" options="Type"/>
|
||||
<nextmatch-filterheader align="center" id="info_status" options="Status"/>
|
||||
<nextmatch-sortheader align="right" label="Completed" id="info_percent"/>
|
||||
</vbox>
|
||||
<grid width="100%" spacing="0" padding="0">
|
||||
<columns>
|
||||
<column/>
|
||||
<column/>
|
||||
</columns>
|
||||
<rows>
|
||||
<row>
|
||||
<nextmatch-customfilter id="linked" onchange="1" options="link-entry"/>
|
||||
<nextmatch-sortheader align="right" label="Priority" id="info_priority" options="DESC" class="noPrint"/>
|
||||
</row>
|
||||
<row>
|
||||
<nextmatch-sortheader label="Subject" id="info_subject"/>
|
||||
<nextmatch-sortheader align="right" label="Creation" id="info_id" options="DESC" class="noPrint"/>
|
||||
</row>
|
||||
<row>
|
||||
<nextmatch-sortheader label="Description" id="info_des"/>
|
||||
<nextmatch-sortheader align="right" label="last changed" id="info_datemodified" options="DESC" class="noPrint"/>
|
||||
</row>
|
||||
</rows>
|
||||
</grid>
|
||||
<nextmatch-customfields id="customfields"/>
|
||||
<nextmatch-header label="Category" id="cat_id"/>
|
||||
<vbox options="0,0">
|
||||
<nextmatch-sortheader label="Startdate" id="info_startdate"/>
|
||||
<nextmatch-sortheader label="Enddate" id="info_enddate"/>
|
||||
<nextmatch-sortheader label="Date completed" id="info_datecompleted"/>
|
||||
</vbox>
|
||||
<vbox cols="1" rows="2">
|
||||
<nextmatch-sortheader label="Times" id="info_used_time"/>
|
||||
<nextmatch-sortheader label="planned" id="info_planned_time" class="planned"/>
|
||||
</vbox>
|
||||
<vbox cols="1" rows="2" options="0,0">
|
||||
<nextmatch-sortheader label="Times" id="info_used_time"/>
|
||||
<nextmatch-sortheader label="planned" id="info_planned_time" class="planned"/>
|
||||
<nextmatch-sortheader label="Re-planned" id="info_replanned_time" class="replanned"/>
|
||||
</vbox>
|
||||
<vbox options="0,0">
|
||||
<nextmatch-accountfilter statustext="Select to filter by owner" id="info_owner" options="Owner" class="user_filter"/>
|
||||
<nextmatch-accountfilter statustext="Select to filter by responsible" id="info_responsible" options="Responsible,both" class="user_filter"/>
|
||||
</vbox>
|
||||
<nextmatch-sortheader label="last changed" id="info_datemodified" options="DESC"/>
|
||||
<description value="Sub" class="noPrint"/>
|
||||
<nextmatch-header label="Action" id="actions" class="noPrint"/>
|
||||
</row>
|
||||
</rows>
|
||||
</grid>
|
||||
</template>
|
||||
|
||||
<nextmatch options="grid_defn" />
|
||||
</overlay>
|
||||
|
BIN
etemplate/js/test/gfx/ajax-loader.gif
Normal file
After Width: | Height: | Size: 1.6 KiB |
BIN
etemplate/js/test/gfx/arrows.png
Normal file
After Width: | Height: | Size: 393 B |
BIN
etemplate/js/test/gfx/down.png
Executable file
After Width: | Height: | Size: 194 B |
BIN
etemplate/js/test/gfx/focused_hatching.png
Normal file
After Width: | Height: | Size: 202 B |
BIN
etemplate/js/test/gfx/header_overlay.png
Normal file
After Width: | Height: | Size: 431 B |
BIN
etemplate/js/test/gfx/non_loaded_bg.png
Normal file
After Width: | Height: | Size: 408 B |
BIN
etemplate/js/test/gfx/selectcols.png
Normal file
After Width: | Height: | Size: 215 B |
BIN
etemplate/js/test/gfx/up.png
Executable file
After Width: | Height: | Size: 189 B |
220
etemplate/js/test/grid.css
Normal file
@ -0,0 +1,220 @@
|
||||
|
||||
.egwGridView_grid {
|
||||
table-layout: fixed;
|
||||
border-spacing: 0;
|
||||
border-collapse: collapse;
|
||||
}
|
||||
|
||||
.egwGridView_outer div.innerContainer.queued {
|
||||
background-image: url(gfx/ajax-loader.gif);
|
||||
background-position: center;
|
||||
background-repeat: no-repeat;
|
||||
height: 19px;
|
||||
}
|
||||
|
||||
.egwGridView_grid tr.focused td {
|
||||
background-image: url(gfx/focused_hatching.png);
|
||||
background-repeat: repeat;
|
||||
}
|
||||
|
||||
.egwGridView_grid tr.selected td {
|
||||
background-color: #b7c3ff;
|
||||
}
|
||||
|
||||
.egwGridView_grid tr.draggedOver td {
|
||||
background-color: #ffd09c !important;
|
||||
}
|
||||
|
||||
/*.egwGridView_grid tr.selected.odd td {
|
||||
background-color: #9dadff;
|
||||
}*/
|
||||
|
||||
.egwGridView_scrollarea {
|
||||
width: 100%;
|
||||
overflow: auto;
|
||||
}
|
||||
|
||||
.egwGridView_spacer {
|
||||
background-image: url(gfx/non_loaded_bg.png);
|
||||
background-position: top left;
|
||||
}
|
||||
|
||||
.egwGridView_outer {
|
||||
table-layout: fixed;
|
||||
border-spacing: 0;
|
||||
border-collapse: collapse;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.egwGridView_outer td, .egwGridView_outer tr {
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.egwGridView_grid td {
|
||||
border-right: 1px solid silver;
|
||||
border-bottom: 1px solid #e0e0e0;
|
||||
padding: 2px 3px 2px 4px;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.egwGridView_outer th div.innerContainer,
|
||||
.egwGridView_grid td div.innerContainer {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
display: block;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.egwGridView_grid tr.fullRow {
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
.egwGridView_grid tr.row:hover {
|
||||
background-color: #f0f0ff;
|
||||
}
|
||||
|
||||
.egwGridView_grid tr {
|
||||
padding: 2px 3px 2px 4px;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.egwGridView_grid tr.hidden {
|
||||
display: none;
|
||||
}
|
||||
|
||||
/*.egwGridView_grid tr.odd {
|
||||
background-color: #F1F1F1;
|
||||
}*/
|
||||
|
||||
.egwGridView_grid span.indentation {
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
.egwGridView_grid span {
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
.egwGridView_grid img.icon {
|
||||
vertical-align: middle;
|
||||
margin: 2px 5px 2px 2px;
|
||||
-moz-user-select: none;
|
||||
-khtml-user-select: none;
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
.egwGridView_grid span.arrow {
|
||||
display: inline-block;
|
||||
vertical-align: middle;
|
||||
width: 8px;
|
||||
height: 8px;
|
||||
background-repeat: no-repeat;
|
||||
margin-right: 2px;
|
||||
-moz-user-select: none;
|
||||
-khtml-user-select: none;
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
.egwGridView_grid span.arrow.opened {
|
||||
cursor: pointer;
|
||||
background-image: url(gfx/arrows.png);
|
||||
background-position: -8px 0;
|
||||
}
|
||||
|
||||
.egwGridView_grid span.arrow.closed {
|
||||
cursor: pointer;
|
||||
background-image: url(gfx/arrows.png);
|
||||
background-position: 0 0;
|
||||
}
|
||||
|
||||
.egwGridView_grid span.arrow.loading {
|
||||
cursor: pointer;
|
||||
background-image: url(gfx/ajax-loader.gif);
|
||||
background-position: 0 0;
|
||||
}
|
||||
|
||||
.egwGridView_grid span.iconContainer {
|
||||
display: inline-block;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.egwGridView_grid span.caption {
|
||||
cursor: default;
|
||||
-moz-user-select: none;
|
||||
-khtml-user-select: none;
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
.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(gfx/header_overlay.png);
|
||||
background-position: center;
|
||||
background-repeat: repeat-x;
|
||||
-webkit-user-select: none;
|
||||
cursor: default;
|
||||
}
|
||||
|
||||
.egwGridView_outer thead th:hover {
|
||||
background-color: #F0F0F0;
|
||||
}
|
||||
|
||||
.egwGridView_outer thead th.optcol {
|
||||
padding: 0;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.egwGridView_outer thead th.optcol:active {
|
||||
background-color: #D0D0D0;
|
||||
border-left: 1px solid gray;
|
||||
border-top: 1px solid gray;
|
||||
border-right: 1px solid silver;
|
||||
border-bottom: 1px solid silver;
|
||||
}
|
||||
|
||||
.selectcols {
|
||||
display: inline-block;
|
||||
width: 10px;
|
||||
height: 9px;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
vertical-align: middle;
|
||||
background-image: url(gfx/selectcols.png);
|
||||
background-position: center;
|
||||
background-repeat: no-repeat;
|
||||
}
|
||||
|
||||
.egwGridView_grid td.frame,
|
||||
.egwGridView_outer td.frame,
|
||||
.egwGridView_grid td.egwGridView_spacer {
|
||||
padding: 0 !important;
|
||||
border-right: 0 none silver !important;
|
||||
border-bottom: 0 none silver !important;
|
||||
}
|
||||
|
||||
.egwGridView_outer span.sort {
|
||||
display: inline-block;
|
||||
width: 7px;
|
||||
height: 7px;
|
||||
background-repeat: no-repeat;
|
||||
background-position: center;
|
||||
margin: 2px;
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
.egwGridView_outer span.sort.asc {
|
||||
background-image: url(gfx/up.png);
|
||||
}
|
||||
|
||||
.egwGridView_outer span.sort.desc {
|
||||
background-image: url(gfx/down.png);
|
||||
}
|
||||
|
@ -316,4 +316,35 @@ label input, label span, label div, label select, label textarea {
|
||||
background-image:url(gfx/hint.png);
|
||||
}
|
||||
|
||||
/**
|
||||
* Nextmatch widget
|
||||
*/
|
||||
|
||||
.et2_nextmatch {
|
||||
background-color: silver;
|
||||
}
|
||||
|
||||
.nextmatch_sortheader {
|
||||
color: #003075;
|
||||
cursor: pointer;
|
||||
padding-right: 18px;
|
||||
margin-right: 10px;
|
||||
background-repeat: no-repeat;
|
||||
background-position: right center;
|
||||
}
|
||||
|
||||
.nextmatch_sortheader:hover {
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
.nextmatch_sortheader.asc {
|
||||
font-weight: bold;
|
||||
background-image: url(gfx/up.png);
|
||||
}
|
||||
|
||||
.nextmatch_sortheader.desc {
|
||||
font-weight: bold;
|
||||
background-image: url(gfx/down.png);
|
||||
}
|
||||
|
||||
|
||||
|
@ -5,6 +5,8 @@
|
||||
<script src="jquery.js"></script>
|
||||
<script src="../et2_core_xml.js"></script>
|
||||
<script src="../et2_core_inheritance.js"></script>
|
||||
<script src="../et2_core_interfaces.js"></script>
|
||||
<script src="../et2_core_stylesheet.js"></script>
|
||||
<script src="../et2_core_common.js"></script>
|
||||
<script src="../et2_core_arrayMgr.js"></script>
|
||||
<script src="../et2_core_widget.js"></script>
|
||||
@ -29,6 +31,9 @@
|
||||
<script src="../et2_widget_date.js"></script>
|
||||
<script src="../et2_widget_tabs.js"></script>
|
||||
|
||||
<script src="../et2_extension_nextmatch.js"></script>
|
||||
<script src="../et2_dataview_view_gridcontainer.js"></script>
|
||||
|
||||
<script src="../etemplate2.js"></script>
|
||||
|
||||
<script src="../lib/tooltip.js"></script>
|
||||
@ -42,6 +47,7 @@
|
||||
<script src="et2_test_expressions.json"></script>
|
||||
<script src="et2_test_dates.json"></script>
|
||||
<link rel="StyleSheet" type="text/css" href="./test.css" />
|
||||
<link rel="StyleSheet" type="text/css" href="./grid.css" />
|
||||
|
||||
<style>
|
||||
#linklist {
|
||||
@ -69,6 +75,7 @@
|
||||
<a href="#" onclick="open_xet('et2_test_expressions.xet', expression_test_data);">Expression test</a>
|
||||
<a href="#" onclick="open_xet('et2_test_hbox.xet');">HBox test</a>
|
||||
<a href="#" onclick="open_xet('et2_test_label.xet');">Label test</a>
|
||||
<a href="#" onclick="open_xet('et2_test_nextmatch.xet');">Nextmatch test</a>
|
||||
</div>
|
||||
<div class="header">ETemplate2 container:</div>
|
||||
<div id="container"></div>
|
||||
|