egroupware/api/js/etemplate/et2_extension_nextmatch_rowProvider.js
nathangray 8f8c437712 Try to avoid error from missing nodes
I suspect this is from either TypeScript conversion or nm node optimization, but can't duplicate it regularly
2020-04-14 15:03:32 -06:00

577 lines
24 KiB
JavaScript

"use strict";
/**
* 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 http://www.egroupware.org
* @author Andreas Stöckel
* @copyright Stylite 2012
* @version $Id$
*/
var __extends = (this && this.__extends) || (function () {
var extendStatics = function (d, b) {
extendStatics = Object.setPrototypeOf ||
({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||
function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; };
return extendStatics(d, b);
};
return function (d, b) {
extendStatics(d, b);
function __() { this.constructor = d; }
d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
};
})();
Object.defineProperty(exports, "__esModule", { value: true });
/*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;
*/
var et2_core_widget_1 = require("./et2_core_widget");
var et2_core_arrayMgr_1 = require("./et2_core_arrayMgr");
/**
* The row provider contains prototypes (full clonable dom-trees)
* for all registered row types.
*
*/
var et2_nextmatch_rowProvider = /** @class */ (function () {
/**
* Creates the nextmatch row provider.
*
* @param {et2_nextmatch_rowProvider} _rowProvider
* @param {function} _subgridCallback
* @param {object} _context
* @memberOf et2_nextmatch_rowProvider
*/
function et2_nextmatch_rowProvider(_rowProvider, _subgridCallback, _context) {
/**
* 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 "!
*/
this.cat_regexp = /(^| |,|cat_)([0-9]+)/g;
/**
* Regular expression used to filter out non-nummerical chars from above matches
*/
this.cat_cleanup = /[^0-9]/g;
// Copy the arguments
this._rowProvider = _rowProvider;
this._subgridCallback = _subgridCallback;
this._context = _context;
this._createEmptyPrototype();
}
et2_nextmatch_rowProvider.prototype.destroy = function () {
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)
*/
et2_nextmatch_rowProvider.prototype.setDataRowTemplate = function (_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;
};
et2_nextmatch_rowProvider.prototype.getDataRow = function (_data, _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_core_arrayMgr_1.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 = 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];
entry.widget.options[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];
// Parse the attribute expressions
var data = {};
for (var j = 0; j < entry.data.length; j++) {
var set = entry.data[j];
data[set.attribute] = mgrs["content"].expandName(set.expression);
}
// Retrieve all DOM-Nodes
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
entry.widget._mgrs = mgrs;
if (typeof data.id != "undefined") {
entry.widget.id = data.id;
}
// Adjust data for that row
entry.widget.transformAttributes.call(entry.widget, data);
// Call the setDetachedAttributes function
entry.widget.setDetachedAttributes(nodes, data, _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;
};
/**
* 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'.
*/
et2_nextmatch_rowProvider.prototype._createEmptyPrototype = function () {
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
*/
et2_nextmatch_rowProvider.prototype._getVariableAttributeSet = function (_widget) {
var variableAttributes = [];
var process = function (_widget) {
// Create the attribtues
var hasAttr = false;
var widgetData = {
"widget": _widget,
"data": []
};
// Get all attribute values
for (var key in _widget.attributes) {
if (!_widget.attributes[key].ignore &&
typeof _widget.options[key] != "undefined") {
var val = _widget.options[key];
// TODO: Improve detection
if (typeof val == "string" && val.indexOf("$") >= 0) {
hasAttr = true;
widgetData.data.push({
"attribute": key,
"expression": val
});
}
}
}
// Add the entry if there is any data in it
if (hasAttr) {
variableAttributes.push(widgetData);
}
};
// Check each column
var 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;
};
et2_nextmatch_rowProvider.prototype._seperateWidgets = function (_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(et2_IDetachedDOM)) {
// Get all attributes the widgets supports to be set in the
// "detached" mode
var supportedAttrs = [];
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
*/
et2_nextmatch_rowProvider.prototype._stripTemplateRow = function (_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)
});
}
}
}
};
et2_nextmatch_rowProvider.prototype._nodeIndex = function (_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
*/
et2_nextmatch_rowProvider.prototype._compileDOMAccessFunc = function (_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
*/
et2_nextmatch_rowProvider.prototype._buildNodeAccessFuncs = function (_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 = 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]);
}
}
};
/**
* Applies additional row data (like the class) to the tr
*
* @param {object} _data
* @param {DOMElement} _tr
* @param {object} _mgrs
*/
et2_nextmatch_rowProvider.prototype._setRowData = function (_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);
}
};
return et2_nextmatch_rowProvider;
}());
exports.et2_nextmatch_rowProvider = et2_nextmatch_rowProvider;
/**
* @augments et2_widget
*/
var et2_nextmatch_rowWidget = /** @class */ (function (_super) {
__extends(et2_nextmatch_rowWidget, _super);
/**
* Constructor
*
* @param _mgrs
* @param _row
* @memberOf et2_nextmatch_rowWidget
*/
function et2_nextmatch_rowWidget(_mgrs, _row) {
var _this =
// Call the parent constructor with some dummy attributes
_super.call(this, null, { "id": "", "type": "rowWidget" }) || this;
// Initialize some variables
_this._widgets = [];
// Copy the given DOM node and the content arrays
_this._mgrs = _mgrs;
_this._row = _row;
return _this;
}
/**
* 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
*/
et2_nextmatch_rowWidget.prototype.createWidgets = function (_widgets) {
// Clone the given the widgets with this element as parent
this._widgets = [];
var 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}
*/
et2_nextmatch_rowWidget.prototype.getDOMNode = function (_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;
};
return et2_nextmatch_rowWidget;
}(et2_core_widget_1.et2_widget));
/**
* @augments et2_widget
*/
var et2_nextmatch_rowTemplateWidget = /** @class */ (function (_super) {
__extends(et2_nextmatch_rowTemplateWidget, _super);
/**
* Constructor
*
* @param _root
* @param _row
* @memberOf et2_nextmatch_rowTemplateWidget
*/
function et2_nextmatch_rowTemplateWidget(_root, _row) {
var _this =
// Call the parent constructor with some dummy attributes
_super.call(this, null, { "id": "", "type": "rowTemplateWidget" }) || this;
_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 = [];
return _this;
}
et2_nextmatch_rowTemplateWidget.prototype.createWidgets = function (_mgrs, _widgets) {
// 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}
*/
et2_nextmatch_rowTemplateWidget.prototype.getDOMNode = function (_sender) {
for (var i = 0; i < this._widgets.length; i++) {
if (this._widgets[i].widget == _sender) {
return this._widgets[i].node;
}
}
return null;
};
return et2_nextmatch_rowTemplateWidget;
}(et2_core_widget_1.et2_widget));
//# sourceMappingURL=et2_extension_nextmatch_rowProvider.js.map