forked from extern/egroupware
433 lines
11 KiB
JavaScript
433 lines
11 KiB
JavaScript
/**
|
|
* 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;
|
|
et2_extension_nextmatch_dynheight;
|
|
et2_dataview_view_gridContainer;
|
|
et2_dataview_model_dataProvider;
|
|
*/
|
|
|
|
/**
|
|
* 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(et2_IResizeable, {
|
|
|
|
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 dynheight component which dynamically scales the inner
|
|
// container.
|
|
this.dynheight = new et2_dynheight(null, this.div, 150);
|
|
|
|
// Create the data provider which cares about streaming the row data
|
|
// efficiently to the rows
|
|
this.dataProvider = new et2_dataview_dataProvider(this, 100);
|
|
|
|
// Create the outer grid container
|
|
this.dataviewContainer = new et2_dataview_gridContainer(this.div,
|
|
this.dataProvider);
|
|
|
|
this.activeFilters = {};
|
|
},
|
|
|
|
/**
|
|
* Destroys all
|
|
*/
|
|
destroy: function() {
|
|
this.dataviewContainer.free();
|
|
this.dataProvider.free();
|
|
this.dynheight.free();
|
|
|
|
this._super.apply(this, arguments);
|
|
},
|
|
|
|
/**
|
|
* Implements the et2_IResizeable interface - lets the dynheight manager
|
|
* update the width and height and then update the dataview container.
|
|
*/
|
|
resize: function() {
|
|
this.dynheight.update(function(_w, _h) {
|
|
this.dataviewContainer.resize(_w, _h);
|
|
}, this);
|
|
},
|
|
|
|
/**
|
|
* Get Rows callback
|
|
*/
|
|
getRows: function(_fetchList, _callback, _context) {
|
|
console.log("Nextmatch will fetch ", _fetchList);
|
|
// Create the entries:
|
|
var entries = {};
|
|
for (var i = 0; i < _fetchList.length; i++)
|
|
{
|
|
var start = _fetchList[i].startIdx;
|
|
for (var j = 0; j < _fetchList[i].count; j++)
|
|
{
|
|
entries[start + j] =
|
|
{"info_id":"5","info_type":"email","info_from":"tracker","info_addr":"tracker","info_subject":"InfoLog grid view: problem with Opera; 'permission denied' while saving","info_des":"<snip>","info_owner":"5","info_responsible":[],"info_access":"public","info_cat":"0","info_datemodified":1307112528,"info_startdate":1306503000,"info_enddate":"0", "info_id_parent":"0","info_planned_time":"0","info_replanned_time":"0","info_used_time":"0","info_status":"done", "info_confirm":"not","info_modifier":"5","info_link_id":0,"info_priority":"1","pl_id":"0","info_price":null, "info_percent":"100%","info_datecompleted":1307112528,"info_location":"","info_custom_from":1, "info_uid":"infolog-5-18d12c7bf195f6b9d602e1fa5cde28f1","info_cc":"","caldav_name":"5.ics","info_etag":"0", "info_created":1307112528,"info_creator":"5","links":[],"info_anz_subs":0,"sub_class":"normal_done","info_link":{"title":"tracker"},"class":"rowNoClose rowNoCloseAll ","info_type_label":"E-Mail","info_status_label":"done","info_number":"5"}
|
|
}
|
|
}
|
|
|
|
_callback.call(_context, entries);
|
|
},
|
|
|
|
/**
|
|
* 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") + "'");
|
|
},
|
|
|
|
/**
|
|
* Removes the sort entry from the active filters object and thus returns to
|
|
* the natural sort order.
|
|
*/
|
|
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);
|
|
|
|
// Delete the "sort" filter entry
|
|
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
|
|
*/
|
|
_genColumnCaption: 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, _colData) {
|
|
// Go over the header row and create the column entries
|
|
this.columns = new Array(_row.length);
|
|
var columnData = new Array(_row.length);
|
|
for (var x = 0; x < _row.length; x++)
|
|
{
|
|
this.columns[x] = {
|
|
"widget": _row[x].widget
|
|
};
|
|
|
|
columnData[x] = {
|
|
"id": "col_" + x,
|
|
"caption": this._genColumnCaption(_row[x].widget),
|
|
"visibility": _colData[x].disabled ?
|
|
ET2_COL_VISIBILITY_INVISIBLE : ET2_COL_VISIBILITY_VISIBLE,
|
|
"width": _colData[x].width
|
|
};
|
|
|
|
// Append the widget to this container
|
|
this.addChild(_row[x].widget);
|
|
}
|
|
|
|
// Create the column manager and update the grid container
|
|
this.dataviewContainer.setColumns(columnData);
|
|
|
|
},
|
|
|
|
_parseDataRow: function(_row, _colData) {
|
|
var columnWidgets = new Array(this.columns.length);
|
|
|
|
for (var x = 0; x < columnWidgets.length; x++)
|
|
{
|
|
if (typeof _row[x] != "undefined" && _row[x].widget)
|
|
{
|
|
columnWidgets[x] = _row[x].widget;
|
|
|
|
// Append the widget to this container
|
|
this.addChild(_row[x].widget);
|
|
}
|
|
else
|
|
{
|
|
columnWidgets[x] = _row[x].widget;
|
|
}
|
|
}
|
|
|
|
this.dataviewContainer.rowProvider.setDataRowTemplate(columnWidgets, this);
|
|
},
|
|
|
|
_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], _grid.colData);
|
|
}
|
|
else
|
|
{
|
|
this._parseDataRow(_grid.cells[y], _grid.colData);
|
|
}
|
|
}
|
|
},
|
|
|
|
/**
|
|
* 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
|
|
*/
|
|
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-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',
|
|
'nextmatch-accountfilter']);
|
|
|