UNPKG

@netdata/charts

Version:

Netdata frontend SDK and chart utilities

501 lines (500 loc) 25.4 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports["default"] = void 0; var _dimensionColors = _interopRequireDefault(require("./theme/dimensionColors")); var _deepEqual = _interopRequireWildcard(require("../../helpers/deepEqual")); var _heatmap = require("../../helpers/heatmap"); var _groupBy = _interopRequireDefault(require("lodash/groupBy")); var _isEmpty = _interopRequireDefault(require("lodash/isEmpty")); var _excluded = ["aggregationMethod"]; function _interopRequireWildcard(e, t) { if ("function" == typeof WeakMap) var r = new WeakMap(), n = new WeakMap(); return (_interopRequireWildcard = function _interopRequireWildcard(e, t) { if (!t && e && e.__esModule) return e; var o, i, f = { __proto__: null, "default": e }; if (null === e || "object" != _typeof(e) && "function" != typeof e) return f; if (o = t ? n : r) { if (o.has(e)) return o.get(e); o.set(e, f); } for (var _t in e) "default" !== _t && {}.hasOwnProperty.call(e, _t) && ((i = (o = Object.defineProperty) && Object.getOwnPropertyDescriptor(e, _t)) && (i.get || i.set) ? o(f, _t, i) : f[_t] = e[_t]); return f; })(e, t); } function _interopRequireDefault(e) { return e && e.__esModule ? e : { "default": e }; } function _toArray(r) { return _arrayWithHoles(r) || _iterableToArray(r) || _unsupportedIterableToArray(r) || _nonIterableRest(); } function _nonIterableRest() { throw new TypeError("Invalid attempt to destructure non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); } function _arrayWithHoles(r) { if (Array.isArray(r)) return r; } function _objectWithoutProperties(e, t) { if (null == e) return {}; var o, r, i = _objectWithoutPropertiesLoose(e, t); if (Object.getOwnPropertySymbols) { var n = Object.getOwnPropertySymbols(e); for (r = 0; r < n.length; r++) o = n[r], -1 === t.indexOf(o) && {}.propertyIsEnumerable.call(e, o) && (i[o] = e[o]); } return i; } function _objectWithoutPropertiesLoose(r, e) { if (null == r) return {}; var t = {}; for (var n in r) if ({}.hasOwnProperty.call(r, n)) { if (-1 !== e.indexOf(n)) continue; t[n] = r[n]; } return t; } function _typeof(o) { "@babel/helpers - typeof"; return _typeof = "function" == typeof Symbol && "symbol" == typeof Symbol.iterator ? function (o) { return typeof o; } : function (o) { return o && "function" == typeof Symbol && o.constructor === Symbol && o !== Symbol.prototype ? "symbol" : typeof o; }, _typeof(o); } function _toConsumableArray(r) { return _arrayWithoutHoles(r) || _iterableToArray(r) || _unsupportedIterableToArray(r) || _nonIterableSpread(); } function _nonIterableSpread() { throw new TypeError("Invalid attempt to spread non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); } function _unsupportedIterableToArray(r, a) { if (r) { if ("string" == typeof r) return _arrayLikeToArray(r, a); var t = {}.toString.call(r).slice(8, -1); return "Object" === t && r.constructor && (t = r.constructor.name), "Map" === t || "Set" === t ? Array.from(r) : "Arguments" === t || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(t) ? _arrayLikeToArray(r, a) : void 0; } } function _iterableToArray(r) { if ("undefined" != typeof Symbol && null != r[Symbol.iterator] || null != r["@@iterator"]) return Array.from(r); } function _arrayWithoutHoles(r) { if (Array.isArray(r)) return _arrayLikeToArray(r); } function _arrayLikeToArray(r, a) { (null == a || a > r.length) && (a = r.length); for (var e = 0, n = Array(a); e < a; e++) n[e] = r[e]; return n; } var noop = function noop() {}; var _default2 = exports["default"] = function _default2(chart, sdk) { var prevDimensionIds = []; var dimensionsById = {}; var sortedDimensionIds = []; var visibleDimensionIds = []; var visibleDimensionSet = new Set(); var colorCursor = 0; var sparklineDimensions = ["sum"]; chart.isSparkline = function () { return chart.getAttribute("sparkline"); }; chart.getHeatmapType = function () { return chart.getAttribute("heatmapType"); }; chart.getPayloadDimensionIds = function () { if (chart.isSparkline()) return sparklineDimensions; var viewDimensions = chart.getAttribute("viewDimensions"); return _toConsumableArray((viewDimensions === null || viewDimensions === void 0 ? void 0 : viewDimensions.ids) || []); }; var withSortByNameFallback = function withSortByNameFallback() { var cb = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : noop; var fallbackCb = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : noop; return function (a, b) { return cb(a, b) || fallbackCb(a, b) || chart.getDimensionName(a).localeCompare(chart.getDimensionName(b), undefined, { sensitivity: "accent", ignorePunctuation: true }); }; }; var bySortMethod = { "default": function _default() { var getIds = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : chart.getPayloadDimensionIds; return getIds().sort(withSortByNameFallback(function (a, b) { return chart.getDimensionPriority(a) - chart.getDimensionPriority(b); })); }, nameAsc: function nameAsc() { var getIds = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : chart.getPayloadDimensionIds; return getIds().sort(withSortByNameFallback()); }, nameDesc: function nameDesc() { var getIds = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : chart.getPayloadDimensionIds; return getIds().sort(function (a, b) { return chart.getDimensionName(b).localeCompare(chart.getDimensionName(a)); }); }, valueDesc: function valueDesc() { var getIds = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : chart.getPayloadDimensionIds; var x = arguments.length > 1 ? arguments[1] : undefined; var _chart$getPayload = chart.getPayload(), data = _chart$getPayload.data; x = x || data.length - 1; return getIds().sort(withSortByNameFallback(function (a, b) { return chart.getDimensionValue(b, x) - chart.getDimensionValue(a, x); })); }, valueAsc: function valueAsc() { var getIds = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : chart.getPayloadDimensionIds; var x = arguments.length > 1 ? arguments[1] : undefined; var _chart$getPayload2 = chart.getPayload(), data = _chart$getPayload2.data; x = x || data.length - 1; return getIds().sort(withSortByNameFallback(function (a, b) { return chart.getDimensionValue(a, x) - chart.getDimensionValue(b, x); })); }, anomalyDesc: function anomalyDesc() { var getIds = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : chart.getPayloadDimensionIds; var x = arguments.length > 1 ? arguments[1] : undefined; var _chart$getPayload3 = chart.getPayload(), all = _chart$getPayload3.all; x = x || all.length - 1; return getIds().sort(withSortByNameFallback(function (a, b) { return chart.getDimensionValue(b, x, { valueKey: "arp" }) - chart.getDimensionValue(a, x, { valueKey: "arp" }); }, function (a, b) { return chart.getDimensionValue(b, x) - chart.getDimensionValue(a, x); })); }, anomalyAsc: function anomalyAsc() { var getIds = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : chart.getPayloadDimensionIds; var x = arguments.length > 1 ? arguments[1] : undefined; var _chart$getPayload4 = chart.getPayload(), all = _chart$getPayload4.all; x = x || all.length - 1; return getIds().sort(withSortByNameFallback(function (a, b) { return chart.getDimensionValue(a, x, { valueKey: "arp" }) - chart.getDimensionValue(b, x, { valueKey: "arp" }); }, function (a, b) { return chart.getDimensionValue(a, x) - chart.getDimensionValue(b, x); })); }, annotationsDesc: function annotationsDesc() { var getIds = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : chart.getPayloadDimensionIds; var x = arguments.length > 1 ? arguments[1] : undefined; var _chart$getPayload5 = chart.getPayload(), all = _chart$getPayload5.all; x = x || all.length - 1; return getIds().sort(withSortByNameFallback(function (a, b) { return chart.getDimensionValue(b, x, { valueKey: "pa" }) - chart.getDimensionValue(a, x, { valueKey: "pa" }); }, function (a, b) { return chart.getDimensionValue(b, x) - chart.getDimensionValue(a, x); })); }, annotationsAsc: function annotationsAsc() { var getIds = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : chart.getPayloadDimensionIds; var x = arguments.length > 1 ? arguments[1] : undefined; var _chart$getPayload6 = chart.getPayload(), all = _chart$getPayload6.all; x = x || all.length - 1; return getIds().sort(withSortByNameFallback(function (a, b) { return chart.getDimensionValue(a, x, { valueKey: "pa" }) - chart.getDimensionValue(b, x, { valueKey: "pa" }); }, function (a, b) { return chart.getDimensionValue(a, x) - chart.getDimensionValue(b, x); })); } }; var alwaysSort = { valueDesc: true, valueAsc: true, anomalyDesc: true, anomalyAsc: true, annotationsDesc: true, annotationsAsc: true }; var updateVisibleDimensions = function updateVisibleDimensions() { var selectedLegendDimensions = chart.getAttribute("selectedLegendDimensions"); visibleDimensionIds = selectedLegendDimensions.length ? sortedDimensionIds.filter(function (id) { return selectedLegendDimensions.includes(id) || selectedLegendDimensions.includes(chart.getDimensionName(id)); }) : sortedDimensionIds; var prevDimensionSet = visibleDimensionSet; visibleDimensionSet = new Set(visibleDimensionIds); if (!(0, _deepEqual.setsAreEqual)(visibleDimensionSet, prevDimensionSet)) chart.trigger("visibleDimensionsChanged"); }; chart.sortDimensions = function () { var dimensionsSort = chart.getAttribute("dimensionsSort"); var sort = bySortMethod[dimensionsSort] || bySortMethod["default"]; sortedDimensionIds = sort(); updateVisibleDimensions(); if (!sortedDimensionIds) return; chart.trigger("dimensionChanged"); }; chart.onHoverSortDimensions = function (x) { var dimensionsSort = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : chart.getAttribute("dimensionsSort"); var sort = (0, _heatmap.isHeatmap)(chart) ? bySortMethod["default"] : bySortMethod[dimensionsSort] || bySortMethod["default"]; return sort(function () { return _toConsumableArray(chart.getVisibleDimensionIds()); }, x); }; var getNextColor = function getNextColor() { var colorsAttribute = chart.getAttribute("colors", []); var index = colorCursor++ % (colorsAttribute.length + _dimensionColors["default"].length); var nextColor = index < colorsAttribute.length ? typeof colorsAttribute[index] === "number" ? _dimensionColors["default"][colorsAttribute[index]] : !colorsAttribute[index] ? _dimensionColors["default"][colorCursor % _dimensionColors["default"].length] : colorsAttribute[index] : _dimensionColors["default"][index - colorsAttribute.length]; return nextColor; }; chart.updateDimensions = function () { var dimensionIds = chart.getPayloadDimensionIds(); if ((0, _deepEqual["default"])(prevDimensionIds, dimensionIds)) { if (alwaysSort[chart.getAttribute("dimensionsSort")]) chart.sortDimensions(); return; } prevDimensionIds = dimensionIds; var groupedByDimension = chart.getAttribute("groupBy").length === 1 && chart.getAttribute("groupBy")[0] === "dimension"; var prefix = null; dimensionsById = dimensionIds.reduce(function (acc, id, index) { if (groupedByDimension && !chart.getHeatmapType()) { var _id$match; var newPrefix = (_id$match = id.match(/(.+)_(\d+?\.?(\d+)?|\+[Ii]nf)$/)) === null || _id$match === void 0 ? void 0 : _id$match[1]; chart.setAttribute("heatmapType", prefix && newPrefix !== prefix || !newPrefix ? _heatmap.heatmapTypes.disabled : null); if (prefix === newPrefix) chart.setAttribute("heatmapType", _heatmap.heatmapTypes[prefix] || _heatmap.heatmapTypes.incremental); prefix = newPrefix; } acc[id] = index; return acc; }, {}); if (!groupedByDimension) chart.setAttribute("heatmapType", null);else if (/latency/.test(chart.getAttribute("context"))) chart.setAttribute("heatmapType", _heatmap.heatmapTypes["default"]); chart.sortDimensions(); chart.updateColors(); }; chart.getDimensionIndex = function (id) { return dimensionsById[id]; }; chart.getDimensionIds = function () { return sortedDimensionIds; }; chart.getVisibleDimensionIds = function () { return visibleDimensionIds; }; chart.isDimensionVisible = function (id) { return visibleDimensionSet.has(id); }; var memKey = null; var getMemKey = function getMemKey() { if (memKey) return memKey; var _chart$getAttributes = chart.getAttributes(), colors = _chart$getAttributes.colors, contextScope = _chart$getAttributes.contextScope, id = _chart$getAttributes.id; var keySource = String(id || contextScope && contextScope[0] || "default"); memKey = colors.length ? chart.getAttribute("id") : keySource.split(/[._]/)[0]; return memKey; }; chart.selectDimensionColor = function () { var _id$split; var id = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : "selected"; var partIndex = arguments.length > 1 ? arguments[1] : undefined; var key = getMemKey(); var colorsAttr = chart.getAttribute("colors"); var sparkline = chart.isSparkline(); if (sparkline && Array.isArray(colorsAttr)) return colorsAttr[0]; var isSelected = id === "selected"; id = !id || isSelected ? chart.getAttribute("selectedDimensions")[0] || id : id; if (!isNaN(partIndex)) id = ((_id$split = id.split(",")) === null || _id$split === void 0 ? void 0 : _id$split[partIndex]) || id; var color = isSelected && colorsAttr !== null && colorsAttr !== void 0 && colorsAttr.length ? colorsAttr[0] : sdk.getRoot().getNextColor(getNextColor, key, id); var index = chart.getThemeIndex(); return typeof color === "string" ? color : color[index]; }; chart.getDimensionName = function (id, partIndex) { var _dimName$split$partIn, _dimName$split; var viewDimensions = chart.getAttribute("viewDimensions"); if (!(viewDimensions !== null && viewDimensions !== void 0 && viewDimensions.names)) return ""; var dimName = viewDimensions.names[dimensionsById[id]] || id; if (!isNaN(partIndex)) dimName = (_dimName$split$partIn = (_dimName$split = dimName.split(",")) === null || _dimName$split === void 0 ? void 0 : _dimName$split[partIndex]) !== null && _dimName$split$partIn !== void 0 ? _dimName$split$partIn : dimName; if ((0, _heatmap.isHeatmap)(chart)) return (0, _heatmap.withoutPrefix)(dimName); return dimName; }; chart.getDimensionPriority = function (id) { var viewDimensions = chart.getAttribute("viewDimensions"); if (!(viewDimensions !== null && viewDimensions !== void 0 && viewDimensions.priorities)) return 0; return viewDimensions.priorities[dimensionsById[id]]; }; chart.getDimensionUnit = function (id) { var viewDimensions = chart.getAttribute("viewDimensions"); if (!(viewDimensions !== null && viewDimensions !== void 0 && viewDimensions.units)) return ""; if (!id) return viewDimensions.units[0]; return viewDimensions.units[dimensionsById[id]] || viewDimensions.units[0]; }; chart.getDimensionContext = function (id) { var viewDimensions = chart.getAttribute("viewDimensions"); if (!(viewDimensions !== null && viewDimensions !== void 0 && viewDimensions.contexts)) return ""; return viewDimensions.contexts[dimensionsById[id]] || viewDimensions.contexts[0]; }; chart.getUnitAttributes = function (id) { var key = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : "units"; var context = chart.getDimensionContext(id); if (context) { if (chart.getAttribute("".concat(key, "ByContext"))[context]) { return chart.getAttribute("".concat(key, "ByContext"))[context]; } } var baseUnit = chart.getDimensionUnit(id); var unitIndex = chart.getAttribute(key).findIndex(function (u) { return u === baseUnit; }); unitIndex = unitIndex === -1 ? 0 : unitIndex; var base = chart.getAttribute("".concat(key, "ConversionBase"))[unitIndex]; var prefix = chart.getAttribute("".concat(key, "ConversionPrefix"))[unitIndex]; var method = chart.getAttribute("".concat(key, "ConversionMethod"))[unitIndex]; var fractionDigits = chart.getAttribute("".concat(key, "ConversionFractionDigits"))[unitIndex]; var divider = chart.getAttribute("".concat(key, "ConversionDivider"))[unitIndex]; var unit = chart.getAttribute(key)[unitIndex]; return { method: method, fractionDigits: fractionDigits, base: base, prefix: prefix, divider: divider, unit: unit }; }; chart.getDimensionGroups = function () { var _viewDimensions$group; var viewDimensions = chart.getAttribute("viewDimensions"); if (!(viewDimensions !== null && viewDimensions !== void 0 && (_viewDimensions$group = viewDimensions.grouped) !== null && _viewDimensions$group !== void 0 && _viewDimensions$group.length)) return []; return viewDimensions.grouped; }; chart.getRowDimensionValue = function (id, pointData) { var _ref = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {}, _ref$valueKey = _ref.valueKey, valueKey = _ref$valueKey === void 0 ? "value" : _ref$valueKey, _ref$abs = _ref.abs, abs = _ref$abs === void 0 ? true : _ref$abs, _ref$incrementable = _ref.incrementable, incrementable = _ref$incrementable === void 0 ? true : _ref$incrementable, _ref$allowNull = _ref.allowNull, allowNull = _ref$allowNull === void 0 ? false : _ref$allowNull; var value = pointData === null || pointData === void 0 ? void 0 : pointData[dimensionsById[id] + 1]; if (typeof value === "undefined") return null; value = value !== null && _typeof(value) === "object" ? value[valueKey] : value; value = allowNull && value === null ? value : abs ? Math.abs(value) : value; if (incrementable && (0, _heatmap.isIncremental)(chart)) { var index = chart.getDimensionIds().findIndex(function (dimId) { return dimId === id; }); if (index === -1) return value; var prevId = chart.getDimensionIds()[index - 1]; while (prevId && !chart.isDimensionVisible(prevId)) prevId = chart.getDimensionIds()[--index - 1]; value = value - (chart.getRowDimensionValue(prevId, pointData, { valueKey: valueKey, abs: abs, incrementable: false, allowNull: allowNull }) || 0); } return value; }; chart.getDimensionValue = function (id, index) { var options = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {}; var _chart$getPayload7 = chart.getPayload(), all = _chart$getPayload7.all; var pointData = all[index]; return chart.getRowDimensionValue(id, pointData, options); }; chart.getAggregatedDimensionValue = function (dimensionIds, index) { var _ref2 = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {}, _ref2$aggregationMeth = _ref2.aggregationMethod, aggregationMethod = _ref2$aggregationMeth === void 0 ? "sum" : _ref2$aggregationMeth, options = _objectWithoutProperties(_ref2, _excluded); var _chart$getPayload8 = chart.getPayload(), all = _chart$getPayload8.all; var pointData = all[index]; var targetDimensionIds = dimensionIds; if (!Array.isArray(dimensionIds) || dimensionIds.length === 0) { var visibleIds = chart.getVisibleDimensionIds(); targetDimensionIds = visibleIds.length > 0 ? visibleIds : chart.getDimensionIds(); } var values = targetDimensionIds.map(function (id) { return chart.getRowDimensionValue(id, pointData, options); }).filter(function (value) { return value !== null && value !== undefined && !isNaN(value); }); if (values.length === 0) { return 0; } switch (aggregationMethod) { case "sum": return values.reduce(function (acc, val) { return acc + val; }, 0); case "avg": return values.reduce(function (acc, val) { return acc + val; }, 0) / values.length; case "min": return Math.min.apply(Math, _toConsumableArray(values)); case "max": return Math.max.apply(Math, _toConsumableArray(values)); default: return values.reduce(function (acc, val) { return acc + val; }, 0); } }; var keepoutRegex = ".*"; var keepRegex = "(" + keepoutRegex + ")"; var _applyGroupByRecursively = function applyGroupByRecursively(obj, groupByRegex) { if (Array.isArray(obj)) { return (0, _groupBy["default"])(obj, function (value) { var _value$match = value.match(groupByRegex), _value$match2 = _toArray(_value$match), matches = _value$match2.slice(1); return matches.join(","); }); } if (_typeof(obj) === "object" && obj !== null) { var result = {}; Object.keys(obj).forEach(function (key) { result[key] = _applyGroupByRecursively(obj[key], groupByRegex); }); return result; } return obj; }; var _groupByColumn = function groupByColumn(result, ids, groups, attrs) { if (!attrs || !Array.isArray(attrs) || attrs.length === 0) { return result; } var _attrs = _toArray(attrs), attr = _attrs[0], restAttrs = _attrs.slice(1); var groupByRegex = new RegExp(groups.reduce(function (s, g) { s = s + (s ? "," : ""); if (attr === g) { s = s + keepRegex; } else { s = s + keepoutRegex; } return s; }, "")); if ((0, _isEmpty["default"])(result)) { result = (0, _groupBy["default"])(ids, function (value) { var _value$match3 = value.match(groupByRegex), _value$match4 = _toArray(_value$match3), matches = _value$match4.slice(1); return matches.join(","); }); } else { Object.keys(result).forEach(function (key) { result[key] = _applyGroupByRecursively(result[key], groupByRegex); }); } if (!restAttrs.length) return result; return _groupByColumn(result, ids, groups, restAttrs); }; chart.getTableMatrix = function () { var dimensionIds = chart.getDimensionIds(); var groups = chart.getDimensionGroups(); var tableColumns = chart.getAttribute("tableColumns"); var forRows = []; var groupByRegex = new RegExp(groups.reduce(function (s, g) { s = s + (s ? "," : ""); if (tableColumns.includes(g)) { s = s + keepoutRegex; } else { s = s + keepRegex; forRows.push(g); } return s; }, "")); var baseGroup = (0, _groupBy["default"])(dimensionIds, function (value) { var _value$match5 = value.match(groupByRegex), _value$match6 = _toArray(_value$match5), matches = _value$match6.slice(1); return matches.join(","); }); var contextAndDimensionsGroup = _groupByColumn({}, dimensionIds, groups, tableColumns); return [baseGroup, contextAndDimensionsGroup, forRows]; }; chart.toggleDimensionId = function (id) { var _ref3 = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {}, _ref3$merge = _ref3.merge, merge = _ref3$merge === void 0 ? false : _ref3$merge; var selectedLegendDimensions = chart.getAttribute("selectedLegendDimensions"); if (!selectedLegendDimensions.length) { chart.updateAttribute("selectedLegendDimensions", merge ? chart.getDimensionIds().filter(function (d) { return d !== id; }) : [id]); return; } if (chart.isDimensionVisible(id)) { var _newSelectedLegendDimensions = selectedLegendDimensions.filter(function (d) { return d !== id; }); chart.updateAttribute("selectedLegendDimensions", _newSelectedLegendDimensions.length ? merge ? _newSelectedLegendDimensions : [id] : []); return; } var newSelectedLegendDimensions = merge ? [].concat(_toConsumableArray(selectedLegendDimensions), [id]) : [id]; chart.updateAttribute("selectedLegendDimensions", newSelectedLegendDimensions); }; chart.onAttributeChange("dimensionsSort", chart.sortDimensions); chart.onAttributeChange("selectedLegendDimensions", updateVisibleDimensions); chart.updateColors = function () { var dimensionIds = chart.getAttribute("dimensionIds"); if (!dimensionIds.length) return; var keys = chart.isSparkline() ? sparklineDimensions : dimensionIds; keys.forEach(function (id) { return chart.selectDimensionColor(id); }); }; };