forked from extern/egroupware
40bbc53af8
onclick for nextmatch rows was not getting re-parsed for each row, so it was trying with the original widget from the template instead of the one from the row. Also accepting widget as argument into compiled legacy code and using it over original context so we don't need to re-compile for each row.
856 lines
22 KiB
TypeScript
856 lines
22 KiB
TypeScript
/**
|
|
* 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
|
|
* @link https://www.egroupware.org
|
|
* @author Andreas Stöckel
|
|
* @copyright EGroupware GmbH 2011-2021
|
|
*/
|
|
|
|
/*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";
|
|
import {et2_dataview_grid} from "./et2_dataview_view_grid";
|
|
import {egw} from "../jsapi/egw_global";
|
|
import {et2_IDetachedDOM, et2_IDOMNode} from "./et2_core_interfaces";
|
|
|
|
/**
|
|
* The row provider contains prototypes (full clonable dom-trees)
|
|
* for all registered row types.
|
|
*
|
|
*/
|
|
export class et2_nextmatch_rowProvider
|
|
{
|
|
private _rowProvider : any;
|
|
private _subgridCallback : any;
|
|
private _context : any;
|
|
private _rootWidget : any;
|
|
private _template : any;
|
|
private _dataRow : any;
|
|
|
|
/**
|
|
* Creates the nextmatch row provider.
|
|
*
|
|
* @param {et2_nextmatch_rowProvider} _rowProvider
|
|
* @param {function} _subgridCallback
|
|
* @param {object} _context
|
|
* @memberOf et2_nextmatch_rowProvider
|
|
*/
|
|
constructor(_rowProvider, _subgridCallback, _context)
|
|
{
|
|
|
|
|
|
// Copy the arguments
|
|
this._rowProvider = _rowProvider;
|
|
this._subgridCallback = _subgridCallback;
|
|
this._context = _context;
|
|
|
|
this._createEmptyPrototype();
|
|
}
|
|
|
|
destroy()
|
|
{
|
|
this._rowProvider.destroy();
|
|
this._subgridCallback = null;
|
|
this._context = null;
|
|
this._dataRow = null;
|
|
}
|
|
|
|
/**
|
|
* 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)
|
|
*/
|
|
setDataRowTemplate(_widgets, _rowData, _rootWidget)
|
|
{
|
|
// 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;
|
|
}
|
|
|
|
getDataRow(_data : any, _row, _idx, _controller)
|
|
{
|
|
|
|
// 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;
|
|
if(this._template.seperated.remaining.length > 0)
|
|
{
|
|
// Transform the variable attributes
|
|
for(var i = 0; i < this._template.seperated.remaining.length; i++)
|
|
{
|
|
var entry = this._template.seperated.remaining[i];
|
|
|
|
for(var j = 0; j < entry.data.length; j++)
|
|
{
|
|
var set = entry.data[j];
|
|
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));
|
|
}
|
|
}
|
|
}
|
|
|
|
// 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
|
|
for(var i = 0; i < this._template.seperated.detachable.length; i++)
|
|
{
|
|
var entry = this._template.seperated.detachable[i];
|
|
let widget = entry.widget;
|
|
|
|
// Parse the attribute expressions
|
|
let data : any = {};
|
|
let attributes = entry.data.map(attr => attr.attribute);
|
|
for(var j = 0; j < entry.data.length; j++)
|
|
{
|
|
var set = entry.data[j];
|
|
data[set.attribute] = mgrs["content"].expandName(set.expression);
|
|
}
|
|
// WebComponent IS the node, and we've already cloned it
|
|
if(typeof window.customElements.get(widget.localName) != "undefined")
|
|
{
|
|
widget = this._cloneWebComponent(entry, row, data);
|
|
if(!widget)
|
|
{
|
|
console.warn("Error cloning ", entry);
|
|
continue;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// Retrieve all DOM-Nodes (legacy widgets)
|
|
var nodes = new Array(entry.nodeFuncs.length);
|
|
for(var j = 0; j < nodes.length; j++)
|
|
{
|
|
// Use the previously compiled node function to get the node
|
|
// from the entry
|
|
try
|
|
{
|
|
nodes[j] = entry.nodeFuncs[j](row);
|
|
}
|
|
catch(e)
|
|
{
|
|
debugger;
|
|
continue;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Set the array managers first
|
|
widget.setArrayMgrs(mgrs);
|
|
if(typeof data.id != "undefined")
|
|
{
|
|
widget.id = data.id;
|
|
}
|
|
|
|
// Adjust data for that row
|
|
widget.transformAttributes?.call(widget, data);
|
|
|
|
// 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;
|
|
|
|
// Call the setDetachedAttributes function
|
|
widget.setDetachedAttributes(nodes, filtered, _data);
|
|
}
|
|
|
|
// Insert the row into the tr
|
|
var tr = _row.getDOMNode();
|
|
tr.appendChild(row);
|
|
|
|
// Make the row expandable
|
|
if(typeof _data.content["is_parent"] !== "undefined"
|
|
&& _data.content["is_parent"])
|
|
{
|
|
_row.makeExpandable(true, function()
|
|
{
|
|
return this._subgridCallback.call(this._context,
|
|
_row, _data, _controller);
|
|
}, 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]
|
|
);
|
|
if(top_controller.kept_expansion && expansion_index >= 0)
|
|
{
|
|
top_controller.kept_expansion.splice(expansion_index, 1);
|
|
// Use a timeout since the DOM nodes might not be finished yet
|
|
window.setTimeout(function()
|
|
{
|
|
_row.expansionButton.trigger('click');
|
|
}, et2_dataview_grid.ET2_GRID_INVALIDATE_TIMEOUT);
|
|
}
|
|
}
|
|
|
|
// Set the row data
|
|
this._setRowData(this._template.rowData, tr, mgrs);
|
|
|
|
return rowWidget;
|
|
}
|
|
|
|
/**
|
|
* Get the cloned web component
|
|
*/
|
|
_cloneWebComponent(entry, row, data)
|
|
{
|
|
// Widget was already cloned from original row, just get it
|
|
let widget = entry.nodeFuncs[0](row);
|
|
// 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);
|
|
let properties = widget_class ? widget_class.properties : [];
|
|
for(let key in properties)
|
|
{
|
|
widget[key] = original[key];
|
|
}
|
|
|
|
if(!widget || widget.localName !== entry.widget.localName)
|
|
{
|
|
return null;
|
|
}
|
|
|
|
// 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;
|
|
|
|
// 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;
|
|
}
|
|
|
|
/**
|
|
* 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'.
|
|
*/
|
|
_createEmptyPrototype()
|
|
{
|
|
var label = this._context && this._context.options && this._context.options.settings.placeholder;
|
|
|
|
var placeholder = jQuery(document.createElement("td"))
|
|
.attr("colspan", this._rowProvider.getColumnCount())
|
|
.css("height", "19px")
|
|
.text(typeof label != "undefined" && label ? label : egw().lang("No matches found"));
|
|
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
|
|
*/
|
|
_getVariableAttributeSet(_widget)
|
|
{
|
|
let variableAttributes = [];
|
|
|
|
const process = function(_widget)
|
|
{
|
|
// Create the attribtues
|
|
var hasAttr = false;
|
|
var widgetData = {
|
|
"widget": _widget,
|
|
"data": []
|
|
};
|
|
|
|
// Get all attribute values
|
|
let attrs = [];
|
|
if(_widget.getDetachedAttributes)
|
|
{
|
|
_widget.getDetachedAttributes(attrs);
|
|
// Manually add in ID for consideration, value won't get pulled without it
|
|
attrs.push("id");
|
|
}
|
|
for(let key of attrs)
|
|
{
|
|
let attr_name = key;
|
|
let val = _widget[key];
|
|
if(typeof val == "string" && val.indexOf("$") >= 0)
|
|
{
|
|
hasAttr = true;
|
|
widgetData.data.push({
|
|
"attribute": attr_name,
|
|
"expression": val
|
|
});
|
|
}
|
|
}
|
|
|
|
// 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
|
|
});
|
|
}
|
|
}
|
|
}
|
|
|
|
// Add the entry if there is any data in it
|
|
if(hasAttr)
|
|
{
|
|
variableAttributes.push(widgetData);
|
|
}
|
|
};
|
|
|
|
// Check each column
|
|
const columns = _widget._widgets;
|
|
for(var i = 0; i < columns.length; i++)
|
|
{
|
|
// If column is hidden, don't process it
|
|
if(typeof columns[i] === 'undefined' || this._context && this._context.columns && this._context.columns[i] && !this._context.columns[i].visible)
|
|
{
|
|
continue;
|
|
}
|
|
columns[i].iterateOver(process, this);
|
|
}
|
|
|
|
return variableAttributes;
|
|
}
|
|
|
|
_seperateWidgets(_varAttrs)
|
|
{
|
|
// 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
|
|
for(var i = 0; i < _varAttrs.length; i++)
|
|
{
|
|
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;
|
|
var checkWidget = function(_widget)
|
|
{
|
|
if(_widget.parent != null)
|
|
{
|
|
for(var i = 0; i < remaining.length; i++)
|
|
{
|
|
if(remaining[i].widget == _widget.parent)
|
|
{
|
|
insertWidget = false;
|
|
return;
|
|
}
|
|
}
|
|
|
|
checkWidget(_widget.parent);
|
|
}
|
|
};
|
|
checkWidget(widget);
|
|
|
|
// Handle the next widget if this one should not be included.
|
|
if(!insertWidget)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
// Check whether the widget implements the et2_IDetachedDOM interface
|
|
var isDetachable = false;
|
|
if(widget.implements && widget.implements(et2_IDetachedDOM))
|
|
{
|
|
// Get all attributes the widgets supports to be set in the
|
|
// "detached" mode
|
|
var supportedAttrs = [];
|
|
if(widget.getDetachedAttributes)
|
|
{
|
|
widget.getDetachedAttributes(supportedAttrs);
|
|
}
|
|
supportedAttrs.push("id");
|
|
isDetachable = true;
|
|
|
|
for(var j = 0; j < _varAttrs[i].data.length/* && isDetachable*/; j++)
|
|
{
|
|
var data = _varAttrs[i].data[j];
|
|
|
|
var supportsAttr = supportedAttrs.indexOf(data.attribute) != -1;
|
|
|
|
if(!supportsAttr)
|
|
{
|
|
egw.debug("warn", "et2_IDetachedDOM widget " +
|
|
widget._type + " does not support " + data.attribute);
|
|
}
|
|
|
|
isDetachable = isDetachable && supportsAttr;
|
|
}
|
|
}
|
|
|
|
// Insert the widget into the correct slot
|
|
if(isDetachable)
|
|
{
|
|
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
|
|
*/
|
|
_stripTemplateRow(_rowTemplate)
|
|
{
|
|
_rowTemplate.placeholders = [];
|
|
|
|
for(var i = 0; i < _rowTemplate.seperated.remaining.length; i++)
|
|
{
|
|
var entry = _rowTemplate.seperated.remaining[i];
|
|
|
|
// Issue a warning - widgets which do not implement et2_IDOMNode
|
|
// are very slow
|
|
egw.debug("warn", "Non-clonable widget '" + entry.widget._type + "' in dataview row - this " +
|
|
"might be slow", entry);
|
|
|
|
// Set the placeholder for the entry to null
|
|
entry.placeholder = null;
|
|
|
|
// Get the outer DOM-Node of the widget
|
|
if(entry.widget.implements(et2_IDOMNode))
|
|
{
|
|
var node = entry.widget.getDOMNode(entry.widget);
|
|
|
|
if(node && node.parentNode)
|
|
{
|
|
// 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)
|
|
});
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
_nodeIndex(_node)
|
|
{
|
|
if(_node.parentNode == null)
|
|
{
|
|
return 0;
|
|
}
|
|
for(var i = 0; i < _node.parentNode.childNodes.length; i++)
|
|
{
|
|
if(_node.parentNode.childNodes[i] == _node)
|
|
{
|
|
return i;
|
|
}
|
|
}
|
|
|
|
return -1;
|
|
}
|
|
|
|
/**
|
|
* Returns a function which does a relative access on the given DOM-Node
|
|
*
|
|
* @param {DOMElement} _root
|
|
* @param {DOMElement} _target
|
|
*/
|
|
_compileDOMAccessFunc(_root, _target)
|
|
{
|
|
function recordPath(_root, _target, _path)
|
|
{
|
|
if(typeof _path == "undefined")
|
|
{
|
|
_path = [];
|
|
}
|
|
|
|
if(_root != _target && _target)
|
|
{
|
|
// Get the index of _target in its parent node
|
|
var idx = this._nodeIndex(_target);
|
|
if(idx >= 0)
|
|
{
|
|
// 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
|
|
*/
|
|
_buildNodeAccessFuncs(_rowTemplate)
|
|
{
|
|
for(var i = 0; i < _rowTemplate.seperated.detachable.length; i++)
|
|
{
|
|
var entry = _rowTemplate.seperated.detachable[i];
|
|
|
|
// Get all needed nodes from the widget
|
|
var nodes = window.customElements.get(entry.widget.localName) ? [entry.widget] : entry.widget.getDetachedNodes();
|
|
var nodeFuncs = entry.nodeFuncs = new Array(nodes.length);
|
|
|
|
// Record the path to each DOM-Node
|
|
for(var j = 0; j < nodes.length; j++)
|
|
{
|
|
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 "!
|
|
*/
|
|
cat_regexp : RegExp = /(^| |,|cat_)([0-9]+)/g;
|
|
/**
|
|
* Regular expression used to filter out non-nummerical chars from above matches
|
|
*/
|
|
cat_cleanup : RegExp = /[^0-9]/g;
|
|
|
|
/**
|
|
* Applies additional row data (like the class) to the tr
|
|
*
|
|
* @param {object} _data
|
|
* @param {DOMElement} _tr
|
|
* @param {object} _mgrs
|
|
*/
|
|
_setRowData(_data, _tr, _mgrs)
|
|
{
|
|
// TODO: Implement other fields than "class"
|
|
if(_data["class"])
|
|
{
|
|
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)?)/);
|
|
if(category_location)
|
|
{
|
|
category_location = category_location[0];
|
|
}
|
|
|
|
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, '');
|
|
var cat_class = 'cat_' + cat_id;
|
|
|
|
classes += ' ' + cat_class;
|
|
}
|
|
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
|
|
*/
|
|
export class et2_nextmatch_rowWidget extends et2_widget implements et2_IDOMNode
|
|
{
|
|
private _widgets : any[];
|
|
private _row : any;
|
|
|
|
/**
|
|
* Constructor
|
|
*
|
|
* @param _mgrs
|
|
* @param _row
|
|
* @memberOf et2_nextmatch_rowWidget
|
|
*/
|
|
constructor(_mgrs, _row)
|
|
{
|
|
// 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
|
|
*/
|
|
createWidgets(_widgets)
|
|
{
|
|
// Clone the given the widgets with this element as parent
|
|
this._widgets = [];
|
|
let row_id = 0;
|
|
for(var i = 0; i < _widgets.length; i++)
|
|
{
|
|
// Disabled columns might be missing widget - skip it
|
|
if(!_widgets[i])
|
|
{
|
|
continue;
|
|
}
|
|
|
|
this._widgets[i] = _widgets[i].clone(this);
|
|
this._widgets[i].loadingFinished();
|
|
// Set column alignment from widget
|
|
if(this._widgets[i].align && this._row.childNodes[row_id])
|
|
{
|
|
this._row.childNodes[row_id].align = this._widgets[i].align;
|
|
}
|
|
row_id++;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Returns the column node for the given sender
|
|
*
|
|
* @param {et2_widget} _sender
|
|
* @return {DOMElement}
|
|
*/
|
|
getDOMNode(_sender)
|
|
{
|
|
var row_id = 0;
|
|
for(var i = 0; i < this._widgets.length; i++)
|
|
{
|
|
// Disabled columns might be missing widget - skip it
|
|
if(!this._widgets[i])
|
|
{
|
|
continue;
|
|
}
|
|
if(this._widgets[i] == _sender && this._row.childNodes[row_id])
|
|
{
|
|
return this._row.childNodes[row_id].childNodes[0]; // Return the i-th td tag
|
|
}
|
|
row_id++;
|
|
}
|
|
|
|
return null;
|
|
}
|
|
|
|
}
|
|
|
|
/**
|
|
* @augments et2_widget
|
|
*/
|
|
export class et2_nextmatch_rowTemplateWidget extends et2_widget implements et2_IDOMNode
|
|
{
|
|
private _root : any;
|
|
private _row : any;
|
|
private _widgets : any[];
|
|
|
|
/**
|
|
* Constructor
|
|
*
|
|
* @param _root
|
|
* @param _row
|
|
* @memberOf et2_nextmatch_rowTemplateWidget
|
|
*/
|
|
constructor(_root, _row)
|
|
{
|
|
// 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 = [];
|
|
}
|
|
|
|
createWidgets(_mgrs, _widgets : { widget : et2_widget, func(_row : any) : any; }[])
|
|
{
|
|
// 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);
|
|
for(var i = 0; i < _widgets.length; i++)
|
|
{
|
|
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}
|
|
*/
|
|
getDOMNode(_sender : et2_widget) : HTMLElement
|
|
{
|
|
|
|
for(var i = 0; i < this._widgets.length; i++)
|
|
{
|
|
if(this._widgets[i].widget == _sender)
|
|
{
|
|
return this._widgets[i].node;
|
|
}
|
|
}
|
|
|
|
return null;
|
|
}
|
|
|
|
}
|
|
|