2020-01-31 21:07:27 +01:00
|
|
|
/**
|
|
|
|
* EGroupware eTemplate2 - Class which contains a factory method for rows
|
|
|
|
*
|
|
|
|
* @license http://opensource.org/licenses/gpl-license.php GPL - GNU General Public License
|
|
|
|
* @package etemplate
|
|
|
|
* @subpackage api
|
2021-06-07 17:33:53 +02:00
|
|
|
* @link https://www.egroupware.org
|
2020-01-31 21:07:27 +01:00
|
|
|
* @author Andreas Stöckel
|
2021-06-07 17:33:53 +02:00
|
|
|
* @copyright EGroupware GmbH 2011-2021
|
2020-01-31 21:07:27 +01:00
|
|
|
*/
|
|
|
|
|
|
|
|
/*egw:uses
|
|
|
|
/vendor/bower-asset/jquery/dist/jquery.js;
|
|
|
|
et2_core_inheritance;
|
|
|
|
et2_core_interfaces;
|
|
|
|
et2_core_arrayMgr;
|
|
|
|
et2_core_widget;
|
|
|
|
et2_dataview_view_rowProvider;
|
|
|
|
*/
|
|
|
|
|
|
|
|
import {et2_widget} from "./et2_core_widget";
|
|
|
|
import {et2_arrayMgrs_expand} from "./et2_core_arrayMgr";
|
2021-06-07 17:33:53 +02:00
|
|
|
import {et2_dataview_grid} from "./et2_dataview_view_grid";
|
|
|
|
import {egw} from "../jsapi/egw_global";
|
|
|
|
import {et2_IDetachedDOM, et2_IDOMNode} from "./et2_core_interfaces";
|
2020-01-31 21:07:27 +01:00
|
|
|
|
|
|
|
/**
|
|
|
|
* The row provider contains prototypes (full clonable dom-trees)
|
|
|
|
* for all registered row types.
|
|
|
|
*
|
|
|
|
*/
|
|
|
|
export class et2_nextmatch_rowProvider
|
|
|
|
{
|
2021-12-15 00:55:02 +01:00
|
|
|
private _rowProvider : any;
|
|
|
|
private _subgridCallback : any;
|
|
|
|
private _context : any;
|
|
|
|
private _rootWidget : any;
|
|
|
|
private _template : any;
|
|
|
|
private _dataRow : any;
|
|
|
|
|
2020-01-31 21:07:27 +01:00
|
|
|
/**
|
|
|
|
* Creates the nextmatch row provider.
|
|
|
|
*
|
|
|
|
* @param {et2_nextmatch_rowProvider} _rowProvider
|
|
|
|
* @param {function} _subgridCallback
|
|
|
|
* @param {object} _context
|
|
|
|
* @memberOf et2_nextmatch_rowProvider
|
|
|
|
*/
|
2021-12-15 00:55:02 +01:00
|
|
|
constructor(_rowProvider, _subgridCallback, _context)
|
2020-01-31 21:07:27 +01:00
|
|
|
{
|
2021-12-15 00:55:02 +01:00
|
|
|
|
2020-01-31 21:07:27 +01:00
|
|
|
|
|
|
|
// Copy the arguments
|
|
|
|
this._rowProvider = _rowProvider;
|
|
|
|
this._subgridCallback = _subgridCallback;
|
|
|
|
this._context = _context;
|
|
|
|
|
|
|
|
this._createEmptyPrototype();
|
|
|
|
}
|
|
|
|
|
2020-02-12 22:49:22 +01:00
|
|
|
destroy()
|
|
|
|
{
|
|
|
|
this._rowProvider.destroy();
|
|
|
|
this._subgridCallback = null;
|
|
|
|
this._context = null;
|
|
|
|
this._dataRow = null;
|
|
|
|
}
|
|
|
|
|
2020-01-31 21:07:27 +01:00
|
|
|
/**
|
|
|
|
* Creates the data row prototype.
|
|
|
|
*
|
|
|
|
* @param _widgets is an array containing the root widget for each column.
|
|
|
|
* @param _rowData contains the properties of the root "tr" (like its class)
|
|
|
|
* @param _rootWidget is the parent widget of the data rows (i.e.
|
|
|
|
* the nextmatch)
|
|
|
|
*/
|
2021-12-15 00:55:02 +01:00
|
|
|
setDataRowTemplate(_widgets, _rowData, _rootWidget)
|
2020-01-31 21:07:27 +01:00
|
|
|
{
|
|
|
|
// Copy the root widget
|
|
|
|
this._rootWidget = _rootWidget;
|
|
|
|
|
|
|
|
// Create the base row
|
|
|
|
var row = this._rowProvider.getPrototype("default");
|
|
|
|
|
|
|
|
// Copy the row template
|
|
|
|
var rowTemplate = {
|
|
|
|
"row": row[0],
|
|
|
|
"rowData": _rowData,
|
|
|
|
"widgets": _widgets,
|
|
|
|
"root": _rootWidget,
|
|
|
|
"seperated": null,
|
|
|
|
"mgrs": _rootWidget.getArrayMgrs()
|
|
|
|
};
|
|
|
|
|
|
|
|
// Create the row widget and insert the given widgets into the row
|
|
|
|
var rowWidget = new et2_nextmatch_rowWidget(rowTemplate.mgrs, row[0]);
|
|
|
|
rowWidget._parent = _rootWidget;
|
|
|
|
rowWidget.createWidgets(_widgets);
|
|
|
|
|
|
|
|
// Get the set containing all variable attributes
|
|
|
|
var variableAttributes = this._getVariableAttributeSet(rowWidget);
|
|
|
|
|
|
|
|
// Filter out all widgets which do not implement the et2_IDetachedDOM
|
|
|
|
// interface or do not support all attributes listed in the et2_IDetachedDOM
|
|
|
|
// interface. A warning is issued for all those widgets as they heavily
|
|
|
|
// degrade the performance of the dataview
|
|
|
|
var seperated = rowTemplate.seperated =
|
|
|
|
this._seperateWidgets(variableAttributes);
|
|
|
|
|
|
|
|
// Remove all DOM-Nodes of all widgets inside the "remaining" slot from
|
|
|
|
// the row-template, then build the access functions for the detachable
|
|
|
|
// widgets
|
|
|
|
this._stripTemplateRow(rowTemplate);
|
|
|
|
this._buildNodeAccessFuncs(rowTemplate);
|
|
|
|
|
|
|
|
// Create the DOM row template
|
|
|
|
var tmpl = document.createDocumentFragment();
|
|
|
|
row.children().each(function() { tmpl.appendChild(this); });
|
|
|
|
|
|
|
|
this._dataRow = tmpl;
|
|
|
|
this._template = rowTemplate;
|
|
|
|
}
|
|
|
|
|
2021-12-15 00:55:02 +01:00
|
|
|
getDataRow(_data : any, _row, _idx, _controller)
|
2020-01-31 21:07:27 +01:00
|
|
|
{
|
|
|
|
|
|
|
|
// Clone the row template
|
|
|
|
var row = this._dataRow.cloneNode(true);
|
|
|
|
|
|
|
|
// Create array managers with the given data merged in
|
|
|
|
var mgrs = et2_arrayMgrs_expand(rowWidget, this._template.mgrs,
|
|
|
|
_data, _idx);
|
|
|
|
|
|
|
|
// Insert the widgets into the row which do not provide the functions
|
|
|
|
// to set the _data directly
|
|
|
|
var rowWidget : et2_nextmatch_rowTemplateWidget = null;
|
2021-12-15 00:55:02 +01:00
|
|
|
if(this._template.seperated.remaining.length > 0)
|
2020-01-31 21:07:27 +01:00
|
|
|
{
|
|
|
|
// Transform the variable attributes
|
2021-12-15 00:55:02 +01:00
|
|
|
for(var i = 0; i < this._template.seperated.remaining.length; i++)
|
2020-01-31 21:07:27 +01:00
|
|
|
{
|
|
|
|
var entry = this._template.seperated.remaining[i];
|
|
|
|
|
2021-12-15 00:55:02 +01:00
|
|
|
for(var j = 0; j < entry.data.length; j++)
|
2020-01-31 21:07:27 +01:00
|
|
|
{
|
|
|
|
var set = entry.data[j];
|
2021-12-15 00:55:02 +01:00
|
|
|
if(typeof entry.widget.options != "undefined")
|
|
|
|
{
|
|
|
|
entry.widget.options[set.attribute] = mgrs["content"].expandName(set.expression);
|
|
|
|
}
|
|
|
|
else if(entry.widget.getAttributeNames().indexOf(set.attribute) >= 0)
|
|
|
|
{
|
|
|
|
entry.widget.setAttribute(set.attribute, mgrs["content"].expandName(set.expression));
|
|
|
|
}
|
2020-01-31 21:07:27 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Create the row widget
|
|
|
|
var rowWidget = new et2_nextmatch_rowTemplateWidget(this._rootWidget,
|
|
|
|
row);
|
|
|
|
|
|
|
|
// Let the row widget create the widgets
|
|
|
|
rowWidget.createWidgets(mgrs, this._template.placeholders);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Update the content of all other widgets
|
2021-12-15 00:55:02 +01:00
|
|
|
for(var i = 0; i < this._template.seperated.detachable.length; i++)
|
2020-01-31 21:07:27 +01:00
|
|
|
{
|
|
|
|
var entry = this._template.seperated.detachable[i];
|
2021-12-15 00:55:02 +01:00
|
|
|
let widget = entry.widget;
|
2022-09-06 17:17:12 +02:00
|
|
|
const widget_class = window.customElements.get(widget.localName);
|
2020-01-31 21:07:27 +01:00
|
|
|
|
|
|
|
// Parse the attribute expressions
|
2022-08-17 21:23:55 +02:00
|
|
|
let data : any = {};
|
|
|
|
let attributes = entry.data.map(attr => attr.attribute);
|
2021-12-15 00:55:02 +01:00
|
|
|
for(var j = 0; j < entry.data.length; j++)
|
2020-01-31 21:07:27 +01:00
|
|
|
{
|
|
|
|
var set = entry.data[j];
|
|
|
|
data[set.attribute] = mgrs["content"].expandName(set.expression);
|
|
|
|
}
|
2021-12-15 00:55:02 +01:00
|
|
|
// WebComponent IS the node, and we've already cloned it
|
2022-09-06 17:17:12 +02:00
|
|
|
if(typeof widget_class != "undefined")
|
2020-01-31 21:07:27 +01:00
|
|
|
{
|
2022-03-09 22:23:00 +01:00
|
|
|
widget = this._cloneWebComponent(entry, row, data);
|
2022-05-05 01:27:58 +02:00
|
|
|
if(!widget)
|
|
|
|
{
|
|
|
|
console.warn("Error cloning ", entry);
|
|
|
|
continue;
|
|
|
|
}
|
2021-12-15 00:55:02 +01:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
// Retrieve all DOM-Nodes (legacy widgets)
|
|
|
|
var nodes = new Array(entry.nodeFuncs.length);
|
|
|
|
for(var j = 0; j < nodes.length; j++)
|
2020-04-14 23:03:32 +02:00
|
|
|
{
|
2021-12-15 00:55:02 +01:00
|
|
|
// Use the previously compiled node function to get the node
|
|
|
|
// from the entry
|
|
|
|
try
|
|
|
|
{
|
|
|
|
nodes[j] = entry.nodeFuncs[j](row);
|
|
|
|
}
|
|
|
|
catch(e)
|
|
|
|
{
|
|
|
|
debugger;
|
|
|
|
continue;
|
|
|
|
}
|
2020-04-14 23:03:32 +02:00
|
|
|
}
|
2020-01-31 21:07:27 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
// Set the array managers first
|
2021-12-15 00:55:02 +01:00
|
|
|
widget.setArrayMgrs(mgrs);
|
|
|
|
if(typeof data.id != "undefined")
|
2020-01-31 21:07:27 +01:00
|
|
|
{
|
2021-12-15 00:55:02 +01:00
|
|
|
widget.id = data.id;
|
2020-01-31 21:07:27 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
// Adjust data for that row
|
2021-12-15 00:55:02 +01:00
|
|
|
widget.transformAttributes?.call(widget, data);
|
2020-01-31 21:07:27 +01:00
|
|
|
|
2022-09-06 17:17:12 +02:00
|
|
|
// Translate
|
|
|
|
if(widget_class)
|
|
|
|
{
|
|
|
|
Object.keys(data).forEach((key) =>
|
|
|
|
{
|
|
|
|
if(widget_class.translate[key])
|
|
|
|
{
|
|
|
|
data[key] = widget.egw().lang(data[key]);
|
|
|
|
}
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2022-08-17 21:23:55 +02:00
|
|
|
// Make sure to only send detached attributes, filter out any deferredProperties
|
|
|
|
let filtered = widget.deferredProperties ? Object.keys(data)
|
|
|
|
.filter(key => attributes.includes(key))
|
|
|
|
.reduce((obj, key) =>
|
|
|
|
{
|
|
|
|
obj[key] = data[key];
|
|
|
|
return obj
|
|
|
|
}, {}) : data;
|
|
|
|
|
2020-01-31 21:07:27 +01:00
|
|
|
// Call the setDetachedAttributes function
|
2022-08-17 21:23:55 +02:00
|
|
|
widget.setDetachedAttributes(nodes, filtered, _data);
|
2020-01-31 21:07:27 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
// Insert the row into the tr
|
|
|
|
var tr = _row.getDOMNode();
|
|
|
|
tr.appendChild(row);
|
|
|
|
|
|
|
|
// Make the row expandable
|
2021-12-15 00:55:02 +01:00
|
|
|
if(typeof _data.content["is_parent"] !== "undefined"
|
|
|
|
&& _data.content["is_parent"])
|
2020-01-31 21:07:27 +01:00
|
|
|
{
|
2021-12-15 00:55:02 +01:00
|
|
|
_row.makeExpandable(true, function()
|
|
|
|
{
|
2020-01-31 21:07:27 +01:00
|
|
|
return this._subgridCallback.call(this._context,
|
2021-12-15 00:55:02 +01:00
|
|
|
_row, _data, _controller);
|
2020-01-31 21:07:27 +01:00
|
|
|
}, this);
|
|
|
|
|
|
|
|
// Check for kept expansion, and set the row up to be re-expanded
|
|
|
|
// Only the top controller tracks expanded, including sub-grids
|
|
|
|
var top_controller = _controller;
|
|
|
|
while(top_controller._parentController != null)
|
|
|
|
{
|
|
|
|
top_controller = top_controller._parentController;
|
|
|
|
}
|
|
|
|
var expansion_index = top_controller.kept_expansion.indexOf(
|
|
|
|
top_controller.dataStorePrefix + '::' + _data.content[this._context.settings.row_id]
|
|
|
|
);
|
2021-12-15 00:55:02 +01:00
|
|
|
if(top_controller.kept_expansion && expansion_index >= 0)
|
2020-01-31 21:07:27 +01:00
|
|
|
{
|
2021-12-15 00:55:02 +01:00
|
|
|
top_controller.kept_expansion.splice(expansion_index, 1);
|
2020-01-31 21:07:27 +01:00
|
|
|
// Use a timeout since the DOM nodes might not be finished yet
|
2021-12-15 00:55:02 +01:00
|
|
|
window.setTimeout(function()
|
|
|
|
{
|
2020-01-31 21:07:27 +01:00
|
|
|
_row.expansionButton.trigger('click');
|
2021-12-15 00:55:02 +01:00
|
|
|
}, et2_dataview_grid.ET2_GRID_INVALIDATE_TIMEOUT);
|
2020-01-31 21:07:27 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Set the row data
|
|
|
|
this._setRowData(this._template.rowData, tr, mgrs);
|
|
|
|
|
|
|
|
return rowWidget;
|
|
|
|
}
|
|
|
|
|
2022-03-09 22:23:00 +01:00
|
|
|
/**
|
|
|
|
* Get the cloned web component
|
|
|
|
*/
|
|
|
|
_cloneWebComponent(entry, row, data)
|
|
|
|
{
|
2022-07-28 17:56:59 +02:00
|
|
|
// Widget was already cloned from original row, just get it
|
2022-03-09 22:23:00 +01:00
|
|
|
let widget = entry.nodeFuncs[0](row);
|
2022-07-28 17:56:59 +02:00
|
|
|
// Original widget
|
|
|
|
let original = entry.nodeFuncs[0](this._dataRow);
|
|
|
|
|
|
|
|
// N.B. cloneNode widget is missing its unreflected properties and we need to get them from original
|
|
|
|
let widget_class = window.customElements.get(widget.localName);
|
2024-01-19 20:42:25 +01:00
|
|
|
let properties = widget_class ? widget_class.elementProperties : [];
|
|
|
|
properties.forEach((v, key) =>
|
2022-07-28 17:56:59 +02:00
|
|
|
{
|
|
|
|
widget[key] = original[key];
|
2024-01-19 20:42:25 +01:00
|
|
|
});
|
2022-03-09 22:23:00 +01:00
|
|
|
|
2022-05-05 01:27:58 +02:00
|
|
|
if(!widget || widget.localName !== entry.widget.localName)
|
|
|
|
{
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
|
2022-04-11 19:21:39 +02:00
|
|
|
// Need to set the parent to the nm or egw() (and probably others) will not be as expected, using window instead
|
|
|
|
// of app. arrayMgrs are fine without this though
|
|
|
|
widget._parent = this._context;
|
|
|
|
|
2022-03-09 22:23:00 +01:00
|
|
|
// Deal with the deferred properties like booleans with string values - we can't reflect them, and we don't want to lose them
|
|
|
|
// No need to transform here, that happens later
|
|
|
|
Object.assign(data, entry.widget.deferredProperties);
|
|
|
|
|
|
|
|
return widget;
|
|
|
|
}
|
2020-01-31 21:07:27 +01:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Placeholder for empty row
|
|
|
|
*
|
|
|
|
* The empty row placeholder is used when there are no results to display.
|
|
|
|
* This allows the user to still have a drop target, or use actions that
|
|
|
|
* do not require a row ID, such as 'Add new'.
|
|
|
|
*/
|
2021-12-15 00:55:02 +01:00
|
|
|
_createEmptyPrototype()
|
2020-01-31 21:07:27 +01:00
|
|
|
{
|
|
|
|
var label = this._context && this._context.options && this._context.options.settings.placeholder;
|
|
|
|
|
|
|
|
var placeholder = jQuery(document.createElement("td"))
|
2021-12-15 00:55:02 +01:00
|
|
|
.attr("colspan", this._rowProvider.getColumnCount())
|
|
|
|
.css("height", "19px")
|
|
|
|
.text(typeof label != "undefined" && label ? label : egw().lang("No matches found"));
|
2020-01-31 21:07:27 +01:00
|
|
|
this._rowProvider._prototypes["empty"] = jQuery(document.createElement("tr"))
|
|
|
|
.addClass("egwGridView_empty")
|
|
|
|
.append(placeholder);
|
|
|
|
}
|
|
|
|
|
|
|
|
/** -- PRIVATE FUNCTIONS -- **/
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Returns an array containing objects which have variable attributes
|
|
|
|
*
|
|
|
|
* @param {et2_widget} _widget
|
|
|
|
*/
|
2021-12-15 00:55:02 +01:00
|
|
|
_getVariableAttributeSet(_widget)
|
2020-01-31 21:07:27 +01:00
|
|
|
{
|
2020-03-11 17:56:51 +01:00
|
|
|
let variableAttributes = [];
|
2020-01-31 21:07:27 +01:00
|
|
|
|
2021-12-15 00:55:02 +01:00
|
|
|
const process = function(_widget)
|
2020-03-11 17:56:51 +01:00
|
|
|
{
|
2020-01-31 21:07:27 +01:00
|
|
|
// Create the attribtues
|
|
|
|
var hasAttr = false;
|
|
|
|
var widgetData = {
|
|
|
|
"widget": _widget,
|
|
|
|
"data": []
|
|
|
|
};
|
|
|
|
|
|
|
|
// Get all attribute values
|
2021-12-15 00:55:02 +01:00
|
|
|
let attrs = [];
|
|
|
|
if(_widget.getDetachedAttributes)
|
|
|
|
{
|
|
|
|
_widget.getDetachedAttributes(attrs);
|
2021-12-16 19:27:28 +01:00
|
|
|
// Manually add in ID for consideration, value won't get pulled without it
|
|
|
|
attrs.push("id");
|
2021-12-15 00:55:02 +01:00
|
|
|
}
|
|
|
|
for(let key of attrs)
|
2020-01-31 21:07:27 +01:00
|
|
|
{
|
2021-09-03 22:45:45 +02:00
|
|
|
let attr_name = key;
|
2021-12-15 00:55:02 +01:00
|
|
|
let val = _widget[key];
|
2021-09-03 22:45:45 +02:00
|
|
|
if(typeof val == "string" && val.indexOf("$") >= 0)
|
|
|
|
{
|
|
|
|
hasAttr = true;
|
|
|
|
widgetData.data.push({
|
|
|
|
"attribute": attr_name,
|
|
|
|
"expression": val
|
|
|
|
});
|
2020-01-31 21:07:27 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-12-15 00:55:02 +01:00
|
|
|
// Legacy
|
|
|
|
if(_widget.instanceOf(et2_widget))
|
|
|
|
{
|
|
|
|
for(const key in _widget.attributes)
|
|
|
|
{
|
|
|
|
if(typeof _widget.attributes[key] !== "object")
|
|
|
|
{
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
let attr_name = key;
|
|
|
|
let val;
|
|
|
|
if(!_widget.attributes[key].ignore &&
|
|
|
|
typeof _widget.options != "undefined" &&
|
|
|
|
typeof _widget.options[key] != "undefined")
|
|
|
|
{
|
|
|
|
val = _widget.options[key];
|
|
|
|
}
|
|
|
|
// TODO: Improve detection
|
|
|
|
if(typeof val == "string" && val.indexOf("$") >= 0)
|
|
|
|
{
|
|
|
|
hasAttr = true;
|
|
|
|
widgetData.data.push({
|
|
|
|
"attribute": attr_name,
|
|
|
|
"expression": val
|
|
|
|
});
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2023-04-14 17:35:06 +02:00
|
|
|
else
|
|
|
|
{
|
|
|
|
// Need to include all webComponents or they lose their event handlers in the clone
|
|
|
|
hasAttr = true;
|
|
|
|
}
|
2021-12-15 00:55:02 +01:00
|
|
|
|
2020-01-31 21:07:27 +01:00
|
|
|
// Add the entry if there is any data in it
|
2020-03-11 17:56:51 +01:00
|
|
|
if(hasAttr)
|
2020-01-31 21:07:27 +01:00
|
|
|
{
|
|
|
|
variableAttributes.push(widgetData);
|
|
|
|
}
|
2020-03-11 17:56:51 +01:00
|
|
|
};
|
2020-01-31 21:07:27 +01:00
|
|
|
|
2020-03-11 17:56:51 +01:00
|
|
|
// Check each column
|
2020-03-17 17:24:24 +01:00
|
|
|
const columns = _widget._widgets;
|
2021-12-15 00:55:02 +01:00
|
|
|
for(var i = 0; i < columns.length; i++)
|
2020-03-11 17:56:51 +01:00
|
|
|
{
|
|
|
|
// If column is hidden, don't process it
|
2020-03-17 17:24:24 +01:00
|
|
|
if(typeof columns[i] === 'undefined' || this._context && this._context.columns && this._context.columns[i] && !this._context.columns[i].visible)
|
2020-03-11 17:56:51 +01:00
|
|
|
{
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
columns[i].iterateOver(process, this);
|
|
|
|
}
|
2020-01-31 21:07:27 +01:00
|
|
|
|
|
|
|
return variableAttributes;
|
|
|
|
}
|
|
|
|
|
2021-12-15 00:55:02 +01:00
|
|
|
_seperateWidgets(_varAttrs)
|
2020-01-31 21:07:27 +01:00
|
|
|
{
|
|
|
|
// The detachable array contains all widgets which implement the
|
|
|
|
// et2_IDetachedDOM interface for all needed attributes
|
|
|
|
var detachable = [];
|
|
|
|
|
|
|
|
// The remaining array creates all widgets which have to be completely
|
|
|
|
// cloned when the widget tree is created
|
|
|
|
var remaining = [];
|
|
|
|
|
|
|
|
// Iterate over the widgets
|
2021-12-15 00:55:02 +01:00
|
|
|
for(var i = 0; i < _varAttrs.length; i++)
|
2020-01-31 21:07:27 +01:00
|
|
|
{
|
|
|
|
var widget = _varAttrs[i].widget;
|
|
|
|
|
|
|
|
// Check whether the widget parents are not allready in the "remaining"
|
|
|
|
// slot - if this is the case do not include the widget at all.
|
|
|
|
var insertWidget = true;
|
2021-12-15 00:55:02 +01:00
|
|
|
var checkWidget = function(_widget)
|
|
|
|
{
|
|
|
|
if(_widget.parent != null)
|
2020-01-31 21:07:27 +01:00
|
|
|
{
|
2021-12-15 00:55:02 +01:00
|
|
|
for(var i = 0; i < remaining.length; i++)
|
2020-01-31 21:07:27 +01:00
|
|
|
{
|
2021-12-15 00:55:02 +01:00
|
|
|
if(remaining[i].widget == _widget.parent)
|
2020-01-31 21:07:27 +01:00
|
|
|
{
|
|
|
|
insertWidget = false;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
checkWidget(_widget.parent);
|
|
|
|
}
|
|
|
|
};
|
|
|
|
checkWidget(widget);
|
|
|
|
|
|
|
|
// Handle the next widget if this one should not be included.
|
2021-12-15 00:55:02 +01:00
|
|
|
if(!insertWidget)
|
2020-01-31 21:07:27 +01:00
|
|
|
{
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Check whether the widget implements the et2_IDetachedDOM interface
|
|
|
|
var isDetachable = false;
|
2021-12-15 00:55:02 +01:00
|
|
|
if(widget.implements && widget.implements(et2_IDetachedDOM))
|
2020-01-31 21:07:27 +01:00
|
|
|
{
|
|
|
|
// Get all attributes the widgets supports to be set in the
|
|
|
|
// "detached" mode
|
|
|
|
var supportedAttrs = [];
|
2021-12-15 00:55:02 +01:00
|
|
|
if(widget.getDetachedAttributes)
|
|
|
|
{
|
|
|
|
widget.getDetachedAttributes(supportedAttrs);
|
|
|
|
}
|
2020-01-31 21:07:27 +01:00
|
|
|
supportedAttrs.push("id");
|
|
|
|
isDetachable = true;
|
|
|
|
|
2021-12-15 00:55:02 +01:00
|
|
|
for(var j = 0; j < _varAttrs[i].data.length/* && isDetachable*/; j++)
|
2020-01-31 21:07:27 +01:00
|
|
|
{
|
|
|
|
var data = _varAttrs[i].data[j];
|
|
|
|
|
|
|
|
var supportsAttr = supportedAttrs.indexOf(data.attribute) != -1;
|
|
|
|
|
2021-12-15 00:55:02 +01:00
|
|
|
if(!supportsAttr)
|
2020-01-31 21:07:27 +01:00
|
|
|
{
|
|
|
|
egw.debug("warn", "et2_IDetachedDOM widget " +
|
|
|
|
widget._type + " does not support " + data.attribute);
|
|
|
|
}
|
|
|
|
|
|
|
|
isDetachable = isDetachable && supportsAttr;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Insert the widget into the correct slot
|
2021-12-15 00:55:02 +01:00
|
|
|
if(isDetachable)
|
2020-01-31 21:07:27 +01:00
|
|
|
{
|
|
|
|
detachable.push(_varAttrs[i]);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
remaining.push(_varAttrs[i]);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return {
|
|
|
|
"detachable": detachable,
|
|
|
|
"remaining": remaining
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Removes to DOM code for all widgets in the "remaining" slot
|
|
|
|
*
|
|
|
|
* @param {object} _rowTemplate
|
|
|
|
*/
|
2021-12-15 00:55:02 +01:00
|
|
|
_stripTemplateRow(_rowTemplate)
|
2020-01-31 21:07:27 +01:00
|
|
|
{
|
|
|
|
_rowTemplate.placeholders = [];
|
|
|
|
|
2021-12-15 00:55:02 +01:00
|
|
|
for(var i = 0; i < _rowTemplate.seperated.remaining.length; i++)
|
2020-01-31 21:07:27 +01:00
|
|
|
{
|
|
|
|
var entry = _rowTemplate.seperated.remaining[i];
|
|
|
|
|
|
|
|
// Issue a warning - widgets which do not implement et2_IDOMNode
|
|
|
|
// are very slow
|
2021-12-15 00:55:02 +01:00
|
|
|
egw.debug("warn", "Non-clonable widget '" + entry.widget._type + "' in dataview row - this " +
|
2020-01-31 21:07:27 +01:00
|
|
|
"might be slow", entry);
|
|
|
|
|
|
|
|
// Set the placeholder for the entry to null
|
|
|
|
entry.placeholder = null;
|
|
|
|
|
|
|
|
// Get the outer DOM-Node of the widget
|
2021-12-15 00:55:02 +01:00
|
|
|
if(entry.widget.implements(et2_IDOMNode))
|
2020-01-31 21:07:27 +01:00
|
|
|
{
|
|
|
|
var node = entry.widget.getDOMNode(entry.widget);
|
|
|
|
|
2021-12-15 00:55:02 +01:00
|
|
|
if(node && node.parentNode)
|
2020-01-31 21:07:27 +01:00
|
|
|
{
|
|
|
|
// Get the parent node and replace the node with a placeholder
|
|
|
|
entry.placeholder = document.createElement("span");
|
|
|
|
node.parentNode.replaceChild(entry.placeholder, node);
|
|
|
|
_rowTemplate.placeholders.push({
|
|
|
|
"widget": entry.widget,
|
|
|
|
"func": this._compileDOMAccessFunc(_rowTemplate.row,
|
|
|
|
entry.placeholder)
|
|
|
|
});
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-12-15 00:55:02 +01:00
|
|
|
_nodeIndex(_node)
|
2020-01-31 21:07:27 +01:00
|
|
|
{
|
|
|
|
if(_node.parentNode == null)
|
|
|
|
{
|
|
|
|
return 0;
|
|
|
|
}
|
2021-12-15 00:55:02 +01:00
|
|
|
for(var i = 0; i < _node.parentNode.childNodes.length; i++)
|
2020-01-31 21:07:27 +01:00
|
|
|
{
|
2021-12-15 00:55:02 +01:00
|
|
|
if(_node.parentNode.childNodes[i] == _node)
|
2020-01-31 21:07:27 +01:00
|
|
|
{
|
|
|
|
return i;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Returns a function which does a relative access on the given DOM-Node
|
|
|
|
*
|
|
|
|
* @param {DOMElement} _root
|
|
|
|
* @param {DOMElement} _target
|
|
|
|
*/
|
2021-12-15 00:55:02 +01:00
|
|
|
_compileDOMAccessFunc(_root, _target)
|
2020-01-31 21:07:27 +01:00
|
|
|
{
|
|
|
|
function recordPath(_root, _target, _path)
|
|
|
|
{
|
2021-12-15 00:55:02 +01:00
|
|
|
if(typeof _path == "undefined")
|
2020-01-31 21:07:27 +01:00
|
|
|
{
|
|
|
|
_path = [];
|
|
|
|
}
|
|
|
|
|
2021-12-15 00:55:02 +01:00
|
|
|
if(_root != _target && _target)
|
2020-01-31 21:07:27 +01:00
|
|
|
{
|
|
|
|
// Get the index of _target in its parent node
|
|
|
|
var idx = this._nodeIndex(_target);
|
2021-12-15 00:55:02 +01:00
|
|
|
if(idx >= 0)
|
2020-01-31 21:07:27 +01:00
|
|
|
{
|
|
|
|
// Add the access selector
|
|
|
|
_path.unshift("childNodes[" + idx + "]");
|
|
|
|
|
|
|
|
// Record the remaining path
|
|
|
|
return recordPath.call(this, _root, _target.parentNode, _path);
|
|
|
|
}
|
|
|
|
|
|
|
|
throw("Internal error while compiling DOM access function.");
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
_path.unshift("_node");
|
|
|
|
return "return " + _path.join(".") + ";";
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return new Function("_node", recordPath.call(this, _root, _target));
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Builds relative paths to the DOM-Nodes and compiles fast-access functions
|
|
|
|
*
|
|
|
|
* @param {object} _rowTemplate
|
|
|
|
*/
|
2021-12-15 00:55:02 +01:00
|
|
|
_buildNodeAccessFuncs(_rowTemplate)
|
2020-01-31 21:07:27 +01:00
|
|
|
{
|
2021-12-15 00:55:02 +01:00
|
|
|
for(var i = 0; i < _rowTemplate.seperated.detachable.length; i++)
|
2020-01-31 21:07:27 +01:00
|
|
|
{
|
|
|
|
var entry = _rowTemplate.seperated.detachable[i];
|
|
|
|
|
|
|
|
// Get all needed nodes from the widget
|
2021-12-15 00:55:02 +01:00
|
|
|
var nodes = window.customElements.get(entry.widget.localName) ? [entry.widget] : entry.widget.getDetachedNodes();
|
2020-01-31 21:07:27 +01:00
|
|
|
var nodeFuncs = entry.nodeFuncs = new Array(nodes.length);
|
|
|
|
|
|
|
|
// Record the path to each DOM-Node
|
2021-12-15 00:55:02 +01:00
|
|
|
for(var j = 0; j < nodes.length; j++)
|
2020-01-31 21:07:27 +01:00
|
|
|
{
|
|
|
|
nodeFuncs[j] = this._compileDOMAccessFunc(_rowTemplate.row,
|
|
|
|
nodes[j]);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Match category-ids from class attribute eg. "cat_15" or "123,456,789 "
|
|
|
|
*
|
|
|
|
* Make sure to not match numbers inside other class-names.
|
|
|
|
*
|
|
|
|
* We can NOT use something like /(^| |,|cat_)([0-9]+)( |,|$)/g as it wont find all cats in "123,456,789 "!
|
|
|
|
*/
|
2021-12-15 00:55:02 +01:00
|
|
|
cat_regexp : RegExp = /(^| |,|cat_)([0-9]+)/g;
|
2020-01-31 21:07:27 +01:00
|
|
|
/**
|
|
|
|
* Regular expression used to filter out non-nummerical chars from above matches
|
|
|
|
*/
|
2021-12-15 00:55:02 +01:00
|
|
|
cat_cleanup : RegExp = /[^0-9]/g;
|
2020-01-31 21:07:27 +01:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Applies additional row data (like the class) to the tr
|
|
|
|
*
|
|
|
|
* @param {object} _data
|
|
|
|
* @param {DOMElement} _tr
|
|
|
|
* @param {object} _mgrs
|
|
|
|
*/
|
2021-12-15 00:55:02 +01:00
|
|
|
_setRowData(_data, _tr, _mgrs)
|
2020-01-31 21:07:27 +01:00
|
|
|
{
|
|
|
|
// TODO: Implement other fields than "class"
|
2021-12-15 00:55:02 +01:00
|
|
|
if(_data["class"])
|
2020-01-31 21:07:27 +01:00
|
|
|
{
|
|
|
|
var classes = _mgrs["content"].expandName(_data["class"]);
|
|
|
|
|
|
|
|
// Get fancy with categories
|
|
|
|
var cats = [];
|
|
|
|
// Assume any numeric class is a category
|
|
|
|
if(_data["class"].indexOf("cat") !== -1 || classes.match(/[0-9]+/))
|
|
|
|
{
|
|
|
|
// Accept either cat, cat_id or category as ID, and look there for category settings
|
|
|
|
var category_location = _data["class"].match(/(cat(_id|egory)?)/);
|
2021-12-15 00:55:02 +01:00
|
|
|
if(category_location)
|
|
|
|
{
|
|
|
|
category_location = category_location[0];
|
|
|
|
}
|
2020-01-31 21:07:27 +01:00
|
|
|
|
|
|
|
cats = classes.match(this.cat_regexp) || [];
|
|
|
|
classes = classes.replace(this.cat_regexp, '');
|
|
|
|
|
|
|
|
// Set category class
|
|
|
|
for(var i = 0; i < cats.length; i++)
|
|
|
|
{
|
|
|
|
// Need cat_, classes can't start with a number
|
|
|
|
var cat_id = cats[i].replace(this.cat_cleanup, '');
|
2021-12-15 00:55:02 +01:00
|
|
|
var cat_class = 'cat_' + cat_id;
|
2020-01-31 21:07:27 +01:00
|
|
|
|
2021-12-15 00:55:02 +01:00
|
|
|
classes += ' ' + cat_class;
|
2020-01-31 21:07:27 +01:00
|
|
|
}
|
|
|
|
classes += " row_category";
|
|
|
|
}
|
|
|
|
classes += " row";
|
|
|
|
_tr.setAttribute("class", classes);
|
|
|
|
}
|
|
|
|
if(_data['valign'])
|
|
|
|
{
|
|
|
|
var align = _mgrs["content"].expandName(_data["valign"]);
|
|
|
|
_tr.setAttribute("valign", align);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @augments et2_widget
|
|
|
|
*/
|
2021-06-07 17:33:53 +02:00
|
|
|
export class et2_nextmatch_rowWidget extends et2_widget implements et2_IDOMNode
|
2020-01-31 21:07:27 +01:00
|
|
|
{
|
2021-12-15 00:55:02 +01:00
|
|
|
private _widgets : any[];
|
|
|
|
private _row : any;
|
|
|
|
|
2020-01-31 21:07:27 +01:00
|
|
|
/**
|
|
|
|
* Constructor
|
|
|
|
*
|
|
|
|
* @param _mgrs
|
|
|
|
* @param _row
|
|
|
|
* @memberOf et2_nextmatch_rowWidget
|
|
|
|
*/
|
2021-12-15 00:55:02 +01:00
|
|
|
constructor(_mgrs, _row)
|
2020-01-31 21:07:27 +01:00
|
|
|
{
|
|
|
|
// Call the parent constructor with some dummy attributes
|
|
|
|
super(null, {"id": "", "type": "rowWidget"});
|
|
|
|
|
|
|
|
// Initialize some variables
|
|
|
|
this._widgets = [];
|
|
|
|
|
|
|
|
// Copy the given DOM node and the content arrays
|
|
|
|
this._mgrs = _mgrs;
|
|
|
|
this._row = _row;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Copies the given array manager and clones the given widgets and inserts
|
|
|
|
* them into the row which has been passed in the constructor.
|
|
|
|
*
|
|
|
|
* @param {array} _widgets
|
|
|
|
*/
|
2021-12-15 00:55:02 +01:00
|
|
|
createWidgets(_widgets)
|
2020-01-31 21:07:27 +01:00
|
|
|
{
|
|
|
|
// Clone the given the widgets with this element as parent
|
2020-03-11 17:56:51 +01:00
|
|
|
this._widgets = [];
|
|
|
|
let row_id = 0;
|
2021-12-15 00:55:02 +01:00
|
|
|
for(var i = 0; i < _widgets.length; i++)
|
2020-01-31 21:07:27 +01:00
|
|
|
{
|
|
|
|
// Disabled columns might be missing widget - skip it
|
2021-12-15 00:55:02 +01:00
|
|
|
if(!_widgets[i])
|
|
|
|
{
|
|
|
|
continue;
|
|
|
|
}
|
2020-01-31 21:07:27 +01:00
|
|
|
|
|
|
|
this._widgets[i] = _widgets[i].clone(this);
|
|
|
|
this._widgets[i].loadingFinished();
|
|
|
|
// Set column alignment from widget
|
2020-04-14 22:14:29 +02:00
|
|
|
if(this._widgets[i].align && this._row.childNodes[row_id])
|
2020-01-31 21:07:27 +01:00
|
|
|
{
|
2020-03-11 17:56:51 +01:00
|
|
|
this._row.childNodes[row_id].align = this._widgets[i].align;
|
2020-01-31 21:07:27 +01:00
|
|
|
}
|
2020-03-11 17:56:51 +01:00
|
|
|
row_id++;
|
2020-01-31 21:07:27 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Returns the column node for the given sender
|
|
|
|
*
|
|
|
|
* @param {et2_widget} _sender
|
|
|
|
* @return {DOMElement}
|
|
|
|
*/
|
2021-12-15 00:55:02 +01:00
|
|
|
getDOMNode(_sender)
|
2020-01-31 21:07:27 +01:00
|
|
|
{
|
2020-03-11 17:56:51 +01:00
|
|
|
var row_id = 0;
|
2021-12-15 00:55:02 +01:00
|
|
|
for(var i = 0; i < this._widgets.length; i++)
|
2020-01-31 21:07:27 +01:00
|
|
|
{
|
2020-03-11 17:56:51 +01:00
|
|
|
// Disabled columns might be missing widget - skip it
|
2021-12-15 00:55:02 +01:00
|
|
|
if(!this._widgets[i])
|
|
|
|
{
|
|
|
|
continue;
|
|
|
|
}
|
2020-04-14 22:14:29 +02:00
|
|
|
if(this._widgets[i] == _sender && this._row.childNodes[row_id])
|
2020-01-31 21:07:27 +01:00
|
|
|
{
|
2020-03-11 17:56:51 +01:00
|
|
|
return this._row.childNodes[row_id].childNodes[0]; // Return the i-th td tag
|
2020-01-31 21:07:27 +01:00
|
|
|
}
|
2020-03-11 17:56:51 +01:00
|
|
|
row_id++;
|
2020-01-31 21:07:27 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @augments et2_widget
|
|
|
|
*/
|
2021-06-07 17:33:53 +02:00
|
|
|
export class et2_nextmatch_rowTemplateWidget extends et2_widget implements et2_IDOMNode
|
2020-01-31 21:07:27 +01:00
|
|
|
{
|
2021-12-15 00:55:02 +01:00
|
|
|
private _root : any;
|
|
|
|
private _row : any;
|
|
|
|
private _widgets : any[];
|
|
|
|
|
2020-01-31 21:07:27 +01:00
|
|
|
/**
|
|
|
|
* Constructor
|
|
|
|
*
|
|
|
|
* @param _root
|
|
|
|
* @param _row
|
|
|
|
* @memberOf et2_nextmatch_rowTemplateWidget
|
|
|
|
*/
|
2021-12-15 00:55:02 +01:00
|
|
|
constructor(_root, _row)
|
2020-01-31 21:07:27 +01:00
|
|
|
{
|
|
|
|
// Call the parent constructor with some dummy attributes
|
|
|
|
super(null, {"id": "", "type": "rowTemplateWidget"});
|
|
|
|
|
|
|
|
this._root = _root;
|
|
|
|
this._mgrs = {};
|
|
|
|
this._row = _row;
|
|
|
|
|
|
|
|
// Set parent to root widget, so sub-widget calls still work
|
|
|
|
this._parent = _root;
|
|
|
|
|
|
|
|
// Clone the widgets inside the placeholders array
|
|
|
|
this._widgets = [];
|
|
|
|
}
|
|
|
|
|
2021-12-15 00:55:02 +01:00
|
|
|
createWidgets(_mgrs, _widgets : { widget : et2_widget, func(_row : any) : any; }[])
|
2020-01-31 21:07:27 +01:00
|
|
|
{
|
|
|
|
// Set the array managers - don't use setArrayMgrs here as this creates
|
|
|
|
// an unnecessary copy of the object
|
|
|
|
this._mgrs = _mgrs;
|
|
|
|
|
|
|
|
this._widgets = new Array(_widgets.length);
|
2021-12-15 00:55:02 +01:00
|
|
|
for(var i = 0; i < _widgets.length; i++)
|
2020-01-31 21:07:27 +01:00
|
|
|
{
|
|
|
|
this._row.childNodes[0].childNodes[0];
|
|
|
|
|
|
|
|
this._widgets[i] = {
|
|
|
|
"widget": _widgets[i].widget.clone(this),
|
|
|
|
"node": _widgets[i].func(this._row)
|
|
|
|
};
|
|
|
|
this._widgets[i].widget.loadingFinished();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Returns the column node for the given sender
|
|
|
|
*
|
|
|
|
* @param {et2_widget} _sender
|
|
|
|
* @return {DOMElement}
|
|
|
|
*/
|
2021-12-15 00:55:02 +01:00
|
|
|
getDOMNode(_sender : et2_widget) : HTMLElement
|
2020-01-31 21:07:27 +01:00
|
|
|
{
|
|
|
|
|
2021-12-15 00:55:02 +01:00
|
|
|
for(var i = 0; i < this._widgets.length; i++)
|
2020-01-31 21:07:27 +01:00
|
|
|
{
|
2021-12-15 00:55:02 +01:00
|
|
|
if(this._widgets[i].widget == _sender)
|
2020-01-31 21:07:27 +01:00
|
|
|
{
|
|
|
|
return this._widgets[i].node;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|