From 2527201dffb36b88e4db7165ea8f1eb825a95613 Mon Sep 17 00:00:00 2001 From: Glomberg Date: Wed, 19 Aug 2026 13:32:46 +0300 Subject: [PATCH 1/6] Fix. React. Select tab endpoint fixed. --- js/public/spbct-react-bundle.js | 2 +- js/src/react/components/TabContent/TabContent.js | 13 +++++++------ 2 files changed, 8 insertions(+), 7 deletions(-) diff --git a/js/public/spbct-react-bundle.js b/js/public/spbct-react-bundle.js index bbf211c4e..e186bdab4 100644 --- a/js/public/spbct-react-bundle.js +++ b/js/public/spbct-react-bundle.js @@ -376,7 +376,7 @@ eval("{__webpack_require__.r(__webpack_exports__);\n/* harmony export */ __webpa \**********************************************************/ (__unused_webpack_module, __webpack_exports__, __webpack_require__) { -eval("{__webpack_require__.r(__webpack_exports__);\n/* harmony export */ __webpack_require__.d(__webpack_exports__, {\n/* harmony export */ \"default\": () => (/* binding */ TabContent)\n/* harmony export */ });\n/* harmony import */ var react__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! react */ \"./node_modules/react/index.js\");\n/* harmony import */ var react__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(react__WEBPACK_IMPORTED_MODULE_0__);\n/* harmony import */ var _styles_css__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./styles.css */ \"./js/src/react/components/TabContent/styles.css\");\n/* harmony import */ var _Preloader_Preloader__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ../Preloader/Preloader */ \"./js/src/react/components/Preloader/Preloader.js\");\n/* harmony import */ var _modules_Http_Ajax__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ../../modules/Http/Ajax */ \"./js/src/react/modules/Http/Ajax.js\");\n/* harmony import */ var _TabDefault_TabDefault__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(/*! ./TabDefault/TabDefault */ \"./js/src/react/components/TabContent/TabDefault/TabDefault.js\");\nfunction _regenerator() { /*! regenerator-runtime -- Copyright (c) 2014-present, Facebook, Inc. -- license (MIT): https://github.com/babel/babel/blob/main/packages/babel-helpers/LICENSE */ var e, t, r = \"function\" == typeof Symbol ? Symbol : {}, n = r.iterator || \"@@iterator\", o = r.toStringTag || \"@@toStringTag\"; function i(r, n, o, i) { var c = n && n.prototype instanceof Generator ? n : Generator, u = Object.create(c.prototype); return _regeneratorDefine2(u, \"_invoke\", function (r, n, o) { var i, c, u, f = 0, p = o || [], y = !1, G = { p: 0, n: 0, v: e, a: d, f: d.bind(e, 4), d: function d(t, r) { return i = t, c = 0, u = e, G.n = r, a; } }; function d(r, n) { for (c = r, u = n, t = 0; !y && f && !o && t < p.length; t++) { var o, i = p[t], d = G.p, l = i[2]; r > 3 ? (o = l === n) && (u = i[(c = i[4]) ? 5 : (c = 3, 3)], i[4] = i[5] = e) : i[0] <= d && ((o = r < 2 && d < i[1]) ? (c = 0, G.v = n, G.n = i[1]) : d < l && (o = r < 3 || i[0] > n || n > l) && (i[4] = r, i[5] = n, G.n = l, c = 0)); } if (o || r > 1) return a; throw y = !0, n; } return function (o, p, l) { if (f > 1) throw TypeError(\"Generator is already running\"); for (y && 1 === p && d(p, l), c = p, u = l; (t = c < 2 ? e : u) || !y;) { i || (c ? c < 3 ? (c > 1 && (G.n = -1), d(c, u)) : G.n = u : G.v = u); try { if (f = 2, i) { if (c || (o = \"next\"), t = i[o]) { if (!(t = t.call(i, u))) throw TypeError(\"iterator result is not an object\"); if (!t.done) return t; u = t.value, c < 2 && (c = 0); } else 1 === c && (t = i[\"return\"]) && t.call(i), c < 2 && (u = TypeError(\"The iterator does not provide a '\" + o + \"' method\"), c = 1); i = e; } else if ((t = (y = G.n < 0) ? u : r.call(n, G)) !== a) break; } catch (t) { i = e, c = 1, u = t; } finally { f = 1; } } return { value: t, done: y }; }; }(r, o, i), !0), u; } var a = {}; function Generator() {} function GeneratorFunction() {} function GeneratorFunctionPrototype() {} t = Object.getPrototypeOf; var c = [][n] ? t(t([][n]())) : (_regeneratorDefine2(t = {}, n, function () { return this; }), t), u = GeneratorFunctionPrototype.prototype = Generator.prototype = Object.create(c); function f(e) { return Object.setPrototypeOf ? Object.setPrototypeOf(e, GeneratorFunctionPrototype) : (e.__proto__ = GeneratorFunctionPrototype, _regeneratorDefine2(e, o, \"GeneratorFunction\")), e.prototype = Object.create(u), e; } return GeneratorFunction.prototype = GeneratorFunctionPrototype, _regeneratorDefine2(u, \"constructor\", GeneratorFunctionPrototype), _regeneratorDefine2(GeneratorFunctionPrototype, \"constructor\", GeneratorFunction), GeneratorFunction.displayName = \"GeneratorFunction\", _regeneratorDefine2(GeneratorFunctionPrototype, o, \"GeneratorFunction\"), _regeneratorDefine2(u), _regeneratorDefine2(u, o, \"Generator\"), _regeneratorDefine2(u, n, function () { return this; }), _regeneratorDefine2(u, \"toString\", function () { return \"[object Generator]\"; }), (_regenerator = function _regenerator() { return { w: i, m: f }; })(); }\nfunction _regeneratorDefine2(e, r, n, t) { var i = Object.defineProperty; try { i({}, \"\", {}); } catch (e) { i = 0; } _regeneratorDefine2 = function _regeneratorDefine(e, r, n, t) { function o(r, n) { _regeneratorDefine2(e, r, function (e) { return this._invoke(r, n, e); }); } r ? i ? i(e, r, { value: n, enumerable: !t, configurable: !t, writable: !t }) : e[r] = n : (o(\"next\", 0), o(\"throw\", 1), o(\"return\", 2)); }, _regeneratorDefine2(e, r, n, t); }\nfunction asyncGeneratorStep(n, t, e, r, o, a, c) { try { var i = n[a](c), u = i.value; } catch (n) { return void e(n); } i.done ? t(u) : Promise.resolve(u).then(r, o); }\nfunction _asyncToGenerator(n) { return function () { var t = this, e = arguments; return new Promise(function (r, o) { var a = n.apply(t, e); function _next(n) { asyncGeneratorStep(a, r, o, _next, _throw, \"next\", n); } function _throw(n) { asyncGeneratorStep(a, r, o, _next, _throw, \"throw\", n); } _next(void 0); }); }; }\nfunction _slicedToArray(r, e) { return _arrayWithHoles(r) || _iterableToArrayLimit(r, e) || _unsupportedIterableToArray(r, e) || _nonIterableRest(); }\nfunction _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.\"); }\nfunction _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; } }\nfunction _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; }\nfunction _iterableToArrayLimit(r, l) { var t = null == r ? null : \"undefined\" != typeof Symbol && r[Symbol.iterator] || r[\"@@iterator\"]; if (null != t) { var e, n, i, u, a = [], f = !0, o = !1; try { if (i = (t = t.call(r)).next, 0 === l) { if (Object(t) !== t) return; f = !1; } else for (; !(f = (e = i.call(t)).done) && (a.push(e.value), a.length !== l); f = !0); } catch (r) { o = !0, n = r; } finally { try { if (!f && null != t[\"return\"] && (u = t[\"return\"](), Object(u) !== u)) return; } finally { if (o) throw n; } } return a; } }\nfunction _arrayWithHoles(r) { if (Array.isArray(r)) return r; }\n;\n\n\n\n\nfunction TabContent(_ref) {\n var sectionId = _ref.sectionId,\n isActive = _ref.isActive,\n tabComponent = _ref.tabComponent;\n var _useState = (0,react__WEBPACK_IMPORTED_MODULE_0__.useState)(null),\n _useState2 = _slicedToArray(_useState, 2),\n sectionData = _useState2[0],\n setSectionData = _useState2[1];\n (0,react__WEBPACK_IMPORTED_MODULE_0__.useEffect)(function () {\n if (isActive && !sectionData) {\n if (sectionId === 'critical_updates' || sectionId === 'fswatcher' || sectionId === 'traffic_control' || sectionId === 'security_certified') {\n fetchContent();\n } else {\n fetchContentOld();\n }\n }\n }, [isActive, sectionData]);\n var fetchContent = /*#__PURE__*/function () {\n var _ref2 = _asyncToGenerator(/*#__PURE__*/_regenerator().m(function _callee() {\n var requestData, data, _t;\n return _regenerator().w(function (_context) {\n while (1) switch (_context.p = _context.n) {\n case 0:\n _context.p = 0;\n requestData = {\n action: 'spbct_get_tab_data',\n tab_name: sectionId\n };\n _context.n = 1;\n return (0,_modules_Http_Ajax__WEBPACK_IMPORTED_MODULE_3__.sendAjaxRequest)(requestData, true);\n case 1:\n data = _context.v;\n setSectionData(data);\n _context.n = 3;\n break;\n case 2:\n _context.p = 2;\n _t = _context.v;\n console.error('Error fetching content:', _t);\n case 3:\n return _context.a(2);\n }\n }, _callee, null, [[0, 2]]);\n }));\n return function fetchContent() {\n return _ref2.apply(this, arguments);\n };\n }();\n var fetchContentOld = /*#__PURE__*/function () {\n var _ref3 = _asyncToGenerator(/*#__PURE__*/_regenerator().m(function _callee2() {\n var requestData, data, _t2;\n return _regenerator().w(function (_context2) {\n while (1) switch (_context2.p = _context2.n) {\n case 0:\n _context2.p = 0;\n requestData = {\n action: 'spbc_settings__draw_elements',\n tab_name: sectionId\n };\n _context2.n = 1;\n return (0,_modules_Http_Ajax__WEBPACK_IMPORTED_MODULE_3__.sendAjaxRequest)(requestData);\n case 1:\n data = _context2.v;\n setSectionData(data);\n document.dispatchEvent(new CustomEvent('spbctTabLoaded', {\n bubbles: true,\n detail: {\n tab: sectionId\n }\n }));\n _context2.n = 3;\n break;\n case 2:\n _context2.p = 2;\n _t2 = _context2.v;\n console.error('Error fetching content:', _t2);\n case 3:\n return _context2.a(2);\n }\n }, _callee2, null, [[0, 2]]);\n }));\n return function fetchContentOld() {\n return _ref3.apply(this, arguments);\n };\n }();\n var isActiveClass = isActive ? 'spbc_tab_section--active' : '';\n var TabContentInner = tabComponent || _TabDefault_TabDefault__WEBPACK_IMPORTED_MODULE_4__[\"default\"];\n return /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement(\"div\", {\n id: 'spbc_tab-' + sectionId,\n className: 'spbc_tab spbc_tab-' + sectionId + ' ' + isActiveClass\n }, isActive && sectionData ? /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement(TabContentInner, {\n sectionId: sectionId,\n sectionData: sectionData,\n isActive: isActive\n }) : /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_Preloader_Preloader__WEBPACK_IMPORTED_MODULE_2__[\"default\"], null));\n}\n\n//# sourceURL=webpack:///./js/src/react/components/TabContent/TabContent.js?\n}"); +eval("{__webpack_require__.r(__webpack_exports__);\n/* harmony export */ __webpack_require__.d(__webpack_exports__, {\n/* harmony export */ \"default\": () => (/* binding */ TabContent)\n/* harmony export */ });\n/* harmony import */ var react__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! react */ \"./node_modules/react/index.js\");\n/* harmony import */ var react__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(react__WEBPACK_IMPORTED_MODULE_0__);\n/* harmony import */ var _styles_css__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./styles.css */ \"./js/src/react/components/TabContent/styles.css\");\n/* harmony import */ var _Preloader_Preloader__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ../Preloader/Preloader */ \"./js/src/react/components/Preloader/Preloader.js\");\n/* harmony import */ var _modules_Http_Ajax__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ../../modules/Http/Ajax */ \"./js/src/react/modules/Http/Ajax.js\");\n/* harmony import */ var _TabDefault_TabDefault__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(/*! ./TabDefault/TabDefault */ \"./js/src/react/components/TabContent/TabDefault/TabDefault.js\");\nfunction _regenerator() { /*! regenerator-runtime -- Copyright (c) 2014-present, Facebook, Inc. -- license (MIT): https://github.com/babel/babel/blob/main/packages/babel-helpers/LICENSE */ var e, t, r = \"function\" == typeof Symbol ? Symbol : {}, n = r.iterator || \"@@iterator\", o = r.toStringTag || \"@@toStringTag\"; function i(r, n, o, i) { var c = n && n.prototype instanceof Generator ? n : Generator, u = Object.create(c.prototype); return _regeneratorDefine2(u, \"_invoke\", function (r, n, o) { var i, c, u, f = 0, p = o || [], y = !1, G = { p: 0, n: 0, v: e, a: d, f: d.bind(e, 4), d: function d(t, r) { return i = t, c = 0, u = e, G.n = r, a; } }; function d(r, n) { for (c = r, u = n, t = 0; !y && f && !o && t < p.length; t++) { var o, i = p[t], d = G.p, l = i[2]; r > 3 ? (o = l === n) && (u = i[(c = i[4]) ? 5 : (c = 3, 3)], i[4] = i[5] = e) : i[0] <= d && ((o = r < 2 && d < i[1]) ? (c = 0, G.v = n, G.n = i[1]) : d < l && (o = r < 3 || i[0] > n || n > l) && (i[4] = r, i[5] = n, G.n = l, c = 0)); } if (o || r > 1) return a; throw y = !0, n; } return function (o, p, l) { if (f > 1) throw TypeError(\"Generator is already running\"); for (y && 1 === p && d(p, l), c = p, u = l; (t = c < 2 ? e : u) || !y;) { i || (c ? c < 3 ? (c > 1 && (G.n = -1), d(c, u)) : G.n = u : G.v = u); try { if (f = 2, i) { if (c || (o = \"next\"), t = i[o]) { if (!(t = t.call(i, u))) throw TypeError(\"iterator result is not an object\"); if (!t.done) return t; u = t.value, c < 2 && (c = 0); } else 1 === c && (t = i[\"return\"]) && t.call(i), c < 2 && (u = TypeError(\"The iterator does not provide a '\" + o + \"' method\"), c = 1); i = e; } else if ((t = (y = G.n < 0) ? u : r.call(n, G)) !== a) break; } catch (t) { i = e, c = 1, u = t; } finally { f = 1; } } return { value: t, done: y }; }; }(r, o, i), !0), u; } var a = {}; function Generator() {} function GeneratorFunction() {} function GeneratorFunctionPrototype() {} t = Object.getPrototypeOf; var c = [][n] ? t(t([][n]())) : (_regeneratorDefine2(t = {}, n, function () { return this; }), t), u = GeneratorFunctionPrototype.prototype = Generator.prototype = Object.create(c); function f(e) { return Object.setPrototypeOf ? Object.setPrototypeOf(e, GeneratorFunctionPrototype) : (e.__proto__ = GeneratorFunctionPrototype, _regeneratorDefine2(e, o, \"GeneratorFunction\")), e.prototype = Object.create(u), e; } return GeneratorFunction.prototype = GeneratorFunctionPrototype, _regeneratorDefine2(u, \"constructor\", GeneratorFunctionPrototype), _regeneratorDefine2(GeneratorFunctionPrototype, \"constructor\", GeneratorFunction), GeneratorFunction.displayName = \"GeneratorFunction\", _regeneratorDefine2(GeneratorFunctionPrototype, o, \"GeneratorFunction\"), _regeneratorDefine2(u), _regeneratorDefine2(u, o, \"Generator\"), _regeneratorDefine2(u, n, function () { return this; }), _regeneratorDefine2(u, \"toString\", function () { return \"[object Generator]\"; }), (_regenerator = function _regenerator() { return { w: i, m: f }; })(); }\nfunction _regeneratorDefine2(e, r, n, t) { var i = Object.defineProperty; try { i({}, \"\", {}); } catch (e) { i = 0; } _regeneratorDefine2 = function _regeneratorDefine(e, r, n, t) { function o(r, n) { _regeneratorDefine2(e, r, function (e) { return this._invoke(r, n, e); }); } r ? i ? i(e, r, { value: n, enumerable: !t, configurable: !t, writable: !t }) : e[r] = n : (o(\"next\", 0), o(\"throw\", 1), o(\"return\", 2)); }, _regeneratorDefine2(e, r, n, t); }\nfunction asyncGeneratorStep(n, t, e, r, o, a, c) { try { var i = n[a](c), u = i.value; } catch (n) { return void e(n); } i.done ? t(u) : Promise.resolve(u).then(r, o); }\nfunction _asyncToGenerator(n) { return function () { var t = this, e = arguments; return new Promise(function (r, o) { var a = n.apply(t, e); function _next(n) { asyncGeneratorStep(a, r, o, _next, _throw, \"next\", n); } function _throw(n) { asyncGeneratorStep(a, r, o, _next, _throw, \"throw\", n); } _next(void 0); }); }; }\nfunction _slicedToArray(r, e) { return _arrayWithHoles(r) || _iterableToArrayLimit(r, e) || _unsupportedIterableToArray(r, e) || _nonIterableRest(); }\nfunction _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.\"); }\nfunction _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; } }\nfunction _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; }\nfunction _iterableToArrayLimit(r, l) { var t = null == r ? null : \"undefined\" != typeof Symbol && r[Symbol.iterator] || r[\"@@iterator\"]; if (null != t) { var e, n, i, u, a = [], f = !0, o = !1; try { if (i = (t = t.call(r)).next, 0 === l) { if (Object(t) !== t) return; f = !1; } else for (; !(f = (e = i.call(t)).done) && (a.push(e.value), a.length !== l); f = !0); } catch (r) { o = !0, n = r; } finally { try { if (!f && null != t[\"return\"] && (u = t[\"return\"](), Object(u) !== u)) return; } finally { if (o) throw n; } } return a; } }\nfunction _arrayWithHoles(r) { if (Array.isArray(r)) return r; }\n;\n\n\n\n\nfunction TabContent(_ref) {\n var sectionId = _ref.sectionId,\n isActive = _ref.isActive,\n tabComponent = _ref.tabComponent;\n var _useState = (0,react__WEBPACK_IMPORTED_MODULE_0__.useState)(null),\n _useState2 = _slicedToArray(_useState, 2),\n sectionData = _useState2[0],\n setSectionData = _useState2[1];\n (0,react__WEBPACK_IMPORTED_MODULE_0__.useEffect)(function () {\n if (isActive && !sectionData) {\n // Legacy way to get tab content for these sectionId-s - spbc_settings__draw_elements\n if (sectionId === 'scanner' || sectionId === 'backups' || sectionId === 'summary' || sectionId === 'debug') {\n fetchContentOld();\n } else {\n fetchContent();\n }\n }\n }, [isActive, sectionData]);\n var fetchContent = /*#__PURE__*/function () {\n var _ref2 = _asyncToGenerator(/*#__PURE__*/_regenerator().m(function _callee() {\n var requestData, data, _t;\n return _regenerator().w(function (_context) {\n while (1) switch (_context.p = _context.n) {\n case 0:\n _context.p = 0;\n requestData = {\n action: 'spbct_get_tab_data',\n tab_name: sectionId\n };\n _context.n = 1;\n return (0,_modules_Http_Ajax__WEBPACK_IMPORTED_MODULE_3__.sendAjaxRequest)(requestData, true);\n case 1:\n data = _context.v;\n setSectionData(data);\n _context.n = 3;\n break;\n case 2:\n _context.p = 2;\n _t = _context.v;\n console.error('Error fetching content:', _t);\n case 3:\n return _context.a(2);\n }\n }, _callee, null, [[0, 2]]);\n }));\n return function fetchContent() {\n return _ref2.apply(this, arguments);\n };\n }();\n var fetchContentOld = /*#__PURE__*/function () {\n var _ref3 = _asyncToGenerator(/*#__PURE__*/_regenerator().m(function _callee2() {\n var requestData, data, _t2;\n return _regenerator().w(function (_context2) {\n while (1) switch (_context2.p = _context2.n) {\n case 0:\n _context2.p = 0;\n requestData = {\n action: 'spbc_settings__draw_elements',\n tab_name: sectionId\n };\n _context2.n = 1;\n return (0,_modules_Http_Ajax__WEBPACK_IMPORTED_MODULE_3__.sendAjaxRequest)(requestData);\n case 1:\n data = _context2.v;\n setSectionData(data);\n document.dispatchEvent(new CustomEvent('spbctTabLoaded', {\n bubbles: true,\n detail: {\n tab: sectionId\n }\n }));\n _context2.n = 3;\n break;\n case 2:\n _context2.p = 2;\n _t2 = _context2.v;\n console.error('Error fetching content:', _t2);\n case 3:\n return _context2.a(2);\n }\n }, _callee2, null, [[0, 2]]);\n }));\n return function fetchContentOld() {\n return _ref3.apply(this, arguments);\n };\n }();\n var isActiveClass = isActive ? 'spbc_tab_section--active' : '';\n var TabContentInner = tabComponent || _TabDefault_TabDefault__WEBPACK_IMPORTED_MODULE_4__[\"default\"];\n return /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement(\"div\", {\n id: 'spbc_tab-' + sectionId,\n className: 'spbc_tab spbc_tab-' + sectionId + ' ' + isActiveClass\n }, isActive && sectionData ? /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement(TabContentInner, {\n sectionId: sectionId,\n sectionData: sectionData,\n isActive: isActive\n }) : /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_Preloader_Preloader__WEBPACK_IMPORTED_MODULE_2__[\"default\"], null));\n}\n\n//# sourceURL=webpack:///./js/src/react/components/TabContent/TabContent.js?\n}"); /***/ }, diff --git a/js/src/react/components/TabContent/TabContent.js b/js/src/react/components/TabContent/TabContent.js index f0bd5ea3d..b365da7b9 100644 --- a/js/src/react/components/TabContent/TabContent.js +++ b/js/src/react/components/TabContent/TabContent.js @@ -9,15 +9,16 @@ export default function TabContent({sectionId, isActive, tabComponent}) { useEffect(() => { if (isActive && !sectionData) { + // Legacy way to get tab content for these sectionId-s - spbc_settings__draw_elements if ( - sectionId === 'critical_updates' || - sectionId === 'fswatcher' || - sectionId === 'traffic_control' || - sectionId === 'security_certified' + sectionId === 'scanner' || + sectionId === 'backups' || + sectionId === 'summary' || + sectionId === 'debug' ) { - fetchContent(); - } else { fetchContentOld(); + } else { + fetchContent(); } } }, [isActive, sectionData]); From c7098579f62fc25d0a96353a54e90519d106a4c7 Mon Sep 17 00:00:00 2001 From: Glomberg Date: Wed, 19 Aug 2026 13:45:54 +0300 Subject: [PATCH 2/6] Fix. React. `Security Log` tab moved to the legacy endpoint. --- js/public/spbct-react-bundle.js | 2 +- js/spbc-timeline-widget.min.js | 2 +- js/spbc-timeline-widget.min.js.map | 2 +- js/src/react/components/TabContent/TabContent.js | 1 + js/src/spbc-timeline-widget.js | 5 ----- 5 files changed, 4 insertions(+), 8 deletions(-) diff --git a/js/public/spbct-react-bundle.js b/js/public/spbct-react-bundle.js index e186bdab4..b91d0c9a4 100644 --- a/js/public/spbct-react-bundle.js +++ b/js/public/spbct-react-bundle.js @@ -376,7 +376,7 @@ eval("{__webpack_require__.r(__webpack_exports__);\n/* harmony export */ __webpa \**********************************************************/ (__unused_webpack_module, __webpack_exports__, __webpack_require__) { -eval("{__webpack_require__.r(__webpack_exports__);\n/* harmony export */ __webpack_require__.d(__webpack_exports__, {\n/* harmony export */ \"default\": () => (/* binding */ TabContent)\n/* harmony export */ });\n/* harmony import */ var react__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! react */ \"./node_modules/react/index.js\");\n/* harmony import */ var react__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(react__WEBPACK_IMPORTED_MODULE_0__);\n/* harmony import */ var _styles_css__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./styles.css */ \"./js/src/react/components/TabContent/styles.css\");\n/* harmony import */ var _Preloader_Preloader__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ../Preloader/Preloader */ \"./js/src/react/components/Preloader/Preloader.js\");\n/* harmony import */ var _modules_Http_Ajax__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ../../modules/Http/Ajax */ \"./js/src/react/modules/Http/Ajax.js\");\n/* harmony import */ var _TabDefault_TabDefault__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(/*! ./TabDefault/TabDefault */ \"./js/src/react/components/TabContent/TabDefault/TabDefault.js\");\nfunction _regenerator() { /*! regenerator-runtime -- Copyright (c) 2014-present, Facebook, Inc. -- license (MIT): https://github.com/babel/babel/blob/main/packages/babel-helpers/LICENSE */ var e, t, r = \"function\" == typeof Symbol ? Symbol : {}, n = r.iterator || \"@@iterator\", o = r.toStringTag || \"@@toStringTag\"; function i(r, n, o, i) { var c = n && n.prototype instanceof Generator ? n : Generator, u = Object.create(c.prototype); return _regeneratorDefine2(u, \"_invoke\", function (r, n, o) { var i, c, u, f = 0, p = o || [], y = !1, G = { p: 0, n: 0, v: e, a: d, f: d.bind(e, 4), d: function d(t, r) { return i = t, c = 0, u = e, G.n = r, a; } }; function d(r, n) { for (c = r, u = n, t = 0; !y && f && !o && t < p.length; t++) { var o, i = p[t], d = G.p, l = i[2]; r > 3 ? (o = l === n) && (u = i[(c = i[4]) ? 5 : (c = 3, 3)], i[4] = i[5] = e) : i[0] <= d && ((o = r < 2 && d < i[1]) ? (c = 0, G.v = n, G.n = i[1]) : d < l && (o = r < 3 || i[0] > n || n > l) && (i[4] = r, i[5] = n, G.n = l, c = 0)); } if (o || r > 1) return a; throw y = !0, n; } return function (o, p, l) { if (f > 1) throw TypeError(\"Generator is already running\"); for (y && 1 === p && d(p, l), c = p, u = l; (t = c < 2 ? e : u) || !y;) { i || (c ? c < 3 ? (c > 1 && (G.n = -1), d(c, u)) : G.n = u : G.v = u); try { if (f = 2, i) { if (c || (o = \"next\"), t = i[o]) { if (!(t = t.call(i, u))) throw TypeError(\"iterator result is not an object\"); if (!t.done) return t; u = t.value, c < 2 && (c = 0); } else 1 === c && (t = i[\"return\"]) && t.call(i), c < 2 && (u = TypeError(\"The iterator does not provide a '\" + o + \"' method\"), c = 1); i = e; } else if ((t = (y = G.n < 0) ? u : r.call(n, G)) !== a) break; } catch (t) { i = e, c = 1, u = t; } finally { f = 1; } } return { value: t, done: y }; }; }(r, o, i), !0), u; } var a = {}; function Generator() {} function GeneratorFunction() {} function GeneratorFunctionPrototype() {} t = Object.getPrototypeOf; var c = [][n] ? t(t([][n]())) : (_regeneratorDefine2(t = {}, n, function () { return this; }), t), u = GeneratorFunctionPrototype.prototype = Generator.prototype = Object.create(c); function f(e) { return Object.setPrototypeOf ? Object.setPrototypeOf(e, GeneratorFunctionPrototype) : (e.__proto__ = GeneratorFunctionPrototype, _regeneratorDefine2(e, o, \"GeneratorFunction\")), e.prototype = Object.create(u), e; } return GeneratorFunction.prototype = GeneratorFunctionPrototype, _regeneratorDefine2(u, \"constructor\", GeneratorFunctionPrototype), _regeneratorDefine2(GeneratorFunctionPrototype, \"constructor\", GeneratorFunction), GeneratorFunction.displayName = \"GeneratorFunction\", _regeneratorDefine2(GeneratorFunctionPrototype, o, \"GeneratorFunction\"), _regeneratorDefine2(u), _regeneratorDefine2(u, o, \"Generator\"), _regeneratorDefine2(u, n, function () { return this; }), _regeneratorDefine2(u, \"toString\", function () { return \"[object Generator]\"; }), (_regenerator = function _regenerator() { return { w: i, m: f }; })(); }\nfunction _regeneratorDefine2(e, r, n, t) { var i = Object.defineProperty; try { i({}, \"\", {}); } catch (e) { i = 0; } _regeneratorDefine2 = function _regeneratorDefine(e, r, n, t) { function o(r, n) { _regeneratorDefine2(e, r, function (e) { return this._invoke(r, n, e); }); } r ? i ? i(e, r, { value: n, enumerable: !t, configurable: !t, writable: !t }) : e[r] = n : (o(\"next\", 0), o(\"throw\", 1), o(\"return\", 2)); }, _regeneratorDefine2(e, r, n, t); }\nfunction asyncGeneratorStep(n, t, e, r, o, a, c) { try { var i = n[a](c), u = i.value; } catch (n) { return void e(n); } i.done ? t(u) : Promise.resolve(u).then(r, o); }\nfunction _asyncToGenerator(n) { return function () { var t = this, e = arguments; return new Promise(function (r, o) { var a = n.apply(t, e); function _next(n) { asyncGeneratorStep(a, r, o, _next, _throw, \"next\", n); } function _throw(n) { asyncGeneratorStep(a, r, o, _next, _throw, \"throw\", n); } _next(void 0); }); }; }\nfunction _slicedToArray(r, e) { return _arrayWithHoles(r) || _iterableToArrayLimit(r, e) || _unsupportedIterableToArray(r, e) || _nonIterableRest(); }\nfunction _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.\"); }\nfunction _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; } }\nfunction _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; }\nfunction _iterableToArrayLimit(r, l) { var t = null == r ? null : \"undefined\" != typeof Symbol && r[Symbol.iterator] || r[\"@@iterator\"]; if (null != t) { var e, n, i, u, a = [], f = !0, o = !1; try { if (i = (t = t.call(r)).next, 0 === l) { if (Object(t) !== t) return; f = !1; } else for (; !(f = (e = i.call(t)).done) && (a.push(e.value), a.length !== l); f = !0); } catch (r) { o = !0, n = r; } finally { try { if (!f && null != t[\"return\"] && (u = t[\"return\"](), Object(u) !== u)) return; } finally { if (o) throw n; } } return a; } }\nfunction _arrayWithHoles(r) { if (Array.isArray(r)) return r; }\n;\n\n\n\n\nfunction TabContent(_ref) {\n var sectionId = _ref.sectionId,\n isActive = _ref.isActive,\n tabComponent = _ref.tabComponent;\n var _useState = (0,react__WEBPACK_IMPORTED_MODULE_0__.useState)(null),\n _useState2 = _slicedToArray(_useState, 2),\n sectionData = _useState2[0],\n setSectionData = _useState2[1];\n (0,react__WEBPACK_IMPORTED_MODULE_0__.useEffect)(function () {\n if (isActive && !sectionData) {\n // Legacy way to get tab content for these sectionId-s - spbc_settings__draw_elements\n if (sectionId === 'scanner' || sectionId === 'backups' || sectionId === 'summary' || sectionId === 'debug') {\n fetchContentOld();\n } else {\n fetchContent();\n }\n }\n }, [isActive, sectionData]);\n var fetchContent = /*#__PURE__*/function () {\n var _ref2 = _asyncToGenerator(/*#__PURE__*/_regenerator().m(function _callee() {\n var requestData, data, _t;\n return _regenerator().w(function (_context) {\n while (1) switch (_context.p = _context.n) {\n case 0:\n _context.p = 0;\n requestData = {\n action: 'spbct_get_tab_data',\n tab_name: sectionId\n };\n _context.n = 1;\n return (0,_modules_Http_Ajax__WEBPACK_IMPORTED_MODULE_3__.sendAjaxRequest)(requestData, true);\n case 1:\n data = _context.v;\n setSectionData(data);\n _context.n = 3;\n break;\n case 2:\n _context.p = 2;\n _t = _context.v;\n console.error('Error fetching content:', _t);\n case 3:\n return _context.a(2);\n }\n }, _callee, null, [[0, 2]]);\n }));\n return function fetchContent() {\n return _ref2.apply(this, arguments);\n };\n }();\n var fetchContentOld = /*#__PURE__*/function () {\n var _ref3 = _asyncToGenerator(/*#__PURE__*/_regenerator().m(function _callee2() {\n var requestData, data, _t2;\n return _regenerator().w(function (_context2) {\n while (1) switch (_context2.p = _context2.n) {\n case 0:\n _context2.p = 0;\n requestData = {\n action: 'spbc_settings__draw_elements',\n tab_name: sectionId\n };\n _context2.n = 1;\n return (0,_modules_Http_Ajax__WEBPACK_IMPORTED_MODULE_3__.sendAjaxRequest)(requestData);\n case 1:\n data = _context2.v;\n setSectionData(data);\n document.dispatchEvent(new CustomEvent('spbctTabLoaded', {\n bubbles: true,\n detail: {\n tab: sectionId\n }\n }));\n _context2.n = 3;\n break;\n case 2:\n _context2.p = 2;\n _t2 = _context2.v;\n console.error('Error fetching content:', _t2);\n case 3:\n return _context2.a(2);\n }\n }, _callee2, null, [[0, 2]]);\n }));\n return function fetchContentOld() {\n return _ref3.apply(this, arguments);\n };\n }();\n var isActiveClass = isActive ? 'spbc_tab_section--active' : '';\n var TabContentInner = tabComponent || _TabDefault_TabDefault__WEBPACK_IMPORTED_MODULE_4__[\"default\"];\n return /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement(\"div\", {\n id: 'spbc_tab-' + sectionId,\n className: 'spbc_tab spbc_tab-' + sectionId + ' ' + isActiveClass\n }, isActive && sectionData ? /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement(TabContentInner, {\n sectionId: sectionId,\n sectionData: sectionData,\n isActive: isActive\n }) : /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_Preloader_Preloader__WEBPACK_IMPORTED_MODULE_2__[\"default\"], null));\n}\n\n//# sourceURL=webpack:///./js/src/react/components/TabContent/TabContent.js?\n}"); +eval("{__webpack_require__.r(__webpack_exports__);\n/* harmony export */ __webpack_require__.d(__webpack_exports__, {\n/* harmony export */ \"default\": () => (/* binding */ TabContent)\n/* harmony export */ });\n/* harmony import */ var react__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! react */ \"./node_modules/react/index.js\");\n/* harmony import */ var react__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(react__WEBPACK_IMPORTED_MODULE_0__);\n/* harmony import */ var _styles_css__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./styles.css */ \"./js/src/react/components/TabContent/styles.css\");\n/* harmony import */ var _Preloader_Preloader__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ../Preloader/Preloader */ \"./js/src/react/components/Preloader/Preloader.js\");\n/* harmony import */ var _modules_Http_Ajax__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ../../modules/Http/Ajax */ \"./js/src/react/modules/Http/Ajax.js\");\n/* harmony import */ var _TabDefault_TabDefault__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(/*! ./TabDefault/TabDefault */ \"./js/src/react/components/TabContent/TabDefault/TabDefault.js\");\nfunction _regenerator() { /*! regenerator-runtime -- Copyright (c) 2014-present, Facebook, Inc. -- license (MIT): https://github.com/babel/babel/blob/main/packages/babel-helpers/LICENSE */ var e, t, r = \"function\" == typeof Symbol ? Symbol : {}, n = r.iterator || \"@@iterator\", o = r.toStringTag || \"@@toStringTag\"; function i(r, n, o, i) { var c = n && n.prototype instanceof Generator ? n : Generator, u = Object.create(c.prototype); return _regeneratorDefine2(u, \"_invoke\", function (r, n, o) { var i, c, u, f = 0, p = o || [], y = !1, G = { p: 0, n: 0, v: e, a: d, f: d.bind(e, 4), d: function d(t, r) { return i = t, c = 0, u = e, G.n = r, a; } }; function d(r, n) { for (c = r, u = n, t = 0; !y && f && !o && t < p.length; t++) { var o, i = p[t], d = G.p, l = i[2]; r > 3 ? (o = l === n) && (u = i[(c = i[4]) ? 5 : (c = 3, 3)], i[4] = i[5] = e) : i[0] <= d && ((o = r < 2 && d < i[1]) ? (c = 0, G.v = n, G.n = i[1]) : d < l && (o = r < 3 || i[0] > n || n > l) && (i[4] = r, i[5] = n, G.n = l, c = 0)); } if (o || r > 1) return a; throw y = !0, n; } return function (o, p, l) { if (f > 1) throw TypeError(\"Generator is already running\"); for (y && 1 === p && d(p, l), c = p, u = l; (t = c < 2 ? e : u) || !y;) { i || (c ? c < 3 ? (c > 1 && (G.n = -1), d(c, u)) : G.n = u : G.v = u); try { if (f = 2, i) { if (c || (o = \"next\"), t = i[o]) { if (!(t = t.call(i, u))) throw TypeError(\"iterator result is not an object\"); if (!t.done) return t; u = t.value, c < 2 && (c = 0); } else 1 === c && (t = i[\"return\"]) && t.call(i), c < 2 && (u = TypeError(\"The iterator does not provide a '\" + o + \"' method\"), c = 1); i = e; } else if ((t = (y = G.n < 0) ? u : r.call(n, G)) !== a) break; } catch (t) { i = e, c = 1, u = t; } finally { f = 1; } } return { value: t, done: y }; }; }(r, o, i), !0), u; } var a = {}; function Generator() {} function GeneratorFunction() {} function GeneratorFunctionPrototype() {} t = Object.getPrototypeOf; var c = [][n] ? t(t([][n]())) : (_regeneratorDefine2(t = {}, n, function () { return this; }), t), u = GeneratorFunctionPrototype.prototype = Generator.prototype = Object.create(c); function f(e) { return Object.setPrototypeOf ? Object.setPrototypeOf(e, GeneratorFunctionPrototype) : (e.__proto__ = GeneratorFunctionPrototype, _regeneratorDefine2(e, o, \"GeneratorFunction\")), e.prototype = Object.create(u), e; } return GeneratorFunction.prototype = GeneratorFunctionPrototype, _regeneratorDefine2(u, \"constructor\", GeneratorFunctionPrototype), _regeneratorDefine2(GeneratorFunctionPrototype, \"constructor\", GeneratorFunction), GeneratorFunction.displayName = \"GeneratorFunction\", _regeneratorDefine2(GeneratorFunctionPrototype, o, \"GeneratorFunction\"), _regeneratorDefine2(u), _regeneratorDefine2(u, o, \"Generator\"), _regeneratorDefine2(u, n, function () { return this; }), _regeneratorDefine2(u, \"toString\", function () { return \"[object Generator]\"; }), (_regenerator = function _regenerator() { return { w: i, m: f }; })(); }\nfunction _regeneratorDefine2(e, r, n, t) { var i = Object.defineProperty; try { i({}, \"\", {}); } catch (e) { i = 0; } _regeneratorDefine2 = function _regeneratorDefine(e, r, n, t) { function o(r, n) { _regeneratorDefine2(e, r, function (e) { return this._invoke(r, n, e); }); } r ? i ? i(e, r, { value: n, enumerable: !t, configurable: !t, writable: !t }) : e[r] = n : (o(\"next\", 0), o(\"throw\", 1), o(\"return\", 2)); }, _regeneratorDefine2(e, r, n, t); }\nfunction asyncGeneratorStep(n, t, e, r, o, a, c) { try { var i = n[a](c), u = i.value; } catch (n) { return void e(n); } i.done ? t(u) : Promise.resolve(u).then(r, o); }\nfunction _asyncToGenerator(n) { return function () { var t = this, e = arguments; return new Promise(function (r, o) { var a = n.apply(t, e); function _next(n) { asyncGeneratorStep(a, r, o, _next, _throw, \"next\", n); } function _throw(n) { asyncGeneratorStep(a, r, o, _next, _throw, \"throw\", n); } _next(void 0); }); }; }\nfunction _slicedToArray(r, e) { return _arrayWithHoles(r) || _iterableToArrayLimit(r, e) || _unsupportedIterableToArray(r, e) || _nonIterableRest(); }\nfunction _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.\"); }\nfunction _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; } }\nfunction _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; }\nfunction _iterableToArrayLimit(r, l) { var t = null == r ? null : \"undefined\" != typeof Symbol && r[Symbol.iterator] || r[\"@@iterator\"]; if (null != t) { var e, n, i, u, a = [], f = !0, o = !1; try { if (i = (t = t.call(r)).next, 0 === l) { if (Object(t) !== t) return; f = !1; } else for (; !(f = (e = i.call(t)).done) && (a.push(e.value), a.length !== l); f = !0); } catch (r) { o = !0, n = r; } finally { try { if (!f && null != t[\"return\"] && (u = t[\"return\"](), Object(u) !== u)) return; } finally { if (o) throw n; } } return a; } }\nfunction _arrayWithHoles(r) { if (Array.isArray(r)) return r; }\n;\n\n\n\n\nfunction TabContent(_ref) {\n var sectionId = _ref.sectionId,\n isActive = _ref.isActive,\n tabComponent = _ref.tabComponent;\n var _useState = (0,react__WEBPACK_IMPORTED_MODULE_0__.useState)(null),\n _useState2 = _slicedToArray(_useState, 2),\n sectionData = _useState2[0],\n setSectionData = _useState2[1];\n (0,react__WEBPACK_IMPORTED_MODULE_0__.useEffect)(function () {\n if (isActive && !sectionData) {\n // Legacy way to get tab content for these sectionId-s - spbc_settings__draw_elements\n if (sectionId === 'scanner' || sectionId === 'backups' || sectionId === 'security_log' || sectionId === 'summary' || sectionId === 'debug') {\n fetchContentOld();\n } else {\n fetchContent();\n }\n }\n }, [isActive, sectionData]);\n var fetchContent = /*#__PURE__*/function () {\n var _ref2 = _asyncToGenerator(/*#__PURE__*/_regenerator().m(function _callee() {\n var requestData, data, _t;\n return _regenerator().w(function (_context) {\n while (1) switch (_context.p = _context.n) {\n case 0:\n _context.p = 0;\n requestData = {\n action: 'spbct_get_tab_data',\n tab_name: sectionId\n };\n _context.n = 1;\n return (0,_modules_Http_Ajax__WEBPACK_IMPORTED_MODULE_3__.sendAjaxRequest)(requestData, true);\n case 1:\n data = _context.v;\n setSectionData(data);\n _context.n = 3;\n break;\n case 2:\n _context.p = 2;\n _t = _context.v;\n console.error('Error fetching content:', _t);\n case 3:\n return _context.a(2);\n }\n }, _callee, null, [[0, 2]]);\n }));\n return function fetchContent() {\n return _ref2.apply(this, arguments);\n };\n }();\n var fetchContentOld = /*#__PURE__*/function () {\n var _ref3 = _asyncToGenerator(/*#__PURE__*/_regenerator().m(function _callee2() {\n var requestData, data, _t2;\n return _regenerator().w(function (_context2) {\n while (1) switch (_context2.p = _context2.n) {\n case 0:\n _context2.p = 0;\n requestData = {\n action: 'spbc_settings__draw_elements',\n tab_name: sectionId\n };\n _context2.n = 1;\n return (0,_modules_Http_Ajax__WEBPACK_IMPORTED_MODULE_3__.sendAjaxRequest)(requestData);\n case 1:\n data = _context2.v;\n setSectionData(data);\n document.dispatchEvent(new CustomEvent('spbctTabLoaded', {\n bubbles: true,\n detail: {\n tab: sectionId\n }\n }));\n _context2.n = 3;\n break;\n case 2:\n _context2.p = 2;\n _t2 = _context2.v;\n console.error('Error fetching content:', _t2);\n case 3:\n return _context2.a(2);\n }\n }, _callee2, null, [[0, 2]]);\n }));\n return function fetchContentOld() {\n return _ref3.apply(this, arguments);\n };\n }();\n var isActiveClass = isActive ? 'spbc_tab_section--active' : '';\n var TabContentInner = tabComponent || _TabDefault_TabDefault__WEBPACK_IMPORTED_MODULE_4__[\"default\"];\n return /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement(\"div\", {\n id: 'spbc_tab-' + sectionId,\n className: 'spbc_tab spbc_tab-' + sectionId + ' ' + isActiveClass\n }, isActive && sectionData ? /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement(TabContentInner, {\n sectionId: sectionId,\n sectionData: sectionData,\n isActive: isActive\n }) : /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_Preloader_Preloader__WEBPACK_IMPORTED_MODULE_2__[\"default\"], null));\n}\n\n//# sourceURL=webpack:///./js/src/react/components/TabContent/TabContent.js?\n}"); /***/ }, diff --git a/js/spbc-timeline-widget.min.js b/js/spbc-timeline-widget.min.js index ecdb14c2c..5200217d4 100644 --- a/js/spbc-timeline-widget.min.js +++ b/js/spbc-timeline-widget.min.js @@ -1,4 +1,4 @@ -class SpbcTimelineWidget{static markerRadius=4;static hoursRangeMax=24;static axisStepsCount=6;static userActionsUpstep=24;static legendCircleX=-120;static legendTextX=-105;static userLegendMaxRows=2;static userLegendRowHeight=18;static userLegendItemGap=15;static userLegendAxisOffset=60;constructor(){this.now=new Date,this.currentTimestamp=Math.floor(this.now.getTime()/1e3),this.eventsData=null,this.timelineMarkersData=[],this.timelineKnownEventsData=[],this.timelineEventTooltip=null,this.timelineStartTimestamp=this.currentTimestamp-3600*SpbcTimelineWidget.hoursRangeMax,this.usersCount=0,this.summaryEventsCount=0,this.userLegendExpanded=!1,this.selectedUserValue="most_active__action"}render(){var e;"undefined"!=typeof spbcTimelineWidgetData&&(this.timelineEventTooltip=document.getElementById("spbc-timeline-tooltip"),this.eventsData=spbcTimelineWidgetData.users_actions,spbcTimelineWidgetData.current_timestamp_local&&(this.currentTimestamp=Number(spbcTimelineWidgetData.current_timestamp_local)),this.timelineStartTimestamp=Number(spbcTimelineWidgetData.first_event_timestamp),this.timelineKnownEventsData=spbcTimelineWidgetData.known_events_data,this.timelineMarkersData=this.generateMarkersData(spbcTimelineWidgetData.found_types),(e=document.querySelector(".spbc_tab_section--active"))&&"spbc_tab-security_log"===e.id&&this.initTimeline(),window.addEventListener("spbctTabLoaded",e=>{"security_log"===e.detail.tab&&this.initTimeline()}),jQuery("#spbc-timeline-tab").on("click",e=>{e.hasOwnProperty("originalEvent")&&("1"!==localStorage.getItem("spbc_timeline_closed")?localStorage.setItem("spbc_timeline_closed","1"):localStorage.setItem("spbc_timeline_closed","0"))}),window.addEventListener("resize",()=>{this.selectedUserValue&&(this.userLegendExpanded=!1,"most_active__action"===this.selectedUserValue?this.drawUserTimelineMostActive():this.drawUserTimeline(this.selectedUserValue))}))}initTimeline(){let i=document.getElementById("spbc-timeline-user-select-wrapper");var e=document.getElementById("spbc-timeline-user-select-trigger");let s=document.getElementById("spbc-timeline-user-select-dropdown"),n=document.getElementById("spbc-timeline-user-select-value");var t,r,a,l=[];for(t in this.usersCount=0,this.summaryEventsCount=0,this.eventsData)this.eventsData.hasOwnProperty(t)&&(r=this.eventsData[t],0<(a=(Array.isArray(r)?r:r.events||[]).filter(e=>e.time>=this.timelineStartTimestamp&&e.time<=this.currentTimestamp)).length)&&(this.usersCount++,this.summaryEventsCount+=a.length,l.push({user:t,role:r.role||"unknown",eventsCount:a.length}));s.innerHTML="";let c=(e,t)=>{var i=document.createElement("div");return i.className="spbc-timeline-select-option",i.dataset.value=t,i.textContent=e,i};var o=c("Most Active Users","most_active__action"),o=(o.classList.add("selected"),s.appendChild(o),l.forEach(e=>{var t=e.user+` (${e.role}) (${e.eventsCount} events)`;s.appendChild(c(t,e.user))}),e.addEventListener("click",e=>{e.stopPropagation(),i.classList.toggle("open")}),document.addEventListener("click",e=>{i.contains(e.target)||i.classList.remove("open")}),s.addEventListener("click",e=>{var t,e=e.target.closest(".spbc-timeline-select-option");e&&(t=e.dataset.value,this.selectedUserValue=t,this.userLegendExpanded=!1,n.textContent=e.textContent,s.querySelectorAll(".spbc-timeline-select-option").forEach(e=>{e.classList.remove("selected")}),e.classList.add("selected"),i.classList.remove("open"),"most_active__action"===t?(this.drawUserTimelineMostActive(),(e=new URL(window.location)).searchParams.delete("users"),window.history.pushState({},"",e)):(this.drawUserTimeline(t),(e=new URL(window.location)).searchParams.set("users",t),window.history.pushState({},"",e)),updateSecurityLogTable())}),0{"security_log"===e.detail.tab&&(console.log(e.detail.tab),this.initTimeline())}),jQuery("#spbc-timeline-tab").on("click",e=>{e.hasOwnProperty("originalEvent")&&("1"!==localStorage.getItem("spbc_timeline_closed")?localStorage.setItem("spbc_timeline_closed","1"):localStorage.setItem("spbc_timeline_closed","0"))}),window.addEventListener("resize",()=>{this.selectedUserValue&&(this.userLegendExpanded=!1,"most_active__action"===this.selectedUserValue?this.drawUserTimelineMostActive():this.drawUserTimeline(this.selectedUserValue))}))}initTimeline(){let i=document.getElementById("spbc-timeline-user-select-wrapper");var e=document.getElementById("spbc-timeline-user-select-trigger");let s=document.getElementById("spbc-timeline-user-select-dropdown"),n=document.getElementById("spbc-timeline-user-select-value");var t,r,a,l=[];for(t in this.usersCount=0,this.summaryEventsCount=0,this.eventsData)this.eventsData.hasOwnProperty(t)&&(r=this.eventsData[t],0<(a=(Array.isArray(r)?r:r.events||[]).filter(e=>e.time>=this.timelineStartTimestamp&&e.time<=this.currentTimestamp)).length)&&(this.usersCount++,this.summaryEventsCount+=a.length,l.push({user:t,role:r.role||"unknown",eventsCount:a.length}));s.innerHTML="";let c=(e,t)=>{var i=document.createElement("div");return i.className="spbc-timeline-select-option",i.dataset.value=t,i.textContent=e,i};var o=c("Most Active Users","most_active__action"),o=(o.classList.add("selected"),s.appendChild(o),l.forEach(e=>{var t=e.user+` (${e.role}) (${e.eventsCount} events)`;s.appendChild(c(t,e.user))}),e.addEventListener("click",e=>{e.stopPropagation(),i.classList.toggle("open")}),document.addEventListener("click",e=>{i.contains(e.target)||i.classList.remove("open")}),s.addEventListener("click",e=>{var t,e=e.target.closest(".spbc-timeline-select-option");e&&(t=e.dataset.value,this.selectedUserValue=t,this.userLegendExpanded=!1,n.textContent=e.textContent,s.querySelectorAll(".spbc-timeline-select-option").forEach(e=>{e.classList.remove("selected")}),e.classList.add("selected"),i.classList.remove("open"),"most_active__action"===t?(this.drawUserTimelineMostActive(),(e=new URL(window.location)).searchParams.delete("users"),window.history.pushState({},"",e)):(this.drawUserTimeline(t),(e=new URL(window.location)).searchParams.set("users",t),window.history.pushState({},"",e)),updateSecurityLogTable())}),0{e.classList.remove("selected"),e.dataset.value===t&&(e.classList.add("selected"),i.textContent=e.textContent)})),this.drawUserTimeline(t);e=new URL(window.location);e.searchParams.set("users",t),window.history.pushState({},"",e),updateSecurityLogTable()}getMostActiveUsers(e){let t=this.currentTimestamp-86400;var i,s,n,r=[];for(i in e)e.hasOwnProperty(i)&&(s=e[i],0<(n=(Array.isArray(s)?s:s.events||[]).filter(e=>e.time>=t)).length)&&r.push({user:i,events:n,count:n.length,role:s.role||"unknown"});return r.sort((e,t)=>t.count-e.count),r.slice(0,15)}groupByHour(i,e){let s={};return e.forEach(e=>{var t=3600*Math.floor(e.time/3600);s[t]||(s[t]={timestamp:1800+t,user:i,count:0,byType:{}}),s[t].count++,s[t].byType[e.event]=(s[t].byType[e.event]||0)+1}),Object.values(s)}drawUserTimeline(n){var e=document.getElementById("spbc-timeline-svg"),t=document.getElementById("spbc-timeline-no-data");e.innerHTML="",t.style.display="none";let i=0;5e.time>=this.timelineStartTimestamp&&e.time<=this.currentTimestamp);if(0===r.length)t.style.display="block";else{let s=this.currentTimestamp-this.timelineStartTimestamp;var p=spbcTimelineWidgetData.found_types.length;for(let e=0;e{var t=(e.time-this.timelineStartTimestamp)/s*d,i=m+this.timelineMarkersData[e.event].upstep;u.appendChild(this.createEventElement(e,t,i,n))})}}measureTextWidth(e,t,i="spbc-timeline-legend-text"){var s=document.createElementNS("http://www.w3.org/2000/svg","text"),i=(s.setAttribute("class",i),s.textContent=t,s.style.visibility="hidden",e.appendChild(s),s.getComputedTextLength());return e.removeChild(s),i}calculateUserLegendRowsCount(t,i,s,n=null){var r=SpbcTimelineWidget.userLegendItemGap,a=SpbcTimelineWidget.markerRadius,l=this.measureTextWidth(t,"and 99 more")+r;let c=0,o=0;for(let e=0;em&&0=n)return n;c+=d}return o+1}drawUserTimelineMostActive(){var n=document.getElementById("spbc-timeline-svg"),r=document.getElementById("spbc-timeline-no-data"),a=(n.innerHTML="",r.style.display="none",this.getMostActiveUsers(this.eventsData));if(0===a.length)r.style.display="block";else{let e=0,i=(5{s[e.user]=i[(t+1)%i.length]});r=n.parentElement.clientWidth;const v=10+e,w=40,y=40,T=140;let t=r-T-w;var l=SpbcTimelineWidget.userLegendRowHeight,m=this.userLegendExpanded?null:SpbcTimelineWidget.userLegendMaxRows,m=this.calculateUserLegendRowsCount(n,a,t,m)*l,l=150+e+SpbcTimelineWidget.userLegendAxisOffset,m=l+m;let c=(l-v-y)/2,o=(n.setAttribute("width",r),n.setAttribute("height",m),document.createElementNS("http://www.w3.org/2000/svg","g")),d=(o.setAttribute("transform",`translate(${T}, ${v+30})`),n.appendChild(o),this.drawTimeAxis(o,t,10+c,!0),this.currentTimestamp-this.timelineStartTimestamp);var u=spbcTimelineWidgetData.found_types.length;for(let e=0;e{let l=s[a.user];this.groupByHour(a.user,a.events).forEach(r=>{if(0!==r.count){let n=Math.max(0,Math.min((r.timestamp-this.timelineStartTimestamp)/d*t,t));Object.entries(r.byType).forEach(([e,t])=>{var i=c+this.timelineMarkersData[e].upstep,s=SpbcTimelineWidget.markerRadius+Math.min(2*Math.sqrt(t-1),8),e={event:e,time:r.timestamp,count:t};o.appendChild(this.createEventElement(e,n,i,a.user,s,!0,l))})}})})}}drawUserLegend(t,i,s,n,r,a){var l=SpbcTimelineWidget.userLegendRowHeight,c=SpbcTimelineWidget.userLegendItemGap,o=SpbcTimelineWidget.markerRadius,d=this.userLegendExpanded?null:SpbcTimelineWidget.userLegendMaxRows;let m=0,u=0,p=0,h=0;var g=this.measureTextWidth(t,"and 99 more")+c;for(let e=0;ey&&0=d)return void(0<(y=s.length-p)&&(w=`and ${y} more`,y=r+(d-1)*l,(T=document.createElementNS("http://www.w3.org/2000/svg","text")).setAttribute("class","spbc-timeline-legend-text spbc-timeline-more-link"),T.setAttribute("x",h),T.setAttribute("y",y),T.setAttribute("text-anchor","start"),T.setAttribute("dominant-baseline","middle"),T.textContent=w,T.style.cursor="pointer",T.style.fill="#0073aa",T.style.textDecoration="underline",T.addEventListener("click",()=>{this.userLegendExpanded=!0,this.drawUserTimelineMostActive()}),i.appendChild(T)));null!==d&&u===d-1&&(h=m+v);var y=r+u*l,w=m+o,T=m+2*o+6,A=document.createElementNS("http://www.w3.org/2000/svg","circle"),w=(A.setAttribute("cx",w),A.setAttribute("cy",y),A.setAttribute("r",o),A.style.fill=n[b.user],i.appendChild(A),document.createElementNS("http://www.w3.org/2000/svg","text"));w.setAttribute("class","spbc-timeline-legend-text"),w.setAttribute("x",T),w.setAttribute("y",y),w.setAttribute("text-anchor","start"),w.setAttribute("dominant-baseline","middle"),w.textContent=b.user,i.appendChild(w),m+=v,p++}}generateMarkersData(e){let t={},i=0,s="default";return e.forEach(e=>{this.timelineKnownEventsData[e]&&this.timelineKnownEventsData[e].color_scheme&&(s=this.timelineKnownEventsData[e].color_scheme),t[e]={color:s,upstep:i},i-=SpbcTimelineWidget.userActionsUpstep}),t}}function spbcTimelineWidgetRender(){var e=new SpbcTimelineWidget;jQuery(function(){jQuery("#spbc-timeline-accordion").accordion({heightStyle:"content",collapsible:!0,active:!1})}),e.render()} diff --git a/js/spbc-timeline-widget.min.js.map b/js/spbc-timeline-widget.min.js.map index 1c8477400..fc07f6169 100644 --- a/js/spbc-timeline-widget.min.js.map +++ b/js/spbc-timeline-widget.min.js.map @@ -1 +1 @@ -{"version":3,"file":"spbc-timeline-widget.min.js","sources":["spbc-timeline-widget.js"],"sourcesContent":["/**\n * Class to render a timeline widget for Security log\n */\nclass SpbcTimelineWidget {\n // Static constants\n static markerRadius = 4;\n static hoursRangeMax = 24;\n static axisStepsCount = 6;\n static userActionsUpstep = 24;\n static legendCircleX = -120;\n static legendTextX = -105;\n static userLegendMaxRows = 2;\n static userLegendRowHeight = 18;\n static userLegendItemGap = 15;\n static userLegendAxisOffset = 60; // Offset from axis to user legend\n\n /**\n * Constructor.\n */\n constructor() {\n // Current time properties\n this.now = new Date();\n this.currentTimestamp = Math.floor(this.now.getTime() / 1000);\n\n // Data properties\n this.eventsData = null;\n this.timelineMarkersData = [];\n this.timelineKnownEventsData = [];\n this.timelineEventTooltip = null;\n this.timelineStartTimestamp = this.currentTimestamp - SpbcTimelineWidget.hoursRangeMax * 3600;\n this.usersCount = 0;\n this.summaryEventsCount = 0;\n this.userLegendExpanded = false;\n this.selectedUserValue = 'most_active__action';\n }\n\n /**\n * Initialize and render the timeline widget\n */\n render() {\n if (typeof spbcTimelineWidgetData === 'undefined') {\n return;\n }\n\n this.timelineEventTooltip = document.getElementById('spbc-timeline-tooltip');\n this.eventsData = spbcTimelineWidgetData.users_actions;\n if (spbcTimelineWidgetData.current_timestamp_local) {\n this.currentTimestamp = Number(spbcTimelineWidgetData.current_timestamp_local);\n }\n this.timelineStartTimestamp = Number(spbcTimelineWidgetData.first_event_timestamp);\n this.timelineKnownEventsData = spbcTimelineWidgetData.known_events_data;\n this.timelineMarkersData = this.generateMarkersData(spbcTimelineWidgetData.found_types);\n\n const activeTab = document.querySelector('.spbc_tab_section--active');\n if (activeTab && activeTab.id === 'spbc_tab-security_log') {\n this.initTimeline();\n }\n\n // Initialize timeline when tab is loaded\n window.addEventListener('spbctTabLoaded', (event) => {\n if (event.detail.tab === 'security_log') {\n this.initTimeline();\n }\n });\n\n jQuery('#spbc-timeline-tab').on('click', (e)=> {\n if (e.hasOwnProperty('originalEvent')) {\n if (localStorage.getItem('spbc_timeline_closed') !== '1') {\n localStorage.setItem('spbc_timeline_closed', '1');\n } else {\n localStorage.setItem('spbc_timeline_closed', '0');\n }\n }\n });\n\n // Handle window resize\n window.addEventListener('resize', () => {\n if (!this.selectedUserValue) {\n return;\n }\n this.userLegendExpanded = false;\n if (this.selectedUserValue === 'most_active__action') {\n this.drawUserTimelineMostActive();\n } else {\n this.drawUserTimeline(this.selectedUserValue);\n }\n });\n }\n\n /**\n * Initialize timeline with user selection\n */\n initTimeline() {\n const wrapper = document.getElementById('spbc-timeline-user-select-wrapper');\n const trigger = document.getElementById('spbc-timeline-user-select-trigger');\n const dropdown = document.getElementById('spbc-timeline-user-select-dropdown');\n const valueDisplay = document.getElementById('spbc-timeline-user-select-value');\n const usersWithRecentEvents = [];\n\n // Calculate summary statistics for the last 24 hours only\n this.usersCount = 0;\n this.summaryEventsCount = 0;\n\n for (let key in this.eventsData) {\n if (this.eventsData.hasOwnProperty(key)) {\n const userData = this.eventsData[key];\n const eventsArray = Array.isArray(userData) ? userData : (userData.events || []);\n const recentEvents = eventsArray.filter((event) =>\n event.time >= this.timelineStartTimestamp &&\n event.time <= this.currentTimestamp,\n );\n\n if (recentEvents.length > 0) {\n this.usersCount++;\n this.summaryEventsCount += recentEvents.length;\n usersWithRecentEvents.push({\n user: key,\n role: userData.role || 'unknown',\n eventsCount: recentEvents.length,\n });\n }\n }\n }\n\n // Reset dropdown options\n dropdown.innerHTML = '';\n\n // Helper to create option element\n const createOption = (text, value) => {\n const option = document.createElement('div');\n option.className = 'spbc-timeline-select-option';\n option.dataset.value = value;\n option.textContent = text;\n return option;\n };\n\n // Add \"Most Active Users\" option\n const mostActiveOption = createOption('Most Active Users', 'most_active__action');\n mostActiveOption.classList.add('selected');\n dropdown.appendChild(mostActiveOption);\n\n // Populate user options with role and event count\n usersWithRecentEvents.forEach((userData) => {\n const displayText = `${userData.user} (${userData.role}) (${userData.eventsCount} events)`;\n dropdown.appendChild(createOption(displayText, userData.user));\n });\n\n // Toggle dropdown on trigger click\n trigger.addEventListener('click', (e) => {\n e.stopPropagation();\n wrapper.classList.toggle('open');\n });\n\n // Close dropdown on outside click\n document.addEventListener('click', (e) => {\n if (!wrapper.contains(e.target)) {\n wrapper.classList.remove('open');\n }\n });\n\n // Handle option selection\n dropdown.addEventListener('click', (e) => {\n const option = e.target.closest('.spbc-timeline-select-option');\n if (!option) return;\n\n const selectedValue = option.dataset.value;\n this.selectedUserValue = selectedValue;\n this.userLegendExpanded = false;\n\n // Update display and selection state\n valueDisplay.textContent = option.textContent;\n dropdown.querySelectorAll('.spbc-timeline-select-option').forEach((opt) => {\n opt.classList.remove('selected');\n });\n option.classList.add('selected');\n\n // Close dropdown\n wrapper.classList.remove('open');\n\n if (selectedValue === 'most_active__action') {\n this.drawUserTimelineMostActive();\n const url = new URL(window.location);\n url.searchParams.delete('users');\n window.history.pushState({}, '', url);\n } else {\n this.drawUserTimeline(selectedValue);\n const url = new URL(window.location);\n url.searchParams.set('users', selectedValue);\n window.history.pushState({}, '', url);\n }\n\n // Update security log table with selected user\n updateSecurityLogTable();\n });\n\n // Draw initial timeline or show message\n if (usersWithRecentEvents.length > 0) {\n this.drawUserTimelineMostActive();\n this.selectedUserValue = 'most_active__action';\n valueDisplay.textContent = 'Most Active Users';\n } else {\n document.querySelector('.spbc-timeline-container').style.display = 'none';\n document.querySelector('.spbc-timeline-controls').style.display = 'none';\n document.getElementById('spbc-timeline-no-data').style.display = 'block';\n }\n\n const activitySelector = jQuery('#spbc_timeline_activity_counters')[0];\n activitySelector.innerText = activitySelector.innerText\n .replace('%users_count%', this.usersCount.toString())\n .replace('%events_count%', this.summaryEventsCount.toString());\n\n const isWidgetClosedByUser = (\n localStorage.getItem('spbc_timeline_closed') === '1'\n );\n if (!isWidgetClosedByUser) {\n setTimeout(function() {\n jQuery('#spbc-timeline-tab').trigger('click');\n }, 200);\n }\n }\n\n /**\n * Draw time axis on the timeline\n * @param {SVGElement} container - SVG container element\n * @param {number} width - Drawing area width\n * @param {number} height - Drawing area height\n * @param {boolean} roundToHour - Whether to round ticks to full hours\n */\n drawTimeAxis(container, width, height, roundToHour = false) {\n const timeRange = this.currentTimestamp - this.timelineStartTimestamp;\n const axis = document.createElementNS('http://www.w3.org/2000/svg', 'line');\n height = height + 10;\n // Create main axis line\n axis.setAttribute('class', 'spbc-timeline-axis');\n axis.setAttribute('x1', '0');\n axis.setAttribute('y1', height);\n axis.setAttribute('x2', width);\n axis.setAttribute('y2', height);\n container.appendChild(axis);\n\n // Create axis ticks and labels\n if (roundToHour) {\n const firstHourTs = Math.ceil(this.timelineStartTimestamp / 3600) * 3600;\n for (let ts = firstHourTs; ts <= this.currentTimestamp; ts += 3600) {\n const x = ((ts - this.timelineStartTimestamp) / timeRange) * width;\n\n // Create tick mark\n const tick = document.createElementNS('http://www.w3.org/2000/svg', 'line');\n tick.setAttribute('class', 'spbc-timeline-axis');\n tick.setAttribute('x1', x);\n tick.setAttribute('y1', height);\n tick.setAttribute('x2', x);\n tick.setAttribute('y2', height + 5);\n container.appendChild(tick);\n\n const label = document.createElementNS('http://www.w3.org/2000/svg', 'text');\n label.setAttribute('class', 'spbc-timeline-axis-text');\n label.setAttribute('x', x);\n label.setAttribute('y', height + 20);\n const h = new Date(ts * 1000).getUTCHours();\n label.textContent = `${h.toString().padStart(2, '0')}:00`;\n container.appendChild(label);\n }\n } else {\n for (let i = 0; i <= SpbcTimelineWidget.axisStepsCount; i++) {\n const timestamp = this.timelineStartTimestamp + (i / SpbcTimelineWidget.axisStepsCount) * timeRange;\n const x = (i / SpbcTimelineWidget.axisStepsCount) * width;\n\n // Create tick mark\n const tick = document.createElementNS('http://www.w3.org/2000/svg', 'line');\n tick.setAttribute('class', 'spbc-timeline-axis');\n tick.setAttribute('x1', x);\n tick.setAttribute('y1', height);\n tick.setAttribute('x2', x);\n tick.setAttribute('y2', height + 5);\n container.appendChild(tick);\n\n // Create time label\n const label = document.createElementNS('http://www.w3.org/2000/svg', 'text');\n label.setAttribute('class', 'spbc-timeline-axis-text');\n label.setAttribute('x', x);\n label.setAttribute('y', height + 20);\n const date = new Date(timestamp * 1000);\n label.textContent =\n `\n ${date.getUTCHours().toString().padStart(2, '0')}:\n ${date.getUTCMinutes().toString().padStart(2, '0')}\n `;\n container.appendChild(label);\n }\n }\n }\n\n /**\n * Create SVG element for an event\n * @param {Object} event - Event data\n * @param {number} x - X coordinate\n * @param {number} y - Y coordinate\n * @param {string} user - User name\n * @param {number} radius - Marker radius\n * @param {boolean} isMostActive - Whether this is a synthetic marker for most active users view\n * @param {string} color - Optional color override for the marker\n * @return {SVGElement} Event group element\n */\n createEventElement(event, x, y, user, radius, isMostActive= false, color) {\n const group = document.createElementNS('http://www.w3.org/2000/svg', 'g');\n group.setAttribute('transform', `translate(${x}, ${y})`);\n group.setAttribute('data-event', JSON.stringify({...event, user, isMostActive}));\n\n // Create event marker\n const marker = document.createElementNS('http://www.w3.org/2000/svg', 'circle');\n marker.setAttribute('class', 'spbc-timeline-event-marker');\n marker.setAttribute('r', radius || SpbcTimelineWidget.markerRadius);\n marker.style.fill = color || this.timelineMarkersData[event.event].color;\n group.appendChild(marker);\n\n // Add event handlers\n group.addEventListener('mouseenter', this.showEventTooltip.bind(this));\n group.addEventListener('mouseleave', this.hideEventTooltip.bind(this));\n\n if (isMostActive) {\n group.addEventListener('click', this.handleEventClick.bind(this, user));\n }\n\n return group;\n }\n\n /**\n * Show event tooltip\n * @param {Event} event - Mouse event\n */\n showEventTooltip(event) {\n const eventData = JSON.parse(event.currentTarget.getAttribute('data-event'));\n const {user, event: eventName, time, isMostActive} = eventData;\n\n // Format date for display\n const formatDate = (timestamp) => {\n const date = new Date(timestamp * 1000);\n\n const months = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun',\n 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'];\n\n const month = months[date.getUTCMonth()];\n const day = date.getUTCDate();\n const dayWithZero = String(day).padStart(2, '0');\n const year = date.getUTCFullYear();\n\n const hours = date.getUTCHours().toString().padStart(2, '0');\n const minutes = date.getUTCMinutes().toString().padStart(2, '0');\n const seconds = date.getUTCSeconds().toString().padStart(2, '0');\n\n return `${month} ${dayWithZero} ${year} ${hours}:${minutes}:${seconds}`;\n };\n\n // Update tooltip content\n document.getElementById('spbc-timeline-tooltip--user').innerText = user;\n document.getElementById('spbc-timeline-tooltip--event').innerText =\n eventData.count > 1 ? `${eventName} (${eventData.count})` : eventName;\n\n const timeRow = document.getElementById('spbc-timeline-tooltip--time').parentElement;\n if (isMostActive) {\n timeRow.style.display = 'none';\n } else {\n timeRow.style.display = '';\n document.getElementById('spbc-timeline-tooltip--time').innerText = formatDate(time);\n }\n\n const tooltip= document.getElementById('spbc-timeline-tooltip');\n const fixedTop = event.offsetY + 35;\n const tooltipY = fixedTop.toString() + 'px';\n const screenWidth = window.innerWidth;\n const tooltipWidth = tooltip.offsetWidth ? tooltip.offsetWidth : 165;\n const tooltipEventPositionX = event.clientX;\n let tooltipX = tooltipEventPositionX - tooltipWidth;\n if (tooltipEventPositionX + (tooltipWidth * 2) > screenWidth) {\n tooltipX = tooltipEventPositionX - (tooltipWidth * 2);\n }\n\n tooltipX = tooltipX.toString() + 'px';\n tooltip.style.position = 'absolute';\n tooltip.style.top = tooltipY;\n tooltip.style.left = tooltipX;\n\n // Show tooltip\n this.timelineEventTooltip.classList.remove('spbc-timeline-hidden-element');\n jQuery('[data-event-type]').filter(function() {\n return jQuery(this).data('event-type') === eventName;\n }).addClass('spbc-timelime-highlight-on-tooltip');\n }\n\n /**\n * Hide event tooltip\n */\n hideEventTooltip() {\n this.timelineEventTooltip.classList.add('spbc-timeline-hidden-element');\n jQuery('[data-event-type]').removeClass('spbc-timelime-highlight-on-tooltip');\n }\n\n /**\n * Handle click on event marker - filter security log table by user\n * @param {string} user - User name\n * @param {Event} event - Click event\n */\n handleEventClick(user) {\n this.hideEventTooltip();\n\n // Update custom dropdown selector to show selected user\n const dropdown = document.getElementById('spbc-timeline-user-select-dropdown');\n const valueDisplay = document.getElementById('spbc-timeline-user-select-value');\n if (dropdown && valueDisplay) {\n this.selectedUserValue = user;\n // Find the option with matching value\n const options = dropdown.querySelectorAll('.spbc-timeline-select-option');\n options.forEach((opt) => {\n opt.classList.remove('selected');\n if (opt.dataset.value === user) {\n opt.classList.add('selected');\n valueDisplay.textContent = opt.textContent;\n }\n });\n }\n\n // Redraw timeline for the selected user\n this.drawUserTimeline(user);\n\n // Update URL with users parameter\n const url = new URL(window.location);\n url.searchParams.set('users', user);\n window.history.pushState({}, '', url);\n\n // Update security log table with selected user\n updateSecurityLogTable();\n }\n\n /**\n * Get most active users from last 24 hours (max 15 users)\n * @param {Object} eventsData - User events data\n * @return {Array} Array of user objects with their events\n */\n getMostActiveUsers(eventsData) {\n const currentTime = this.currentTimestamp;\n const twentyFourHoursAgo = currentTime - (24 * 3600);\n\n // Filter users with events in last 24 hours and count their events\n const usersWithRecentEvents = [];\n\n for (let user in eventsData) {\n if (eventsData.hasOwnProperty(user)) {\n const userData = eventsData[user];\n const eventsArray = Array.isArray(userData) ? userData : (userData.events || []);\n\n // Filter events from last 24 hours\n const recentEvents = eventsArray.filter((event) => event.time >= twentyFourHoursAgo);\n\n if (recentEvents.length > 0) {\n usersWithRecentEvents.push({\n user: user,\n events: recentEvents,\n count: recentEvents.length,\n role: userData.role || 'unknown',\n });\n }\n }\n }\n\n // Sort by event count (descending) and take top 15\n usersWithRecentEvents.sort((a, b) => b.count - a.count);\n\n return usersWithRecentEvents.slice(0, 15);\n }\n\n /**\n * Group events into a 24-element array (one slot per calendar hour).\n * @param {string} user\n * @param {Array} events - array of {event, time}\n * @return {Array} 24 items: {hour, user, count, byType}\n */\n groupByHour(user, events) {\n // Group events by actual hour slots (using real timestamps, not just hour of day)\n const slots = {};\n\n events.forEach((ev) => {\n // Round down to the start of the hour\n const hourStart = Math.floor(ev.time / 3600) * 3600;\n\n if (!slots[hourStart]) {\n slots[hourStart] = {\n timestamp: hourStart + 1800, // midpoint of the hour\n user: user,\n count: 0,\n byType: {},\n };\n }\n\n slots[hourStart].count++;\n slots[hourStart].byType[ev.event] = (slots[hourStart].byType[ev.event] || 0) + 1;\n });\n\n return Object.values(slots);\n }\n\n /**\n * Draw timeline for specific user\n * @param {string} user - Selected user name\n */\n drawUserTimeline(user) {\n const svg = document.getElementById('spbc-timeline-svg');\n const noDataMessage = document.getElementById('spbc-timeline-no-data');\n\n // Clear previous content\n svg.innerHTML = '';\n noDataMessage.style.display = 'none';\n\n let additionalEventsHeight = 0;\n if (spbcTimelineWidgetData.found_types.length > 5) {\n additionalEventsHeight = (spbcTimelineWidgetData.found_types.length - 5) * 12;\n }\n\n // Calculate dimensions\n const width = svg.parentElement.clientWidth;\n const height = 150 + additionalEventsHeight + SpbcTimelineWidget.userLegendAxisOffset;\n\n // Setup drawing area\n const margin = {top: 10 + additionalEventsHeight, right: 40, bottom: 40, left: 140};\n const contentWidth = width - margin.left - margin.right;\n const contentHeight = height - margin.top - margin.bottom;\n const baseLineY = contentHeight / 2;\n\n svg.setAttribute('width', width);\n svg.setAttribute('height', height);\n\n const g = document.createElementNS('http://www.w3.org/2000/svg', 'g');\n g.setAttribute('transform', `translate(${margin.left}, ${margin.top+30})`);\n svg.appendChild(g);\n\n // Calculate base positions\n\n\n // Draw axis\n this.drawTimeAxis(g, contentWidth, baseLineY + 10);\n\n // Filter user events\n const userData = this.eventsData[user];\n const userEvents = (Array.isArray(userData) ? userData : (userData.events || [])).filter((event) =>\n event.time >= this.timelineStartTimestamp &&\n event.time <= this.currentTimestamp,\n );\n\n // Handle no events case\n if (userEvents.length === 0) {\n noDataMessage.style.display = 'block';\n return;\n }\n\n const timeRange = this.currentTimestamp - this.timelineStartTimestamp;\n const uniqueEventsCount = spbcTimelineWidgetData.found_types.length;\n\n // Draw event lines and legends\n for (let i = 0; i < uniqueEventsCount; i++) {\n const userLineY = baseLineY - (SpbcTimelineWidget.userActionsUpstep * i);\n const eventType = spbcTimelineWidgetData.found_types[i];\n const color = this.timelineMarkersData[eventType].color;\n\n // Draw event line\n const userLine = document.createElementNS('http://www.w3.org/2000/svg', 'line');\n userLine.setAttribute('class', 'spbc-timeline-user-line');\n userLine.setAttribute('x1', '0');\n userLine.setAttribute('y1', userLineY);\n userLine.setAttribute('x2', contentWidth);\n userLine.setAttribute('y2', userLineY);\n userLine.setAttribute('data-event-type', eventType);\n g.appendChild(userLine);\n\n // Draw legend circle\n const legendCircle = document.createElementNS('http://www.w3.org/2000/svg', 'circle');\n legendCircle.setAttribute('class', 'spbc-timeline-legend-circle');\n legendCircle.setAttribute('cx', SpbcTimelineWidget.legendCircleX);\n legendCircle.setAttribute('cy', userLineY);\n legendCircle.setAttribute('r', SpbcTimelineWidget.markerRadius);\n legendCircle.style.fill = color;\n g.appendChild(legendCircle);\n\n // Draw legend text\n const legendText = document.createElementNS('http://www.w3.org/2000/svg', 'text');\n legendText.setAttribute('class', 'spbc-timeline-legend-text');\n legendText.setAttribute('x', SpbcTimelineWidget.legendTextX);\n legendText.setAttribute('y', userLineY);\n legendText.setAttribute('text-anchor', 'start');\n legendText.setAttribute('dominant-baseline', 'middle');\n legendText.setAttribute('data-event-type', eventType);\n legendText.textContent = eventType;\n g.appendChild(legendText);\n }\n\n // Draw events\n userEvents.forEach((userEvent) => {\n const x = ((userEvent.time - this.timelineStartTimestamp) / timeRange) * contentWidth;\n const eventY = baseLineY + this.timelineMarkersData[userEvent.event].upstep;\n g.appendChild(this.createEventElement(userEvent, x, eventY, user));\n });\n }\n\n /**\n * Measure text width in SVG\n * @param {SVGElement} svg - SVG container element\n * @param {string} text - Text to measure\n * @param {string} className - CSS class for text styling\n * @return {number} Text width in pixels\n */\n measureTextWidth(svg, text, className = 'spbc-timeline-legend-text') {\n const tempText = document.createElementNS('http://www.w3.org/2000/svg', 'text');\n tempText.setAttribute('class', className);\n tempText.textContent = text;\n tempText.style.visibility = 'hidden';\n svg.appendChild(tempText);\n const width = tempText.getComputedTextLength();\n svg.removeChild(tempText);\n return width;\n }\n\n /**\n * Calculate how many rows the user legend will occupy\n * @param {SVGElement} svg - SVG container element\n * @param {Array} users - Array of user data objects\n * @param {number} maxWidth - Maximum width for the legend area\n * @param {number|null} maxRows - Maximum rows limit (null for unlimited)\n * @return {number} Number of rows needed\n */\n calculateUserLegendRowsCount(svg, users, maxWidth, maxRows = null) {\n const itemGap = SpbcTimelineWidget.userLegendItemGap;\n const circleRadius = SpbcTimelineWidget.markerRadius;\n const circleTextGap = 6;\n const startX = 0;\n\n // Calculate \"and N more\" text width for reservation\n const moreTextTemplate = 'and 99 more';\n const moreTextWidth = this.measureTextWidth(svg, moreTextTemplate) + itemGap;\n\n let currentX = startX;\n let currentRow = 0;\n\n for (let i = 0; i < users.length; i++) {\n const userData = users[i];\n const textWidth = this.measureTextWidth(svg, userData.user);\n const itemWidth = circleRadius * 2 + circleTextGap + textWidth + itemGap;\n\n // Check if we need to reserve space for \"and N more\" on last allowed row\n const isLastRow = maxRows !== null && currentRow === maxRows - 1;\n const reserveForMore = isLastRow && (i < users.length - 1);\n const effectiveMaxWidth = reserveForMore ? maxWidth - moreTextWidth : maxWidth;\n\n if (currentX + itemWidth > effectiveMaxWidth && currentX > startX) {\n currentRow++;\n currentX = startX;\n\n // If we hit maxRows limit, stop counting\n if (maxRows !== null && currentRow >= maxRows) {\n return maxRows;\n }\n }\n\n currentX += itemWidth;\n }\n\n return currentRow + 1; // +1 because rows are 0-indexed\n }\n\n /**\n * Draw timeline for most active users\n */\n drawUserTimelineMostActive() {\n const svg = document.getElementById('spbc-timeline-svg');\n const noDataMessage = document.getElementById('spbc-timeline-no-data');\n\n // Clear previous content\n svg.innerHTML = '';\n noDataMessage.style.display = 'none';\n\n // Get most active users\n const mostActiveUsers = this.getMostActiveUsers(this.eventsData);\n\n if (mostActiveUsers.length === 0) {\n noDataMessage.style.display = 'block';\n return;\n }\n\n let additionalEventsHeight = 0;\n if (spbcTimelineWidgetData.found_types.length > 5) {\n additionalEventsHeight = (spbcTimelineWidgetData.found_types.length - 5) * 12;\n }\n\n // Pre-assign a color to each user\n const randomColors = [\n '#0284A8', '#E74C3C', '#2ECC71', '#F39C12', '#9B59B6',\n '#1ABC9C', '#E67E22', '#3498DB', '#E91E63', '#00BCD4',\n '#8BC34A', '#FF5722', '#607D8B', '#795548', '#673AB7',\n ];\n const userColors = {};\n mostActiveUsers.forEach((userData, i) => {\n userColors[userData.user] = randomColors[(i + 1) % randomColors.length];\n });\n\n // Calculate dimensions - need to know content width first for row calculation\n const width = svg.parentElement.clientWidth;\n const margin = {top: 10 + additionalEventsHeight, right: 40, bottom: 40, left: 140};\n const contentWidth = width - margin.left - margin.right;\n\n // Calculate user legend rows count based on expanded state\n const userLegendRowHeight = SpbcTimelineWidget.userLegendRowHeight;\n const maxRows = this.userLegendExpanded ? null : SpbcTimelineWidget.userLegendMaxRows;\n const actualRowsCount = this.calculateUserLegendRowsCount(svg, mostActiveUsers, contentWidth, maxRows);\n const userLegendHeight = actualRowsCount * userLegendRowHeight;\n\n // Calculate height: base height (like drawUserTimeline) + user legend space\n const baseHeight = 150 + additionalEventsHeight + SpbcTimelineWidget.userLegendAxisOffset;\n const height = baseHeight + userLegendHeight;\n\n // Setup drawing area - baseLineY calculated from baseHeight (same as drawUserTimeline)\n const baseContentHeight = baseHeight - margin.top - margin.bottom;\n const baseLineY = baseContentHeight / 2;\n\n svg.setAttribute('width', width);\n svg.setAttribute('height', height);\n\n const g = document.createElementNS('http://www.w3.org/2000/svg', 'g');\n g.setAttribute('transform', `translate(${margin.left}, ${margin.top+30})`);\n svg.appendChild(g);\n\n // Draw axis\n this.drawTimeAxis(g, contentWidth, baseLineY + 10, true);\n\n const timeRange = this.currentTimestamp - this.timelineStartTimestamp;\n const uniqueEventsCount = spbcTimelineWidgetData.found_types.length;\n\n // Draw event lines and legends\n for (let i = 0; i < uniqueEventsCount; i++) {\n const userLineY = baseLineY - (SpbcTimelineWidget.userActionsUpstep * i);\n const eventType = spbcTimelineWidgetData.found_types[i];\n const color = this.timelineMarkersData[eventType].color;\n\n // Draw event line\n const userLine = document.createElementNS('http://www.w3.org/2000/svg', 'line');\n userLine.setAttribute('class', 'spbc-timeline-user-line');\n userLine.setAttribute('x1', '0');\n userLine.setAttribute('y1', userLineY);\n userLine.setAttribute('x2', contentWidth);\n userLine.setAttribute('y2', userLineY);\n userLine.setAttribute('data-event-type', eventType);\n g.appendChild(userLine);\n\n // Draw legend circle\n const legendCircle = document.createElementNS('http://www.w3.org/2000/svg', 'circle');\n legendCircle.setAttribute('class', 'spbc-timeline-legend-circle');\n legendCircle.setAttribute('cx', SpbcTimelineWidget.legendCircleX);\n legendCircle.setAttribute('cy', userLineY);\n legendCircle.setAttribute('r', SpbcTimelineWidget.markerRadius);\n legendCircle.style.fill = color;\n g.appendChild(legendCircle);\n\n // Draw legend text\n const legendText = document.createElementNS('http://www.w3.org/2000/svg', 'text');\n legendText.setAttribute('class', 'spbc-timeline-legend-text');\n legendText.setAttribute('x', SpbcTimelineWidget.legendTextX);\n legendText.setAttribute('y', userLineY);\n legendText.setAttribute('text-anchor', 'start');\n legendText.setAttribute('dominant-baseline', 'middle');\n legendText.setAttribute('data-event-type', eventType);\n legendText.textContent = eventType;\n g.appendChild(legendText);\n }\n\n // Draw user color legend (below axis)\n const userLegendStartY = baseLineY + SpbcTimelineWidget.userLegendAxisOffset;\n this.drawUserLegend(svg, g, mostActiveUsers, userColors, userLegendStartY, contentWidth);\n\n // Draw events for all most active users, grouped by hour\n mostActiveUsers.forEach((userData) => {\n const eventColor = userColors[userData.user];\n\n const hourly = this.groupByHour(userData.user, userData.events);\n hourly.forEach((slot) => {\n if (slot.count === 0) return;\n const x = Math.max(\n 0,\n Math.min(\n ((slot.timestamp - this.timelineStartTimestamp) / timeRange) * contentWidth, contentWidth,\n ),\n );\n Object.entries(slot.byType).forEach(([eventType, cnt]) => {\n const eventY = baseLineY + this.timelineMarkersData[eventType].upstep;\n const radius = SpbcTimelineWidget.markerRadius + Math.min(Math.sqrt(cnt - 1) * 2, 8);\n const syntheticEvent = {event: eventType, time: slot.timestamp, count: cnt};\n g.appendChild(\n this.createEventElement(\n syntheticEvent,\n x,\n eventY,\n userData.user,\n radius,\n true,\n eventColor,\n ),\n );\n });\n });\n });\n }\n\n /**\n * Draw user legend with horizontal layout and row limit\n * @param {SVGElement} svg - SVG element\n * @param {SVGElement} g - Group element to append to\n * @param {Array} users - Array of user data objects\n * @param {Object} userColors - Map of user to color\n * @param {number} startY - Starting Y position\n * @param {number} maxWidth - Maximum width for the legend area\n */\n drawUserLegend(svg, g, users, userColors, startY, maxWidth) {\n const rowHeight = SpbcTimelineWidget.userLegendRowHeight;\n const itemGap = SpbcTimelineWidget.userLegendItemGap;\n const circleRadius = SpbcTimelineWidget.markerRadius;\n const circleTextGap = 6;\n const startX = 0;\n\n // Determine max rows: null for expanded (unlimited), or limit for collapsed\n const maxRows = this.userLegendExpanded ? null : SpbcTimelineWidget.userLegendMaxRows;\n\n // Horizontal layout with optional row limit\n let currentX = startX;\n let currentRow = 0;\n let visibleCount = 0;\n let lastRowEndX = startX; // Track end position on the last allowed row for \"and N more\" button\n\n // Calculate \"and N more\" text width for reservation\n const moreTextTemplate = 'and 99 more';\n const moreTextWidth = this.measureTextWidth(svg, moreTextTemplate) + itemGap;\n\n for (let i = 0; i < users.length; i++) {\n const userData = users[i];\n const textWidth = this.measureTextWidth(svg, userData.user);\n const itemWidth = circleRadius * 2 + circleTextGap + textWidth + itemGap;\n\n // Check if we need to wrap to next row\n const isLastRow = maxRows !== null && currentRow === maxRows - 1;\n const reserveForMore = isLastRow && (i < users.length - 1);\n const effectiveMaxWidth = reserveForMore ? maxWidth - moreTextWidth : maxWidth;\n\n if (currentX + itemWidth > effectiveMaxWidth && currentX > startX) {\n // Before moving to next row, save position if we're on the last allowed row\n if (isLastRow) {\n lastRowEndX = currentX;\n }\n\n currentRow++;\n currentX = startX;\n\n // Check if we exceeded max rows (only when maxRows is set)\n if (maxRows !== null && currentRow >= maxRows) {\n // Draw \"and N more\" link at the end of the last row (continuation of the line)\n const remainingCount = users.length - visibleCount;\n if (remainingCount > 0) {\n const moreText = `and ${remainingCount} more`;\n const ly = startY + (maxRows - 1) * rowHeight;\n\n const moreLink = document.createElementNS('http://www.w3.org/2000/svg', 'text');\n moreLink.setAttribute('class', 'spbc-timeline-legend-text spbc-timeline-more-link');\n moreLink.setAttribute('x', lastRowEndX);\n moreLink.setAttribute('y', ly);\n moreLink.setAttribute('text-anchor', 'start');\n moreLink.setAttribute('dominant-baseline', 'middle');\n moreLink.textContent = moreText;\n moreLink.style.cursor = 'pointer';\n moreLink.style.fill = '#0073aa';\n moreLink.style.textDecoration = 'underline';\n moreLink.addEventListener('click', () => {\n this.userLegendExpanded = true;\n this.drawUserTimelineMostActive();\n });\n g.appendChild(moreLink);\n }\n return;\n }\n }\n\n // Track position on the last allowed row\n if (maxRows !== null && currentRow === maxRows - 1) {\n lastRowEndX = currentX + itemWidth;\n }\n\n // Draw user legend item\n const ly = startY + currentRow * rowHeight;\n const cx = currentX + circleRadius;\n const tx = currentX + circleRadius * 2 + circleTextGap;\n\n const circle = document.createElementNS('http://www.w3.org/2000/svg', 'circle');\n circle.setAttribute('cx', cx);\n circle.setAttribute('cy', ly);\n circle.setAttribute('r', circleRadius);\n circle.style.fill = userColors[userData.user];\n g.appendChild(circle);\n\n const text = document.createElementNS('http://www.w3.org/2000/svg', 'text');\n text.setAttribute('class', 'spbc-timeline-legend-text');\n text.setAttribute('x', tx);\n text.setAttribute('y', ly);\n text.setAttribute('text-anchor', 'start');\n text.setAttribute('dominant-baseline', 'middle');\n text.textContent = userData.user;\n g.appendChild(text);\n\n currentX += itemWidth;\n visibleCount++;\n }\n }\n\n /*\n /!**\n * Updates the security log table based on selected user\n * @param {string} selectedUser - The selected user or 'most_active__action'\n *!/\n updateSecurityLogTable(selectedUser) {\n const data= {};\n const urlParams = new URLSearchParams(window.location.search);\n const usersParam = urlParams.get('users');\n if (usersParam) {\n data.users = usersParam;\n }\n spbcSendAJAXRequest(data, params, obj);\n }*/\n\n /**\n * Generate marker data for events\n * @param {Array} events - List of event types\n * @return {Object} Marker configuration data\n */\n generateMarkersData(events) {\n const colorMap = {};\n let upstep = 0;\n let colorScheme = 'default';\n\n events.forEach((userEvent) => {\n if (this.timelineKnownEventsData[userEvent] && this.timelineKnownEventsData[userEvent].color_scheme) {\n colorScheme = this.timelineKnownEventsData[userEvent].color_scheme;\n }\n colorMap[userEvent] = {\n color: colorScheme,\n upstep: upstep,\n };\n upstep -= SpbcTimelineWidget.userActionsUpstep;\n });\n\n return colorMap;\n }\n}\n\n/**\n * Wrapper\n */\nfunction spbcTimelineWidgetRender() { // eslint-disable-line no-unused-vars\n const timelineWidget = new SpbcTimelineWidget();\n jQuery( function() {\n jQuery('#spbc-timeline-accordion').accordion({\n heightStyle: 'content',\n collapsible: true,\n active: false,\n });\n } );\n timelineWidget.render();\n}\n\n\n"],"names":["SpbcTimelineWidget","markerRadius","hoursRangeMax","axisStepsCount","userActionsUpstep","legendCircleX","legendTextX","userLegendMaxRows","userLegendRowHeight","userLegendItemGap","userLegendAxisOffset","constructor","this","now","Date","currentTimestamp","Math","floor","getTime","eventsData","timelineMarkersData","timelineKnownEventsData","timelineEventTooltip","timelineStartTimestamp","usersCount","summaryEventsCount","userLegendExpanded","selectedUserValue","render","activeTab","spbcTimelineWidgetData","document","getElementById","users_actions","current_timestamp_local","Number","first_event_timestamp","known_events_data","generateMarkersData","found_types","querySelector","id","initTimeline","window","addEventListener","event","detail","tab","jQuery","on","e","hasOwnProperty","localStorage","getItem","setItem","drawUserTimelineMostActive","drawUserTimeline","wrapper","trigger","dropdown","valueDisplay","key","userData","recentEvents","usersWithRecentEvents","Array","isArray","events","filter","time","length","push","user","role","eventsCount","innerHTML","createOption","text","value","option","createElement","className","dataset","textContent","mostActiveOption","activitySelector","classList","add","appendChild","forEach","displayText","stopPropagation","toggle","contains","target","remove","selectedValue","closest","querySelectorAll","opt","url","URL","location","searchParams","delete","history","pushState","set","updateSecurityLogTable","style","display","isWidgetClosedByUser","innerText","replace","toString","setTimeout","drawTimeAxis","container","width","height","roundToHour","timeRange","axis","createElementNS","setAttribute","let","ts","ceil","x","tick","label","h","getUTCHours","padStart","i","timestamp","date","getUTCMinutes","createEventElement","y","radius","isMostActive","color","group","marker","JSON","stringify","fill","showEventTooltip","bind","hideEventTooltip","handleEventClick","eventData","parse","currentTarget","getAttribute","eventName","count","timeRow","parentElement","tooltip","month","getUTCMonth","day","getUTCDate","String","getUTCFullYear","getUTCSeconds","tooltipY","offsetY","screenWidth","innerWidth","tooltipWidth","offsetWidth","tooltipEventPositionX","clientX","tooltipX","position","top","left","data","addClass","removeClass","getMostActiveUsers","twentyFourHoursAgo","sort","a","b","slice","groupByHour","slots","hourStart","ev","byType","Object","values","svg","noDataMessage","additionalEventsHeight","clientWidth","margin","contentWidth","baseLineY","g","userEvents","uniqueEventsCount","userLineY","eventType","userLine","legendCircle","legendText","userEvent","eventY","upstep","measureTextWidth","tempText","visibility","getComputedTextLength","removeChild","calculateUserLegendRowsCount","users","maxWidth","maxRows","itemGap","circleRadius","moreTextWidth","currentX","currentRow","itemWidth","effectiveMaxWidth","mostActiveUsers","randomColors","userColors","userLegendHeight","baseHeight","userLegendStartY","drawUserLegend","eventColor","slot","max","min","entries","cnt","sqrt","syntheticEvent","startY","rowHeight","visibleCount","lastRowEndX","isLastRow","remainingCount","moreText","ly","moreLink","cursor","textDecoration","cx","tx","circle","colorMap","colorScheme","color_scheme","spbcTimelineWidgetRender","timelineWidget","accordion","heightStyle","collapsible","active"],"mappings":"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"} \ No newline at end of file +{"version":3,"file":"spbc-timeline-widget.min.js","sources":["spbc-timeline-widget.js"],"sourcesContent":["/**\n * Class to render a timeline widget for Security log\n */\nclass SpbcTimelineWidget {\n // Static constants\n static markerRadius = 4;\n static hoursRangeMax = 24;\n static axisStepsCount = 6;\n static userActionsUpstep = 24;\n static legendCircleX = -120;\n static legendTextX = -105;\n static userLegendMaxRows = 2;\n static userLegendRowHeight = 18;\n static userLegendItemGap = 15;\n static userLegendAxisOffset = 60; // Offset from axis to user legend\n\n /**\n * Constructor.\n */\n constructor() {\n // Current time properties\n this.now = new Date();\n this.currentTimestamp = Math.floor(this.now.getTime() / 1000);\n\n // Data properties\n this.eventsData = null;\n this.timelineMarkersData = [];\n this.timelineKnownEventsData = [];\n this.timelineEventTooltip = null;\n this.timelineStartTimestamp = this.currentTimestamp - SpbcTimelineWidget.hoursRangeMax * 3600;\n this.usersCount = 0;\n this.summaryEventsCount = 0;\n this.userLegendExpanded = false;\n this.selectedUserValue = 'most_active__action';\n }\n\n /**\n * Initialize and render the timeline widget\n */\n render() {\n if (typeof spbcTimelineWidgetData === 'undefined') {\n return;\n }\n\n this.timelineEventTooltip = document.getElementById('spbc-timeline-tooltip');\n this.eventsData = spbcTimelineWidgetData.users_actions;\n if (spbcTimelineWidgetData.current_timestamp_local) {\n this.currentTimestamp = Number(spbcTimelineWidgetData.current_timestamp_local);\n }\n this.timelineStartTimestamp = Number(spbcTimelineWidgetData.first_event_timestamp);\n this.timelineKnownEventsData = spbcTimelineWidgetData.known_events_data;\n this.timelineMarkersData = this.generateMarkersData(spbcTimelineWidgetData.found_types);\n\n // Initialize timeline when tab is loaded\n window.addEventListener('spbctTabLoaded', (event) => {\n if (event.detail.tab === 'security_log') {\n console.log(event.detail.tab);\n this.initTimeline();\n }\n });\n\n jQuery('#spbc-timeline-tab').on('click', (e)=> {\n if (e.hasOwnProperty('originalEvent')) {\n if (localStorage.getItem('spbc_timeline_closed') !== '1') {\n localStorage.setItem('spbc_timeline_closed', '1');\n } else {\n localStorage.setItem('spbc_timeline_closed', '0');\n }\n }\n });\n\n // Handle window resize\n window.addEventListener('resize', () => {\n if (!this.selectedUserValue) {\n return;\n }\n this.userLegendExpanded = false;\n if (this.selectedUserValue === 'most_active__action') {\n this.drawUserTimelineMostActive();\n } else {\n this.drawUserTimeline(this.selectedUserValue);\n }\n });\n }\n\n /**\n * Initialize timeline with user selection\n */\n initTimeline() {\n const wrapper = document.getElementById('spbc-timeline-user-select-wrapper');\n const trigger = document.getElementById('spbc-timeline-user-select-trigger');\n const dropdown = document.getElementById('spbc-timeline-user-select-dropdown');\n const valueDisplay = document.getElementById('spbc-timeline-user-select-value');\n const usersWithRecentEvents = [];\n\n // Calculate summary statistics for the last 24 hours only\n this.usersCount = 0;\n this.summaryEventsCount = 0;\n\n for (let key in this.eventsData) {\n if (this.eventsData.hasOwnProperty(key)) {\n const userData = this.eventsData[key];\n const eventsArray = Array.isArray(userData) ? userData : (userData.events || []);\n const recentEvents = eventsArray.filter((event) =>\n event.time >= this.timelineStartTimestamp &&\n event.time <= this.currentTimestamp,\n );\n\n if (recentEvents.length > 0) {\n this.usersCount++;\n this.summaryEventsCount += recentEvents.length;\n usersWithRecentEvents.push({\n user: key,\n role: userData.role || 'unknown',\n eventsCount: recentEvents.length,\n });\n }\n }\n }\n\n // Reset dropdown options\n dropdown.innerHTML = '';\n\n // Helper to create option element\n const createOption = (text, value) => {\n const option = document.createElement('div');\n option.className = 'spbc-timeline-select-option';\n option.dataset.value = value;\n option.textContent = text;\n return option;\n };\n\n // Add \"Most Active Users\" option\n const mostActiveOption = createOption('Most Active Users', 'most_active__action');\n mostActiveOption.classList.add('selected');\n dropdown.appendChild(mostActiveOption);\n\n // Populate user options with role and event count\n usersWithRecentEvents.forEach((userData) => {\n const displayText = `${userData.user} (${userData.role}) (${userData.eventsCount} events)`;\n dropdown.appendChild(createOption(displayText, userData.user));\n });\n\n // Toggle dropdown on trigger click\n trigger.addEventListener('click', (e) => {\n e.stopPropagation();\n wrapper.classList.toggle('open');\n });\n\n // Close dropdown on outside click\n document.addEventListener('click', (e) => {\n if (!wrapper.contains(e.target)) {\n wrapper.classList.remove('open');\n }\n });\n\n // Handle option selection\n dropdown.addEventListener('click', (e) => {\n const option = e.target.closest('.spbc-timeline-select-option');\n if (!option) return;\n\n const selectedValue = option.dataset.value;\n this.selectedUserValue = selectedValue;\n this.userLegendExpanded = false;\n\n // Update display and selection state\n valueDisplay.textContent = option.textContent;\n dropdown.querySelectorAll('.spbc-timeline-select-option').forEach((opt) => {\n opt.classList.remove('selected');\n });\n option.classList.add('selected');\n\n // Close dropdown\n wrapper.classList.remove('open');\n\n if (selectedValue === 'most_active__action') {\n this.drawUserTimelineMostActive();\n const url = new URL(window.location);\n url.searchParams.delete('users');\n window.history.pushState({}, '', url);\n } else {\n this.drawUserTimeline(selectedValue);\n const url = new URL(window.location);\n url.searchParams.set('users', selectedValue);\n window.history.pushState({}, '', url);\n }\n\n // Update security log table with selected user\n updateSecurityLogTable();\n });\n\n // Draw initial timeline or show message\n if (usersWithRecentEvents.length > 0) {\n this.drawUserTimelineMostActive();\n this.selectedUserValue = 'most_active__action';\n valueDisplay.textContent = 'Most Active Users';\n } else {\n document.querySelector('.spbc-timeline-container').style.display = 'none';\n document.querySelector('.spbc-timeline-controls').style.display = 'none';\n document.getElementById('spbc-timeline-no-data').style.display = 'block';\n }\n\n const activitySelector = jQuery('#spbc_timeline_activity_counters')[0];\n activitySelector.innerText = activitySelector.innerText\n .replace('%users_count%', this.usersCount.toString())\n .replace('%events_count%', this.summaryEventsCount.toString());\n\n const isWidgetClosedByUser = (\n localStorage.getItem('spbc_timeline_closed') === '1'\n );\n if (!isWidgetClosedByUser) {\n setTimeout(function() {\n jQuery('#spbc-timeline-tab').trigger('click');\n }, 200);\n }\n }\n\n /**\n * Draw time axis on the timeline\n * @param {SVGElement} container - SVG container element\n * @param {number} width - Drawing area width\n * @param {number} height - Drawing area height\n * @param {boolean} roundToHour - Whether to round ticks to full hours\n */\n drawTimeAxis(container, width, height, roundToHour = false) {\n const timeRange = this.currentTimestamp - this.timelineStartTimestamp;\n const axis = document.createElementNS('http://www.w3.org/2000/svg', 'line');\n height = height + 10;\n // Create main axis line\n axis.setAttribute('class', 'spbc-timeline-axis');\n axis.setAttribute('x1', '0');\n axis.setAttribute('y1', height);\n axis.setAttribute('x2', width);\n axis.setAttribute('y2', height);\n container.appendChild(axis);\n\n // Create axis ticks and labels\n if (roundToHour) {\n const firstHourTs = Math.ceil(this.timelineStartTimestamp / 3600) * 3600;\n for (let ts = firstHourTs; ts <= this.currentTimestamp; ts += 3600) {\n const x = ((ts - this.timelineStartTimestamp) / timeRange) * width;\n\n // Create tick mark\n const tick = document.createElementNS('http://www.w3.org/2000/svg', 'line');\n tick.setAttribute('class', 'spbc-timeline-axis');\n tick.setAttribute('x1', x);\n tick.setAttribute('y1', height);\n tick.setAttribute('x2', x);\n tick.setAttribute('y2', height + 5);\n container.appendChild(tick);\n\n const label = document.createElementNS('http://www.w3.org/2000/svg', 'text');\n label.setAttribute('class', 'spbc-timeline-axis-text');\n label.setAttribute('x', x);\n label.setAttribute('y', height + 20);\n const h = new Date(ts * 1000).getUTCHours();\n label.textContent = `${h.toString().padStart(2, '0')}:00`;\n container.appendChild(label);\n }\n } else {\n for (let i = 0; i <= SpbcTimelineWidget.axisStepsCount; i++) {\n const timestamp = this.timelineStartTimestamp + (i / SpbcTimelineWidget.axisStepsCount) * timeRange;\n const x = (i / SpbcTimelineWidget.axisStepsCount) * width;\n\n // Create tick mark\n const tick = document.createElementNS('http://www.w3.org/2000/svg', 'line');\n tick.setAttribute('class', 'spbc-timeline-axis');\n tick.setAttribute('x1', x);\n tick.setAttribute('y1', height);\n tick.setAttribute('x2', x);\n tick.setAttribute('y2', height + 5);\n container.appendChild(tick);\n\n // Create time label\n const label = document.createElementNS('http://www.w3.org/2000/svg', 'text');\n label.setAttribute('class', 'spbc-timeline-axis-text');\n label.setAttribute('x', x);\n label.setAttribute('y', height + 20);\n const date = new Date(timestamp * 1000);\n label.textContent =\n `\n ${date.getUTCHours().toString().padStart(2, '0')}:\n ${date.getUTCMinutes().toString().padStart(2, '0')}\n `;\n container.appendChild(label);\n }\n }\n }\n\n /**\n * Create SVG element for an event\n * @param {Object} event - Event data\n * @param {number} x - X coordinate\n * @param {number} y - Y coordinate\n * @param {string} user - User name\n * @param {number} radius - Marker radius\n * @param {boolean} isMostActive - Whether this is a synthetic marker for most active users view\n * @param {string} color - Optional color override for the marker\n * @return {SVGElement} Event group element\n */\n createEventElement(event, x, y, user, radius, isMostActive= false, color) {\n const group = document.createElementNS('http://www.w3.org/2000/svg', 'g');\n group.setAttribute('transform', `translate(${x}, ${y})`);\n group.setAttribute('data-event', JSON.stringify({...event, user, isMostActive}));\n\n // Create event marker\n const marker = document.createElementNS('http://www.w3.org/2000/svg', 'circle');\n marker.setAttribute('class', 'spbc-timeline-event-marker');\n marker.setAttribute('r', radius || SpbcTimelineWidget.markerRadius);\n marker.style.fill = color || this.timelineMarkersData[event.event].color;\n group.appendChild(marker);\n\n // Add event handlers\n group.addEventListener('mouseenter', this.showEventTooltip.bind(this));\n group.addEventListener('mouseleave', this.hideEventTooltip.bind(this));\n\n if (isMostActive) {\n group.addEventListener('click', this.handleEventClick.bind(this, user));\n }\n\n return group;\n }\n\n /**\n * Show event tooltip\n * @param {Event} event - Mouse event\n */\n showEventTooltip(event) {\n const eventData = JSON.parse(event.currentTarget.getAttribute('data-event'));\n const {user, event: eventName, time, isMostActive} = eventData;\n\n // Format date for display\n const formatDate = (timestamp) => {\n const date = new Date(timestamp * 1000);\n\n const months = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun',\n 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'];\n\n const month = months[date.getUTCMonth()];\n const day = date.getUTCDate();\n const dayWithZero = String(day).padStart(2, '0');\n const year = date.getUTCFullYear();\n\n const hours = date.getUTCHours().toString().padStart(2, '0');\n const minutes = date.getUTCMinutes().toString().padStart(2, '0');\n const seconds = date.getUTCSeconds().toString().padStart(2, '0');\n\n return `${month} ${dayWithZero} ${year} ${hours}:${minutes}:${seconds}`;\n };\n\n // Update tooltip content\n document.getElementById('spbc-timeline-tooltip--user').innerText = user;\n document.getElementById('spbc-timeline-tooltip--event').innerText =\n eventData.count > 1 ? `${eventName} (${eventData.count})` : eventName;\n\n const timeRow = document.getElementById('spbc-timeline-tooltip--time').parentElement;\n if (isMostActive) {\n timeRow.style.display = 'none';\n } else {\n timeRow.style.display = '';\n document.getElementById('spbc-timeline-tooltip--time').innerText = formatDate(time);\n }\n\n const tooltip= document.getElementById('spbc-timeline-tooltip');\n const fixedTop = event.offsetY + 35;\n const tooltipY = fixedTop.toString() + 'px';\n const screenWidth = window.innerWidth;\n const tooltipWidth = tooltip.offsetWidth ? tooltip.offsetWidth : 165;\n const tooltipEventPositionX = event.clientX;\n let tooltipX = tooltipEventPositionX - tooltipWidth;\n if (tooltipEventPositionX + (tooltipWidth * 2) > screenWidth) {\n tooltipX = tooltipEventPositionX - (tooltipWidth * 2);\n }\n\n tooltipX = tooltipX.toString() + 'px';\n tooltip.style.position = 'absolute';\n tooltip.style.top = tooltipY;\n tooltip.style.left = tooltipX;\n\n // Show tooltip\n this.timelineEventTooltip.classList.remove('spbc-timeline-hidden-element');\n jQuery('[data-event-type]').filter(function() {\n return jQuery(this).data('event-type') === eventName;\n }).addClass('spbc-timelime-highlight-on-tooltip');\n }\n\n /**\n * Hide event tooltip\n */\n hideEventTooltip() {\n this.timelineEventTooltip.classList.add('spbc-timeline-hidden-element');\n jQuery('[data-event-type]').removeClass('spbc-timelime-highlight-on-tooltip');\n }\n\n /**\n * Handle click on event marker - filter security log table by user\n * @param {string} user - User name\n * @param {Event} event - Click event\n */\n handleEventClick(user) {\n this.hideEventTooltip();\n\n // Update custom dropdown selector to show selected user\n const dropdown = document.getElementById('spbc-timeline-user-select-dropdown');\n const valueDisplay = document.getElementById('spbc-timeline-user-select-value');\n if (dropdown && valueDisplay) {\n this.selectedUserValue = user;\n // Find the option with matching value\n const options = dropdown.querySelectorAll('.spbc-timeline-select-option');\n options.forEach((opt) => {\n opt.classList.remove('selected');\n if (opt.dataset.value === user) {\n opt.classList.add('selected');\n valueDisplay.textContent = opt.textContent;\n }\n });\n }\n\n // Redraw timeline for the selected user\n this.drawUserTimeline(user);\n\n // Update URL with users parameter\n const url = new URL(window.location);\n url.searchParams.set('users', user);\n window.history.pushState({}, '', url);\n\n // Update security log table with selected user\n updateSecurityLogTable();\n }\n\n /**\n * Get most active users from last 24 hours (max 15 users)\n * @param {Object} eventsData - User events data\n * @return {Array} Array of user objects with their events\n */\n getMostActiveUsers(eventsData) {\n const currentTime = this.currentTimestamp;\n const twentyFourHoursAgo = currentTime - (24 * 3600);\n\n // Filter users with events in last 24 hours and count their events\n const usersWithRecentEvents = [];\n\n for (let user in eventsData) {\n if (eventsData.hasOwnProperty(user)) {\n const userData = eventsData[user];\n const eventsArray = Array.isArray(userData) ? userData : (userData.events || []);\n\n // Filter events from last 24 hours\n const recentEvents = eventsArray.filter((event) => event.time >= twentyFourHoursAgo);\n\n if (recentEvents.length > 0) {\n usersWithRecentEvents.push({\n user: user,\n events: recentEvents,\n count: recentEvents.length,\n role: userData.role || 'unknown',\n });\n }\n }\n }\n\n // Sort by event count (descending) and take top 15\n usersWithRecentEvents.sort((a, b) => b.count - a.count);\n\n return usersWithRecentEvents.slice(0, 15);\n }\n\n /**\n * Group events into a 24-element array (one slot per calendar hour).\n * @param {string} user\n * @param {Array} events - array of {event, time}\n * @return {Array} 24 items: {hour, user, count, byType}\n */\n groupByHour(user, events) {\n // Group events by actual hour slots (using real timestamps, not just hour of day)\n const slots = {};\n\n events.forEach((ev) => {\n // Round down to the start of the hour\n const hourStart = Math.floor(ev.time / 3600) * 3600;\n\n if (!slots[hourStart]) {\n slots[hourStart] = {\n timestamp: hourStart + 1800, // midpoint of the hour\n user: user,\n count: 0,\n byType: {},\n };\n }\n\n slots[hourStart].count++;\n slots[hourStart].byType[ev.event] = (slots[hourStart].byType[ev.event] || 0) + 1;\n });\n\n return Object.values(slots);\n }\n\n /**\n * Draw timeline for specific user\n * @param {string} user - Selected user name\n */\n drawUserTimeline(user) {\n const svg = document.getElementById('spbc-timeline-svg');\n const noDataMessage = document.getElementById('spbc-timeline-no-data');\n\n // Clear previous content\n svg.innerHTML = '';\n noDataMessage.style.display = 'none';\n\n let additionalEventsHeight = 0;\n if (spbcTimelineWidgetData.found_types.length > 5) {\n additionalEventsHeight = (spbcTimelineWidgetData.found_types.length - 5) * 12;\n }\n\n // Calculate dimensions\n const width = svg.parentElement.clientWidth;\n const height = 150 + additionalEventsHeight + SpbcTimelineWidget.userLegendAxisOffset;\n\n // Setup drawing area\n const margin = {top: 10 + additionalEventsHeight, right: 40, bottom: 40, left: 140};\n const contentWidth = width - margin.left - margin.right;\n const contentHeight = height - margin.top - margin.bottom;\n const baseLineY = contentHeight / 2;\n\n svg.setAttribute('width', width);\n svg.setAttribute('height', height);\n\n const g = document.createElementNS('http://www.w3.org/2000/svg', 'g');\n g.setAttribute('transform', `translate(${margin.left}, ${margin.top+30})`);\n svg.appendChild(g);\n\n // Calculate base positions\n\n\n // Draw axis\n this.drawTimeAxis(g, contentWidth, baseLineY + 10);\n\n // Filter user events\n const userData = this.eventsData[user];\n const userEvents = (Array.isArray(userData) ? userData : (userData.events || [])).filter((event) =>\n event.time >= this.timelineStartTimestamp &&\n event.time <= this.currentTimestamp,\n );\n\n // Handle no events case\n if (userEvents.length === 0) {\n noDataMessage.style.display = 'block';\n return;\n }\n\n const timeRange = this.currentTimestamp - this.timelineStartTimestamp;\n const uniqueEventsCount = spbcTimelineWidgetData.found_types.length;\n\n // Draw event lines and legends\n for (let i = 0; i < uniqueEventsCount; i++) {\n const userLineY = baseLineY - (SpbcTimelineWidget.userActionsUpstep * i);\n const eventType = spbcTimelineWidgetData.found_types[i];\n const color = this.timelineMarkersData[eventType].color;\n\n // Draw event line\n const userLine = document.createElementNS('http://www.w3.org/2000/svg', 'line');\n userLine.setAttribute('class', 'spbc-timeline-user-line');\n userLine.setAttribute('x1', '0');\n userLine.setAttribute('y1', userLineY);\n userLine.setAttribute('x2', contentWidth);\n userLine.setAttribute('y2', userLineY);\n userLine.setAttribute('data-event-type', eventType);\n g.appendChild(userLine);\n\n // Draw legend circle\n const legendCircle = document.createElementNS('http://www.w3.org/2000/svg', 'circle');\n legendCircle.setAttribute('class', 'spbc-timeline-legend-circle');\n legendCircle.setAttribute('cx', SpbcTimelineWidget.legendCircleX);\n legendCircle.setAttribute('cy', userLineY);\n legendCircle.setAttribute('r', SpbcTimelineWidget.markerRadius);\n legendCircle.style.fill = color;\n g.appendChild(legendCircle);\n\n // Draw legend text\n const legendText = document.createElementNS('http://www.w3.org/2000/svg', 'text');\n legendText.setAttribute('class', 'spbc-timeline-legend-text');\n legendText.setAttribute('x', SpbcTimelineWidget.legendTextX);\n legendText.setAttribute('y', userLineY);\n legendText.setAttribute('text-anchor', 'start');\n legendText.setAttribute('dominant-baseline', 'middle');\n legendText.setAttribute('data-event-type', eventType);\n legendText.textContent = eventType;\n g.appendChild(legendText);\n }\n\n // Draw events\n userEvents.forEach((userEvent) => {\n const x = ((userEvent.time - this.timelineStartTimestamp) / timeRange) * contentWidth;\n const eventY = baseLineY + this.timelineMarkersData[userEvent.event].upstep;\n g.appendChild(this.createEventElement(userEvent, x, eventY, user));\n });\n }\n\n /**\n * Measure text width in SVG\n * @param {SVGElement} svg - SVG container element\n * @param {string} text - Text to measure\n * @param {string} className - CSS class for text styling\n * @return {number} Text width in pixels\n */\n measureTextWidth(svg, text, className = 'spbc-timeline-legend-text') {\n const tempText = document.createElementNS('http://www.w3.org/2000/svg', 'text');\n tempText.setAttribute('class', className);\n tempText.textContent = text;\n tempText.style.visibility = 'hidden';\n svg.appendChild(tempText);\n const width = tempText.getComputedTextLength();\n svg.removeChild(tempText);\n return width;\n }\n\n /**\n * Calculate how many rows the user legend will occupy\n * @param {SVGElement} svg - SVG container element\n * @param {Array} users - Array of user data objects\n * @param {number} maxWidth - Maximum width for the legend area\n * @param {number|null} maxRows - Maximum rows limit (null for unlimited)\n * @return {number} Number of rows needed\n */\n calculateUserLegendRowsCount(svg, users, maxWidth, maxRows = null) {\n const itemGap = SpbcTimelineWidget.userLegendItemGap;\n const circleRadius = SpbcTimelineWidget.markerRadius;\n const circleTextGap = 6;\n const startX = 0;\n\n // Calculate \"and N more\" text width for reservation\n const moreTextTemplate = 'and 99 more';\n const moreTextWidth = this.measureTextWidth(svg, moreTextTemplate) + itemGap;\n\n let currentX = startX;\n let currentRow = 0;\n\n for (let i = 0; i < users.length; i++) {\n const userData = users[i];\n const textWidth = this.measureTextWidth(svg, userData.user);\n const itemWidth = circleRadius * 2 + circleTextGap + textWidth + itemGap;\n\n // Check if we need to reserve space for \"and N more\" on last allowed row\n const isLastRow = maxRows !== null && currentRow === maxRows - 1;\n const reserveForMore = isLastRow && (i < users.length - 1);\n const effectiveMaxWidth = reserveForMore ? maxWidth - moreTextWidth : maxWidth;\n\n if (currentX + itemWidth > effectiveMaxWidth && currentX > startX) {\n currentRow++;\n currentX = startX;\n\n // If we hit maxRows limit, stop counting\n if (maxRows !== null && currentRow >= maxRows) {\n return maxRows;\n }\n }\n\n currentX += itemWidth;\n }\n\n return currentRow + 1; // +1 because rows are 0-indexed\n }\n\n /**\n * Draw timeline for most active users\n */\n drawUserTimelineMostActive() {\n const svg = document.getElementById('spbc-timeline-svg');\n const noDataMessage = document.getElementById('spbc-timeline-no-data');\n\n // Clear previous content\n svg.innerHTML = '';\n noDataMessage.style.display = 'none';\n\n // Get most active users\n const mostActiveUsers = this.getMostActiveUsers(this.eventsData);\n\n if (mostActiveUsers.length === 0) {\n noDataMessage.style.display = 'block';\n return;\n }\n\n let additionalEventsHeight = 0;\n if (spbcTimelineWidgetData.found_types.length > 5) {\n additionalEventsHeight = (spbcTimelineWidgetData.found_types.length - 5) * 12;\n }\n\n // Pre-assign a color to each user\n const randomColors = [\n '#0284A8', '#E74C3C', '#2ECC71', '#F39C12', '#9B59B6',\n '#1ABC9C', '#E67E22', '#3498DB', '#E91E63', '#00BCD4',\n '#8BC34A', '#FF5722', '#607D8B', '#795548', '#673AB7',\n ];\n const userColors = {};\n mostActiveUsers.forEach((userData, i) => {\n userColors[userData.user] = randomColors[(i + 1) % randomColors.length];\n });\n\n // Calculate dimensions - need to know content width first for row calculation\n const width = svg.parentElement.clientWidth;\n const margin = {top: 10 + additionalEventsHeight, right: 40, bottom: 40, left: 140};\n const contentWidth = width - margin.left - margin.right;\n\n // Calculate user legend rows count based on expanded state\n const userLegendRowHeight = SpbcTimelineWidget.userLegendRowHeight;\n const maxRows = this.userLegendExpanded ? null : SpbcTimelineWidget.userLegendMaxRows;\n const actualRowsCount = this.calculateUserLegendRowsCount(svg, mostActiveUsers, contentWidth, maxRows);\n const userLegendHeight = actualRowsCount * userLegendRowHeight;\n\n // Calculate height: base height (like drawUserTimeline) + user legend space\n const baseHeight = 150 + additionalEventsHeight + SpbcTimelineWidget.userLegendAxisOffset;\n const height = baseHeight + userLegendHeight;\n\n // Setup drawing area - baseLineY calculated from baseHeight (same as drawUserTimeline)\n const baseContentHeight = baseHeight - margin.top - margin.bottom;\n const baseLineY = baseContentHeight / 2;\n\n svg.setAttribute('width', width);\n svg.setAttribute('height', height);\n\n const g = document.createElementNS('http://www.w3.org/2000/svg', 'g');\n g.setAttribute('transform', `translate(${margin.left}, ${margin.top+30})`);\n svg.appendChild(g);\n\n // Draw axis\n this.drawTimeAxis(g, contentWidth, baseLineY + 10, true);\n\n const timeRange = this.currentTimestamp - this.timelineStartTimestamp;\n const uniqueEventsCount = spbcTimelineWidgetData.found_types.length;\n\n // Draw event lines and legends\n for (let i = 0; i < uniqueEventsCount; i++) {\n const userLineY = baseLineY - (SpbcTimelineWidget.userActionsUpstep * i);\n const eventType = spbcTimelineWidgetData.found_types[i];\n const color = this.timelineMarkersData[eventType].color;\n\n // Draw event line\n const userLine = document.createElementNS('http://www.w3.org/2000/svg', 'line');\n userLine.setAttribute('class', 'spbc-timeline-user-line');\n userLine.setAttribute('x1', '0');\n userLine.setAttribute('y1', userLineY);\n userLine.setAttribute('x2', contentWidth);\n userLine.setAttribute('y2', userLineY);\n userLine.setAttribute('data-event-type', eventType);\n g.appendChild(userLine);\n\n // Draw legend circle\n const legendCircle = document.createElementNS('http://www.w3.org/2000/svg', 'circle');\n legendCircle.setAttribute('class', 'spbc-timeline-legend-circle');\n legendCircle.setAttribute('cx', SpbcTimelineWidget.legendCircleX);\n legendCircle.setAttribute('cy', userLineY);\n legendCircle.setAttribute('r', SpbcTimelineWidget.markerRadius);\n legendCircle.style.fill = color;\n g.appendChild(legendCircle);\n\n // Draw legend text\n const legendText = document.createElementNS('http://www.w3.org/2000/svg', 'text');\n legendText.setAttribute('class', 'spbc-timeline-legend-text');\n legendText.setAttribute('x', SpbcTimelineWidget.legendTextX);\n legendText.setAttribute('y', userLineY);\n legendText.setAttribute('text-anchor', 'start');\n legendText.setAttribute('dominant-baseline', 'middle');\n legendText.setAttribute('data-event-type', eventType);\n legendText.textContent = eventType;\n g.appendChild(legendText);\n }\n\n // Draw user color legend (below axis)\n const userLegendStartY = baseLineY + SpbcTimelineWidget.userLegendAxisOffset;\n this.drawUserLegend(svg, g, mostActiveUsers, userColors, userLegendStartY, contentWidth);\n\n // Draw events for all most active users, grouped by hour\n mostActiveUsers.forEach((userData) => {\n const eventColor = userColors[userData.user];\n\n const hourly = this.groupByHour(userData.user, userData.events);\n hourly.forEach((slot) => {\n if (slot.count === 0) return;\n const x = Math.max(\n 0,\n Math.min(\n ((slot.timestamp - this.timelineStartTimestamp) / timeRange) * contentWidth, contentWidth,\n ),\n );\n Object.entries(slot.byType).forEach(([eventType, cnt]) => {\n const eventY = baseLineY + this.timelineMarkersData[eventType].upstep;\n const radius = SpbcTimelineWidget.markerRadius + Math.min(Math.sqrt(cnt - 1) * 2, 8);\n const syntheticEvent = {event: eventType, time: slot.timestamp, count: cnt};\n g.appendChild(\n this.createEventElement(\n syntheticEvent,\n x,\n eventY,\n userData.user,\n radius,\n true,\n eventColor,\n ),\n );\n });\n });\n });\n }\n\n /**\n * Draw user legend with horizontal layout and row limit\n * @param {SVGElement} svg - SVG element\n * @param {SVGElement} g - Group element to append to\n * @param {Array} users - Array of user data objects\n * @param {Object} userColors - Map of user to color\n * @param {number} startY - Starting Y position\n * @param {number} maxWidth - Maximum width for the legend area\n */\n drawUserLegend(svg, g, users, userColors, startY, maxWidth) {\n const rowHeight = SpbcTimelineWidget.userLegendRowHeight;\n const itemGap = SpbcTimelineWidget.userLegendItemGap;\n const circleRadius = SpbcTimelineWidget.markerRadius;\n const circleTextGap = 6;\n const startX = 0;\n\n // Determine max rows: null for expanded (unlimited), or limit for collapsed\n const maxRows = this.userLegendExpanded ? null : SpbcTimelineWidget.userLegendMaxRows;\n\n // Horizontal layout with optional row limit\n let currentX = startX;\n let currentRow = 0;\n let visibleCount = 0;\n let lastRowEndX = startX; // Track end position on the last allowed row for \"and N more\" button\n\n // Calculate \"and N more\" text width for reservation\n const moreTextTemplate = 'and 99 more';\n const moreTextWidth = this.measureTextWidth(svg, moreTextTemplate) + itemGap;\n\n for (let i = 0; i < users.length; i++) {\n const userData = users[i];\n const textWidth = this.measureTextWidth(svg, userData.user);\n const itemWidth = circleRadius * 2 + circleTextGap + textWidth + itemGap;\n\n // Check if we need to wrap to next row\n const isLastRow = maxRows !== null && currentRow === maxRows - 1;\n const reserveForMore = isLastRow && (i < users.length - 1);\n const effectiveMaxWidth = reserveForMore ? maxWidth - moreTextWidth : maxWidth;\n\n if (currentX + itemWidth > effectiveMaxWidth && currentX > startX) {\n // Before moving to next row, save position if we're on the last allowed row\n if (isLastRow) {\n lastRowEndX = currentX;\n }\n\n currentRow++;\n currentX = startX;\n\n // Check if we exceeded max rows (only when maxRows is set)\n if (maxRows !== null && currentRow >= maxRows) {\n // Draw \"and N more\" link at the end of the last row (continuation of the line)\n const remainingCount = users.length - visibleCount;\n if (remainingCount > 0) {\n const moreText = `and ${remainingCount} more`;\n const ly = startY + (maxRows - 1) * rowHeight;\n\n const moreLink = document.createElementNS('http://www.w3.org/2000/svg', 'text');\n moreLink.setAttribute('class', 'spbc-timeline-legend-text spbc-timeline-more-link');\n moreLink.setAttribute('x', lastRowEndX);\n moreLink.setAttribute('y', ly);\n moreLink.setAttribute('text-anchor', 'start');\n moreLink.setAttribute('dominant-baseline', 'middle');\n moreLink.textContent = moreText;\n moreLink.style.cursor = 'pointer';\n moreLink.style.fill = '#0073aa';\n moreLink.style.textDecoration = 'underline';\n moreLink.addEventListener('click', () => {\n this.userLegendExpanded = true;\n this.drawUserTimelineMostActive();\n });\n g.appendChild(moreLink);\n }\n return;\n }\n }\n\n // Track position on the last allowed row\n if (maxRows !== null && currentRow === maxRows - 1) {\n lastRowEndX = currentX + itemWidth;\n }\n\n // Draw user legend item\n const ly = startY + currentRow * rowHeight;\n const cx = currentX + circleRadius;\n const tx = currentX + circleRadius * 2 + circleTextGap;\n\n const circle = document.createElementNS('http://www.w3.org/2000/svg', 'circle');\n circle.setAttribute('cx', cx);\n circle.setAttribute('cy', ly);\n circle.setAttribute('r', circleRadius);\n circle.style.fill = userColors[userData.user];\n g.appendChild(circle);\n\n const text = document.createElementNS('http://www.w3.org/2000/svg', 'text');\n text.setAttribute('class', 'spbc-timeline-legend-text');\n text.setAttribute('x', tx);\n text.setAttribute('y', ly);\n text.setAttribute('text-anchor', 'start');\n text.setAttribute('dominant-baseline', 'middle');\n text.textContent = userData.user;\n g.appendChild(text);\n\n currentX += itemWidth;\n visibleCount++;\n }\n }\n\n /*\n /!**\n * Updates the security log table based on selected user\n * @param {string} selectedUser - The selected user or 'most_active__action'\n *!/\n updateSecurityLogTable(selectedUser) {\n const data= {};\n const urlParams = new URLSearchParams(window.location.search);\n const usersParam = urlParams.get('users');\n if (usersParam) {\n data.users = usersParam;\n }\n spbcSendAJAXRequest(data, params, obj);\n }*/\n\n /**\n * Generate marker data for events\n * @param {Array} events - List of event types\n * @return {Object} Marker configuration data\n */\n generateMarkersData(events) {\n const colorMap = {};\n let upstep = 0;\n let colorScheme = 'default';\n\n events.forEach((userEvent) => {\n if (this.timelineKnownEventsData[userEvent] && this.timelineKnownEventsData[userEvent].color_scheme) {\n colorScheme = this.timelineKnownEventsData[userEvent].color_scheme;\n }\n colorMap[userEvent] = {\n color: colorScheme,\n upstep: upstep,\n };\n upstep -= SpbcTimelineWidget.userActionsUpstep;\n });\n\n return colorMap;\n }\n}\n\n/**\n * Wrapper\n */\nfunction spbcTimelineWidgetRender() { // eslint-disable-line no-unused-vars\n const timelineWidget = new SpbcTimelineWidget();\n jQuery( function() {\n jQuery('#spbc-timeline-accordion').accordion({\n heightStyle: 'content',\n collapsible: true,\n active: false,\n });\n } );\n timelineWidget.render();\n}\n\n\n"],"names":["SpbcTimelineWidget","markerRadius","hoursRangeMax","axisStepsCount","userActionsUpstep","legendCircleX","legendTextX","userLegendMaxRows","userLegendRowHeight","userLegendItemGap","userLegendAxisOffset","constructor","this","now","Date","currentTimestamp","Math","floor","getTime","eventsData","timelineMarkersData","timelineKnownEventsData","timelineEventTooltip","timelineStartTimestamp","usersCount","summaryEventsCount","userLegendExpanded","selectedUserValue","render","spbcTimelineWidgetData","document","getElementById","users_actions","current_timestamp_local","Number","first_event_timestamp","known_events_data","generateMarkersData","found_types","window","addEventListener","event","detail","tab","console","log","initTimeline","jQuery","on","e","hasOwnProperty","localStorage","getItem","setItem","drawUserTimelineMostActive","drawUserTimeline","wrapper","trigger","dropdown","valueDisplay","key","userData","recentEvents","usersWithRecentEvents","Array","isArray","events","filter","time","length","push","user","role","eventsCount","innerHTML","createOption","text","value","option","createElement","className","dataset","textContent","mostActiveOption","activitySelector","classList","add","appendChild","forEach","displayText","stopPropagation","toggle","contains","target","remove","selectedValue","closest","querySelectorAll","opt","url","URL","location","searchParams","delete","history","pushState","set","updateSecurityLogTable","querySelector","style","display","isWidgetClosedByUser","innerText","replace","toString","setTimeout","drawTimeAxis","container","width","height","roundToHour","timeRange","axis","createElementNS","setAttribute","let","ts","ceil","x","tick","label","h","getUTCHours","padStart","i","timestamp","date","getUTCMinutes","createEventElement","y","radius","isMostActive","color","group","marker","JSON","stringify","fill","showEventTooltip","bind","hideEventTooltip","handleEventClick","eventData","parse","currentTarget","getAttribute","eventName","count","timeRow","parentElement","tooltip","month","getUTCMonth","day","getUTCDate","String","getUTCFullYear","getUTCSeconds","tooltipY","offsetY","screenWidth","innerWidth","tooltipWidth","offsetWidth","tooltipEventPositionX","clientX","tooltipX","position","top","left","data","addClass","removeClass","getMostActiveUsers","twentyFourHoursAgo","sort","a","b","slice","groupByHour","slots","hourStart","ev","byType","Object","values","svg","noDataMessage","additionalEventsHeight","clientWidth","margin","contentWidth","baseLineY","g","userEvents","uniqueEventsCount","userLineY","eventType","userLine","legendCircle","legendText","userEvent","eventY","upstep","measureTextWidth","tempText","visibility","getComputedTextLength","removeChild","calculateUserLegendRowsCount","users","maxWidth","maxRows","itemGap","circleRadius","moreTextWidth","currentX","currentRow","itemWidth","effectiveMaxWidth","mostActiveUsers","randomColors","userColors","userLegendHeight","baseHeight","userLegendStartY","drawUserLegend","eventColor","slot","max","min","entries","cnt","sqrt","syntheticEvent","startY","rowHeight","visibleCount","lastRowEndX","isLastRow","remainingCount","moreText","ly","moreLink","cursor","textDecoration","cx","tx","circle","colorMap","colorScheme","color_scheme","spbcTimelineWidgetRender","timelineWidget","accordion","heightStyle","collapsible","active"],"mappings":"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"} \ No newline at end of file diff --git a/js/src/react/components/TabContent/TabContent.js b/js/src/react/components/TabContent/TabContent.js index b365da7b9..d26098b6a 100644 --- a/js/src/react/components/TabContent/TabContent.js +++ b/js/src/react/components/TabContent/TabContent.js @@ -13,6 +13,7 @@ export default function TabContent({sectionId, isActive, tabComponent}) { if ( sectionId === 'scanner' || sectionId === 'backups' || + sectionId === 'security_log' || sectionId === 'summary' || sectionId === 'debug' ) { diff --git a/js/src/spbc-timeline-widget.js b/js/src/spbc-timeline-widget.js index 840e62d47..a8914066a 100644 --- a/js/src/spbc-timeline-widget.js +++ b/js/src/spbc-timeline-widget.js @@ -51,11 +51,6 @@ class SpbcTimelineWidget { this.timelineKnownEventsData = spbcTimelineWidgetData.known_events_data; this.timelineMarkersData = this.generateMarkersData(spbcTimelineWidgetData.found_types); - const activeTab = document.querySelector('.spbc_tab_section--active'); - if (activeTab && activeTab.id === 'spbc_tab-security_log') { - this.initTimeline(); - } - // Initialize timeline when tab is loaded window.addEventListener('spbctTabLoaded', (event) => { if (event.detail.tab === 'security_log') { From 7078e1e57230362dd265a6fced61f94bec32713f Mon Sep 17 00:00:00 2001 From: Glomberg Date: Wed, 19 Aug 2026 14:33:51 +0300 Subject: [PATCH 3/6] Fix. React. `Settings General` tab fixed. --- js/public/spbct-react-bundle.js | 2 +- .../TabSettingsGeneral/TabSettingsGeneral.js | 38 +++---------------- 2 files changed, 6 insertions(+), 34 deletions(-) diff --git a/js/public/spbct-react-bundle.js b/js/public/spbct-react-bundle.js index b91d0c9a4..e594b8ea1 100644 --- a/js/public/spbct-react-bundle.js +++ b/js/public/spbct-react-bundle.js @@ -1486,7 +1486,7 @@ eval("{__webpack_require__.r(__webpack_exports__);\n/* harmony export */ __webpa \*************************************************************************************/ (__unused_webpack_module, __webpack_exports__, __webpack_require__) { -eval("{__webpack_require__.r(__webpack_exports__);\n/* harmony export */ __webpack_require__.d(__webpack_exports__, {\n/* harmony export */ \"default\": () => (/* binding */ TabSettingsGeneral)\n/* harmony export */ });\n/* harmony import */ var react__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! react */ \"./node_modules/react/index.js\");\n/* harmony import */ var react__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(react__WEBPACK_IMPORTED_MODULE_0__);\n/* harmony import */ var _styles_css__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./styles.css */ \"./js/src/react/components/TabContent/TabSettingsGeneral/styles.css\");\n/* harmony import */ var _TabContentTypes_Html_TabContentHtml__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ../../TabContentTypes/Html/TabContentHtml */ \"./js/src/react/components/TabContentTypes/Html/TabContentHtml.js\");\n/* harmony import */ var _modules_Http_Ajax__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ../../../modules/Http/Ajax */ \"./js/src/react/modules/Http/Ajax.js\");\n/* harmony import */ var _Preloader_Preloader__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(/*! ../../Preloader/Preloader */ \"./js/src/react/components/Preloader/Preloader.js\");\n/* harmony import */ var _FormElements_InputHidden__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(/*! ../../FormElements/InputHidden */ \"./js/src/react/components/FormElements/InputHidden.js\");\n/* harmony import */ var _Settings__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(/*! ./Settings */ \"./js/src/react/components/TabContent/TabSettingsGeneral/Settings.js\");\n/* harmony import */ var _NoticeBanner_NoticeBanner__WEBPACK_IMPORTED_MODULE_7__ = __webpack_require__(/*! ../NoticeBanner/NoticeBanner */ \"./js/src/react/components/TabContent/NoticeBanner/NoticeBanner.js\");\n/* harmony import */ var _SettingsOverview_SettingsOverview__WEBPACK_IMPORTED_MODULE_8__ = __webpack_require__(/*! ../../SettingsOverview/SettingsOverview */ \"./js/src/react/components/SettingsOverview/SettingsOverview.js\");\n/* harmony import */ var _QuickNavigation_QuickNavigation__WEBPACK_IMPORTED_MODULE_9__ = __webpack_require__(/*! ./QuickNavigation/QuickNavigation */ \"./js/src/react/components/TabContent/TabSettingsGeneral/QuickNavigation/QuickNavigation.js\");\nfunction _regenerator() { /*! regenerator-runtime -- Copyright (c) 2014-present, Facebook, Inc. -- license (MIT): https://github.com/babel/babel/blob/main/packages/babel-helpers/LICENSE */ var e, t, r = \"function\" == typeof Symbol ? Symbol : {}, n = r.iterator || \"@@iterator\", o = r.toStringTag || \"@@toStringTag\"; function i(r, n, o, i) { var c = n && n.prototype instanceof Generator ? n : Generator, u = Object.create(c.prototype); return _regeneratorDefine2(u, \"_invoke\", function (r, n, o) { var i, c, u, f = 0, p = o || [], y = !1, G = { p: 0, n: 0, v: e, a: d, f: d.bind(e, 4), d: function d(t, r) { return i = t, c = 0, u = e, G.n = r, a; } }; function d(r, n) { for (c = r, u = n, t = 0; !y && f && !o && t < p.length; t++) { var o, i = p[t], d = G.p, l = i[2]; r > 3 ? (o = l === n) && (u = i[(c = i[4]) ? 5 : (c = 3, 3)], i[4] = i[5] = e) : i[0] <= d && ((o = r < 2 && d < i[1]) ? (c = 0, G.v = n, G.n = i[1]) : d < l && (o = r < 3 || i[0] > n || n > l) && (i[4] = r, i[5] = n, G.n = l, c = 0)); } if (o || r > 1) return a; throw y = !0, n; } return function (o, p, l) { if (f > 1) throw TypeError(\"Generator is already running\"); for (y && 1 === p && d(p, l), c = p, u = l; (t = c < 2 ? e : u) || !y;) { i || (c ? c < 3 ? (c > 1 && (G.n = -1), d(c, u)) : G.n = u : G.v = u); try { if (f = 2, i) { if (c || (o = \"next\"), t = i[o]) { if (!(t = t.call(i, u))) throw TypeError(\"iterator result is not an object\"); if (!t.done) return t; u = t.value, c < 2 && (c = 0); } else 1 === c && (t = i[\"return\"]) && t.call(i), c < 2 && (u = TypeError(\"The iterator does not provide a '\" + o + \"' method\"), c = 1); i = e; } else if ((t = (y = G.n < 0) ? u : r.call(n, G)) !== a) break; } catch (t) { i = e, c = 1, u = t; } finally { f = 1; } } return { value: t, done: y }; }; }(r, o, i), !0), u; } var a = {}; function Generator() {} function GeneratorFunction() {} function GeneratorFunctionPrototype() {} t = Object.getPrototypeOf; var c = [][n] ? t(t([][n]())) : (_regeneratorDefine2(t = {}, n, function () { return this; }), t), u = GeneratorFunctionPrototype.prototype = Generator.prototype = Object.create(c); function f(e) { return Object.setPrototypeOf ? Object.setPrototypeOf(e, GeneratorFunctionPrototype) : (e.__proto__ = GeneratorFunctionPrototype, _regeneratorDefine2(e, o, \"GeneratorFunction\")), e.prototype = Object.create(u), e; } return GeneratorFunction.prototype = GeneratorFunctionPrototype, _regeneratorDefine2(u, \"constructor\", GeneratorFunctionPrototype), _regeneratorDefine2(GeneratorFunctionPrototype, \"constructor\", GeneratorFunction), GeneratorFunction.displayName = \"GeneratorFunction\", _regeneratorDefine2(GeneratorFunctionPrototype, o, \"GeneratorFunction\"), _regeneratorDefine2(u), _regeneratorDefine2(u, o, \"Generator\"), _regeneratorDefine2(u, n, function () { return this; }), _regeneratorDefine2(u, \"toString\", function () { return \"[object Generator]\"; }), (_regenerator = function _regenerator() { return { w: i, m: f }; })(); }\nfunction _regeneratorDefine2(e, r, n, t) { var i = Object.defineProperty; try { i({}, \"\", {}); } catch (e) { i = 0; } _regeneratorDefine2 = function _regeneratorDefine(e, r, n, t) { function o(r, n) { _regeneratorDefine2(e, r, function (e) { return this._invoke(r, n, e); }); } r ? i ? i(e, r, { value: n, enumerable: !t, configurable: !t, writable: !t }) : e[r] = n : (o(\"next\", 0), o(\"throw\", 1), o(\"return\", 2)); }, _regeneratorDefine2(e, r, n, t); }\nfunction asyncGeneratorStep(n, t, e, r, o, a, c) { try { var i = n[a](c), u = i.value; } catch (n) { return void e(n); } i.done ? t(u) : Promise.resolve(u).then(r, o); }\nfunction _asyncToGenerator(n) { return function () { var t = this, e = arguments; return new Promise(function (r, o) { var a = n.apply(t, e); function _next(n) { asyncGeneratorStep(a, r, o, _next, _throw, \"next\", n); } function _throw(n) { asyncGeneratorStep(a, r, o, _next, _throw, \"throw\", n); } _next(void 0); }); }; }\nfunction _slicedToArray(r, e) { return _arrayWithHoles(r) || _iterableToArrayLimit(r, e) || _unsupportedIterableToArray(r, e) || _nonIterableRest(); }\nfunction _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.\"); }\nfunction _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; } }\nfunction _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; }\nfunction _iterableToArrayLimit(r, l) { var t = null == r ? null : \"undefined\" != typeof Symbol && r[Symbol.iterator] || r[\"@@iterator\"]; if (null != t) { var e, n, i, u, a = [], f = !0, o = !1; try { if (i = (t = t.call(r)).next, 0 === l) { if (Object(t) !== t) return; f = !1; } else for (; !(f = (e = i.call(t)).done) && (a.push(e.value), a.length !== l); f = !0); } catch (r) { o = !0, n = r; } finally { try { if (!f && null != t[\"return\"] && (u = t[\"return\"](), Object(u) !== u)) return; } finally { if (o) throw n; } } return a; } }\nfunction _arrayWithHoles(r) { if (Array.isArray(r)) return r; }\n;\n\n\n\n\n\n\n\n\n\nfunction TabSettingsGeneral(_ref) {\n var _settings$objects, _settings$strings, _settings$strings2, _settings$strings3, _settings$objects2, _settings$objects3, _settings$strings4, _settings$strings5, _settings$strings6, _settings$strings7, _settings$flags, _settings$flags2, _settings$strings8, _settings$strings9, _settings$strings0, _settings$strings1, _settings$objects4, _settings$objects5, _settings$objects6;\n var sectionId = _ref.sectionId,\n sectionData = _ref.sectionData,\n isActive = _ref.isActive;\n var _useState = (0,react__WEBPACK_IMPORTED_MODULE_0__.useState)({\n sectionData: sectionData\n }),\n _useState2 = _slicedToArray(_useState, 2),\n sectionDataLegacy = _useState2[0],\n setSectionDataLegacy = _useState2[1];\n var _useState3 = (0,react__WEBPACK_IMPORTED_MODULE_0__.useState)({}),\n _useState4 = _slicedToArray(_useState3, 2),\n settings = _useState4[0],\n setSettings = _useState4[1];\n var _useState5 = (0,react__WEBPACK_IMPORTED_MODULE_0__.useState)(false),\n _useState6 = _slicedToArray(_useState5, 2),\n settingsFetched = _useState6[0],\n setSettingsFetched = _useState6[1];\n var _useState7 = (0,react__WEBPACK_IMPORTED_MODULE_0__.useState)(false),\n _useState8 = _slicedToArray(_useState7, 2),\n keyIsOk = _useState8[0],\n setKeyIsOk = _useState8[1];\n jQuery(document).ready(function () {\n // Show/Hide access key\n jQuery('#showHideLink').on('click', function () {\n jQuery('#spbc_key').val(jQuery('#spbc_key').attr('key'));\n jQuery('#spbc_key+div').show();\n jQuery(this).fadeOut(300);\n });\n\n // Set time zone filed value if key is bad\n // @ToDo unused code?\n if (!+spbcSettings.key_is_ok) {\n var d = new Date();\n jQuery('#spbc_admin_timezone').val(d.getTimezoneOffset() / 60 * -1);\n }\n\n /**\n * Shuffle authentication unique keys and salts\n */\n function spbcShuffleSalts() {\n var data = {};\n data.security = spbcSettings.ajax_nonce;\n data.action = 'spbc_action_shuffle_salts';\n jQuery.ajax({\n type: 'POST',\n async: false,\n url: spbcSettings.ajaxurl,\n data: data,\n success: function success(result) {\n if (result.success) {\n document.location.reload();\n } else {\n alert(result.data);\n }\n }\n });\n }\n\n // Event for shuffle authentication unique keys and salts\n // Enabling the salt update button and repeatedly pressing\n var actionShuffleSaltsCountClicks = 0;\n jQuery(document).on('click', '#action-shuffle-salts', function (e) {\n e.preventDefault();\n var btn = jQuery(this);\n if (btn.hasClass('disabled')) {\n actionShuffleSaltsCountClicks++;\n if (actionShuffleSaltsCountClicks === 3) {\n btn.removeClass('disabled');\n }\n } else {\n actionShuffleSaltsCountClicks = 0;\n if (confirm('Are you sure?')) {\n spbcShuffleSalts();\n }\n }\n });\n jQuery('button[id*=\"action-adjust-change-\"]').click(function (e) {\n e.preventDefault();\n var data = {};\n data.action = 'spbc_action_adjust_change';\n data.adjust = jQuery(this).data('adjust');\n var params = {};\n params.button = document.getElementById('action-adjust-change-' + data.adjust);\n params.success = function () {\n document.location.reload();\n };\n spbcSendAJAXRequest(data, params);\n });\n jQuery('button[id*=\"action-adjust-reverse-\"]').click(function (e) {\n e.preventDefault();\n var data = {};\n data.action = 'spbc_action_adjust_reverse';\n data.adjust = jQuery(this).data('adjust');\n var params = {};\n params.button = document.getElementById('action-adjust-reverse-' + data.adjust);\n params.success = function () {\n document.location.reload();\n };\n spbcSendAJAXRequest(data, params);\n });\n });\n (0,react__WEBPACK_IMPORTED_MODULE_0__.useEffect)(function () {\n setSectionDataLegacy(sectionData);\n fetchContent();\n }, []);\n\n // @ToDo - need to be moved to the separate hook, same duplication is in `TabContent` component\n var fetchContent = /*#__PURE__*/function () {\n var _ref2 = _asyncToGenerator(/*#__PURE__*/_regenerator().m(function _callee() {\n var _data$flags, requestData, data, _t;\n return _regenerator().w(function (_context) {\n while (1) switch (_context.p = _context.n) {\n case 0:\n _context.p = 0;\n requestData = {\n action: 'spbct_get_tab_data',\n tab_name: sectionId\n };\n _context.n = 1;\n return (0,_modules_Http_Ajax__WEBPACK_IMPORTED_MODULE_3__.sendAjaxRequest)(requestData, true);\n case 1:\n data = _context.v;\n setSettings(data);\n setKeyIsOk((_data$flags = data.flags) === null || _data$flags === void 0 ? void 0 : _data$flags.keyIsOk);\n setSettingsFetched(true);\n _context.n = 3;\n break;\n case 2:\n _context.p = 2;\n _t = _context.v;\n console.error('Error fetching content:', _t);\n setSettingsFetched(true);\n case 3:\n return _context.a(2);\n }\n }, _callee, null, [[0, 2]]);\n }));\n return function fetchContent() {\n return _ref2.apply(this, arguments);\n };\n }();\n if (!settingsFetched) {\n return /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_Preloader_Preloader__WEBPACK_IMPORTED_MODULE_4__[\"default\"], null);\n }\n return /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement((react__WEBPACK_IMPORTED_MODULE_0___default().Fragment), null, window.spbcSettings && window.spbcSettings.critical > 0 && /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_NoticeBanner_NoticeBanner__WEBPACK_IMPORTED_MODULE_7__[\"default\"], null), keyIsOk && /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_SettingsOverview_SettingsOverview__WEBPACK_IMPORTED_MODULE_8__[\"default\"], {\n settings: (_settings$objects = settings.objects) === null || _settings$objects === void 0 ? void 0 : _settings$objects.settings\n }), /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement(\"form\", {\n id: 'spbc_settings_form',\n method: 'POST',\n action: 'options.php'\n }, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_FormElements_InputHidden__WEBPACK_IMPORTED_MODULE_5__[\"default\"], {\n id: 'option_page',\n name: 'option_page',\n value: (_settings$strings = settings.strings) === null || _settings$strings === void 0 ? void 0 : _settings$strings.settingsSlug\n }), /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_FormElements_InputHidden__WEBPACK_IMPORTED_MODULE_5__[\"default\"], {\n id: 'action',\n name: 'action',\n value: 'update'\n }), /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_FormElements_InputHidden__WEBPACK_IMPORTED_MODULE_5__[\"default\"], {\n id: '_wpnonce',\n name: '_wpnonce',\n value: (_settings$strings2 = settings.strings) === null || _settings$strings2 === void 0 ? void 0 : _settings$strings2.nonce\n }), /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement(\"div\", {\n id: \"spbc-settings-general-wrapper\",\n className: \"spbc-settings-general-wrapper\"\n }, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement(\"div\", {\n id: \"spbc-settings-general-wrapper-settings\"\n }, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_Settings__WEBPACK_IMPORTED_MODULE_6__[\"default\"], {\n settingsSlug: (_settings$strings3 = settings.strings) === null || _settings$strings3 === void 0 ? void 0 : _settings$strings3.settingsSlug,\n settings: (_settings$objects2 = settings.objects) === null || _settings$objects2 === void 0 ? void 0 : _settings$objects2.settings,\n keyIsOk: keyIsOk,\n wpUserRolesOptions: (_settings$objects3 = settings.objects) === null || _settings$objects3 === void 0 ? void 0 : _settings$objects3.wp_user_roles_options,\n verificationEmail: (_settings$strings4 = settings.strings) === null || _settings$strings4 === void 0 ? void 0 : _settings$strings4.two_fa_verification_email_recipient,\n authLoginPageUrl: (_settings$strings5 = settings.strings) === null || _settings$strings5 === void 0 ? void 0 : _settings$strings5.auth_login_page_url,\n techSupportUrlPrefix: (_settings$strings6 = settings.strings) === null || _settings$strings6 === void 0 ? void 0 : _settings$strings6.tech_support_url_field_prefix,\n loginRenameNotifyAdminEmail: (_settings$strings7 = settings.strings) === null || _settings$strings7 === void 0 ? void 0 : _settings$strings7.login_rename_notify_admin_email,\n canActivatePlugins: (_settings$flags = settings.flags) === null || _settings$flags === void 0 ? void 0 : _settings$flags.canActivatePlugins,\n shuffleSaltsUnlocked: (_settings$flags2 = settings.flags) === null || _settings$flags2 === void 0 ? void 0 : _settings$flags2.shuffleSaltsUnlocked,\n visitorCurrentIp: (_settings$strings8 = settings.strings) === null || _settings$strings8 === void 0 ? void 0 : _settings$strings8.visitorCurrentIp,\n nextScheduledScanDescription: (_settings$strings9 = settings.strings) === null || _settings$strings9 === void 0 ? void 0 : _settings$strings9.nextScheduledScanDescription,\n scannerAutoStartManualTimezone: (_settings$strings0 = settings.strings) === null || _settings$strings0 === void 0 ? void 0 : _settings$strings0.scannerAutoStartManualTimezone,\n wlCompanyName: (_settings$strings1 = settings.strings) === null || _settings$strings1 === void 0 ? void 0 : _settings$strings1.wlCompanyName,\n visitorIpSources: (_settings$objects4 = settings.objects) === null || _settings$objects4 === void 0 ? void 0 : _settings$objects4.visitorIpSources,\n scannerExclusionsDir: (_settings$objects5 = settings.objects) === null || _settings$objects5 === void 0 ? void 0 : _settings$objects5.scannerExclusionsDir,\n scannerExclusionsURL: (_settings$objects6 = settings.objects) === null || _settings$objects6 === void 0 ? void 0 : _settings$objects6.scannerExclusionsURL\n }), /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_TabContentTypes_Html_TabContentHtml__WEBPACK_IMPORTED_MODULE_2__[\"default\"], {\n sectionId: sectionId,\n sectionData: sectionDataLegacy,\n isActive: isActive\n })), /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_QuickNavigation_QuickNavigation__WEBPACK_IMPORTED_MODULE_9__[\"default\"], {\n keyIsOk: keyIsOk\n }))));\n}\n;\n\n//# sourceURL=webpack:///./js/src/react/components/TabContent/TabSettingsGeneral/TabSettingsGeneral.js?\n}"); +eval("{__webpack_require__.r(__webpack_exports__);\n/* harmony export */ __webpack_require__.d(__webpack_exports__, {\n/* harmony export */ \"default\": () => (/* binding */ TabSettingsGeneral)\n/* harmony export */ });\n/* harmony import */ var react__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! react */ \"./node_modules/react/index.js\");\n/* harmony import */ var react__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(react__WEBPACK_IMPORTED_MODULE_0__);\n/* harmony import */ var _styles_css__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./styles.css */ \"./js/src/react/components/TabContent/TabSettingsGeneral/styles.css\");\n/* harmony import */ var _FormElements_InputHidden__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ../../FormElements/InputHidden */ \"./js/src/react/components/FormElements/InputHidden.js\");\n/* harmony import */ var _Settings__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ./Settings */ \"./js/src/react/components/TabContent/TabSettingsGeneral/Settings.js\");\n/* harmony import */ var _NoticeBanner_NoticeBanner__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(/*! ../NoticeBanner/NoticeBanner */ \"./js/src/react/components/TabContent/NoticeBanner/NoticeBanner.js\");\n/* harmony import */ var _SettingsOverview_SettingsOverview__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(/*! ../../SettingsOverview/SettingsOverview */ \"./js/src/react/components/SettingsOverview/SettingsOverview.js\");\n/* harmony import */ var _QuickNavigation_QuickNavigation__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(/*! ./QuickNavigation/QuickNavigation */ \"./js/src/react/components/TabContent/TabSettingsGeneral/QuickNavigation/QuickNavigation.js\");\nfunction _slicedToArray(r, e) { return _arrayWithHoles(r) || _iterableToArrayLimit(r, e) || _unsupportedIterableToArray(r, e) || _nonIterableRest(); }\nfunction _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.\"); }\nfunction _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; } }\nfunction _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; }\nfunction _iterableToArrayLimit(r, l) { var t = null == r ? null : \"undefined\" != typeof Symbol && r[Symbol.iterator] || r[\"@@iterator\"]; if (null != t) { var e, n, i, u, a = [], f = !0, o = !1; try { if (i = (t = t.call(r)).next, 0 === l) { if (Object(t) !== t) return; f = !1; } else for (; !(f = (e = i.call(t)).done) && (a.push(e.value), a.length !== l); f = !0); } catch (r) { o = !0, n = r; } finally { try { if (!f && null != t[\"return\"] && (u = t[\"return\"](), Object(u) !== u)) return; } finally { if (o) throw n; } } return a; } }\nfunction _arrayWithHoles(r) { if (Array.isArray(r)) return r; }\n;\n\n\n\n\n\n\nfunction TabSettingsGeneral(_ref) {\n var _sectionData$flags$ke, _sectionData$flags, _settings$objects, _settings$strings, _settings$strings2, _settings$strings3, _settings$objects2, _settings$objects3, _settings$strings4, _settings$strings5, _settings$strings6, _settings$strings7, _settings$flags, _settings$flags2, _settings$strings8, _settings$strings9, _settings$strings0, _settings$strings1, _settings$objects4, _settings$objects5, _settings$objects6;\n var sectionId = _ref.sectionId,\n sectionData = _ref.sectionData,\n isActive = _ref.isActive;\n var _useState = (0,react__WEBPACK_IMPORTED_MODULE_0__.useState)(sectionData),\n _useState2 = _slicedToArray(_useState, 2),\n settings = _useState2[0],\n setSettings = _useState2[1];\n var _useState3 = (0,react__WEBPACK_IMPORTED_MODULE_0__.useState)((_sectionData$flags$ke = (_sectionData$flags = sectionData.flags) === null || _sectionData$flags === void 0 ? void 0 : _sectionData$flags.keyIsOk) !== null && _sectionData$flags$ke !== void 0 ? _sectionData$flags$ke : false),\n _useState4 = _slicedToArray(_useState3, 2),\n keyIsOk = _useState4[0],\n setKeyIsOk = _useState4[1];\n jQuery(document).ready(function () {\n // Show/Hide access key\n jQuery('#showHideLink').on('click', function () {\n jQuery('#spbc_key').val(jQuery('#spbc_key').attr('key'));\n jQuery('#spbc_key+div').show();\n jQuery(this).fadeOut(300);\n });\n\n // Set time zone filed value if key is bad\n // @ToDo unused code?\n if (!+spbcSettings.key_is_ok) {\n var d = new Date();\n jQuery('#spbc_admin_timezone').val(d.getTimezoneOffset() / 60 * -1);\n }\n\n /**\n * Shuffle authentication unique keys and salts\n */\n function spbcShuffleSalts() {\n var data = {};\n data.security = spbcSettings.ajax_nonce;\n data.action = 'spbc_action_shuffle_salts';\n jQuery.ajax({\n type: 'POST',\n async: false,\n url: spbcSettings.ajaxurl,\n data: data,\n success: function success(result) {\n if (result.success) {\n document.location.reload();\n } else {\n alert(result.data);\n }\n }\n });\n }\n\n // Event for shuffle authentication unique keys and salts\n // Enabling the salt update button and repeatedly pressing\n var actionShuffleSaltsCountClicks = 0;\n jQuery(document).on('click', '#action-shuffle-salts', function (e) {\n e.preventDefault();\n var btn = jQuery(this);\n if (btn.hasClass('disabled')) {\n actionShuffleSaltsCountClicks++;\n if (actionShuffleSaltsCountClicks === 3) {\n btn.removeClass('disabled');\n }\n } else {\n actionShuffleSaltsCountClicks = 0;\n if (confirm('Are you sure?')) {\n spbcShuffleSalts();\n }\n }\n });\n jQuery('button[id*=\"action-adjust-change-\"]').click(function (e) {\n e.preventDefault();\n var data = {};\n data.action = 'spbc_action_adjust_change';\n data.adjust = jQuery(this).data('adjust');\n var params = {};\n params.button = document.getElementById('action-adjust-change-' + data.adjust);\n params.success = function () {\n document.location.reload();\n };\n spbcSendAJAXRequest(data, params);\n });\n jQuery('button[id*=\"action-adjust-reverse-\"]').click(function (e) {\n e.preventDefault();\n var data = {};\n data.action = 'spbc_action_adjust_reverse';\n data.adjust = jQuery(this).data('adjust');\n var params = {};\n params.button = document.getElementById('action-adjust-reverse-' + data.adjust);\n params.success = function () {\n document.location.reload();\n };\n spbcSendAJAXRequest(data, params);\n });\n });\n (0,react__WEBPACK_IMPORTED_MODULE_0__.useEffect)(function () {\n var _sectionData$flags2;\n setSettings(sectionData);\n setKeyIsOk((_sectionData$flags2 = sectionData.flags) === null || _sectionData$flags2 === void 0 ? void 0 : _sectionData$flags2.keyIsOk);\n }, [sectionData]);\n return /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement((react__WEBPACK_IMPORTED_MODULE_0___default().Fragment), null, window.spbcSettings && window.spbcSettings.critical > 0 && /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_NoticeBanner_NoticeBanner__WEBPACK_IMPORTED_MODULE_4__[\"default\"], null), keyIsOk && /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_SettingsOverview_SettingsOverview__WEBPACK_IMPORTED_MODULE_5__[\"default\"], {\n settings: (_settings$objects = settings.objects) === null || _settings$objects === void 0 ? void 0 : _settings$objects.settings\n }), /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement(\"form\", {\n id: 'spbc_settings_form',\n method: 'POST',\n action: 'options.php'\n }, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_FormElements_InputHidden__WEBPACK_IMPORTED_MODULE_2__[\"default\"], {\n id: 'option_page',\n name: 'option_page',\n value: (_settings$strings = settings.strings) === null || _settings$strings === void 0 ? void 0 : _settings$strings.settingsSlug\n }), /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_FormElements_InputHidden__WEBPACK_IMPORTED_MODULE_2__[\"default\"], {\n id: 'action',\n name: 'action',\n value: 'update'\n }), /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_FormElements_InputHidden__WEBPACK_IMPORTED_MODULE_2__[\"default\"], {\n id: '_wpnonce',\n name: '_wpnonce',\n value: (_settings$strings2 = settings.strings) === null || _settings$strings2 === void 0 ? void 0 : _settings$strings2.nonce\n }), /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement(\"div\", {\n id: \"spbc-settings-general-wrapper\",\n className: \"spbc-settings-general-wrapper\"\n }, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement(\"div\", {\n id: \"spbc-settings-general-wrapper-settings\"\n }, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_Settings__WEBPACK_IMPORTED_MODULE_3__[\"default\"], {\n settingsSlug: (_settings$strings3 = settings.strings) === null || _settings$strings3 === void 0 ? void 0 : _settings$strings3.settingsSlug,\n settings: (_settings$objects2 = settings.objects) === null || _settings$objects2 === void 0 ? void 0 : _settings$objects2.settings,\n keyIsOk: keyIsOk,\n wpUserRolesOptions: (_settings$objects3 = settings.objects) === null || _settings$objects3 === void 0 ? void 0 : _settings$objects3.wp_user_roles_options,\n verificationEmail: (_settings$strings4 = settings.strings) === null || _settings$strings4 === void 0 ? void 0 : _settings$strings4.two_fa_verification_email_recipient,\n authLoginPageUrl: (_settings$strings5 = settings.strings) === null || _settings$strings5 === void 0 ? void 0 : _settings$strings5.auth_login_page_url,\n techSupportUrlPrefix: (_settings$strings6 = settings.strings) === null || _settings$strings6 === void 0 ? void 0 : _settings$strings6.tech_support_url_field_prefix,\n loginRenameNotifyAdminEmail: (_settings$strings7 = settings.strings) === null || _settings$strings7 === void 0 ? void 0 : _settings$strings7.login_rename_notify_admin_email,\n canActivatePlugins: (_settings$flags = settings.flags) === null || _settings$flags === void 0 ? void 0 : _settings$flags.canActivatePlugins,\n shuffleSaltsUnlocked: (_settings$flags2 = settings.flags) === null || _settings$flags2 === void 0 ? void 0 : _settings$flags2.shuffleSaltsUnlocked,\n visitorCurrentIp: (_settings$strings8 = settings.strings) === null || _settings$strings8 === void 0 ? void 0 : _settings$strings8.visitorCurrentIp,\n nextScheduledScanDescription: (_settings$strings9 = settings.strings) === null || _settings$strings9 === void 0 ? void 0 : _settings$strings9.nextScheduledScanDescription,\n scannerAutoStartManualTimezone: (_settings$strings0 = settings.strings) === null || _settings$strings0 === void 0 ? void 0 : _settings$strings0.scannerAutoStartManualTimezone,\n wlCompanyName: (_settings$strings1 = settings.strings) === null || _settings$strings1 === void 0 ? void 0 : _settings$strings1.wlCompanyName,\n visitorIpSources: (_settings$objects4 = settings.objects) === null || _settings$objects4 === void 0 ? void 0 : _settings$objects4.visitorIpSources,\n scannerExclusionsDir: (_settings$objects5 = settings.objects) === null || _settings$objects5 === void 0 ? void 0 : _settings$objects5.scannerExclusionsDir,\n scannerExclusionsURL: (_settings$objects6 = settings.objects) === null || _settings$objects6 === void 0 ? void 0 : _settings$objects6.scannerExclusionsURL\n })), /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_QuickNavigation_QuickNavigation__WEBPACK_IMPORTED_MODULE_6__[\"default\"], {\n keyIsOk: keyIsOk\n }))));\n}\n;\n\n//# sourceURL=webpack:///./js/src/react/components/TabContent/TabSettingsGeneral/TabSettingsGeneral.js?\n}"); /***/ }, diff --git a/js/src/react/components/TabContent/TabSettingsGeneral/TabSettingsGeneral.js b/js/src/react/components/TabContent/TabSettingsGeneral/TabSettingsGeneral.js index 1ef3b25bd..ff90590b7 100644 --- a/js/src/react/components/TabContent/TabSettingsGeneral/TabSettingsGeneral.js +++ b/js/src/react/components/TabContent/TabSettingsGeneral/TabSettingsGeneral.js @@ -1,8 +1,5 @@ import React, {useEffect, useState} from 'react'; import './styles.css'; -import TabContentHtml from '../../TabContentTypes/Html/TabContentHtml'; -import {sendAjaxRequest} from '../../../modules/Http/Ajax'; -import Preloader from '../../Preloader/Preloader'; import InputHidden from '../../FormElements/InputHidden'; import Settings from './Settings'; import NoticeBanner from '../NoticeBanner/NoticeBanner'; @@ -10,10 +7,8 @@ import SettingsOverview from '../../SettingsOverview/SettingsOverview'; import QuickNavigation from './QuickNavigation/QuickNavigation'; export default function TabSettingsGeneral({sectionId, sectionData, isActive}) { - const [sectionDataLegacy, setSectionDataLegacy] = useState({sectionData}); - const [settings, setSettings] = useState({}); - const [settingsFetched, setSettingsFetched] = useState(false); - const [keyIsOk, setKeyIsOk] = useState(false); + const [settings, setSettings] = useState(sectionData); + const [keyIsOk, setKeyIsOk] = useState(sectionData.flags?.keyIsOk ?? false); jQuery(document).ready(function() { // Show/Hide access key @@ -111,30 +106,9 @@ export default function TabSettingsGeneral({sectionId, sectionData, isActive}) { }); useEffect(() => { - setSectionDataLegacy(sectionData); - fetchContent(); - }, []); - - // @ToDo - need to be moved to the separate hook, same duplication is in `TabContent` component - const fetchContent = async () => { - try { - const requestData = { - action: 'spbct_get_tab_data', - tab_name: sectionId, - }; - const data = await sendAjaxRequest(requestData, true); - setSettings(data); - setKeyIsOk(data.flags?.keyIsOk); - setSettingsFetched(true); - } catch (error) { - console.error('Error fetching content:', error); - setSettingsFetched(true); - } - }; - - if (!settingsFetched) { - return ; - } + setSettings(sectionData); + setKeyIsOk(sectionData.flags?.keyIsOk); + }, [sectionData]); return <> {window.spbcSettings && window.spbcSettings.critical > 0 && } @@ -168,8 +142,6 @@ export default function TabSettingsGeneral({sectionId, sectionData, isActive}) { scannerExclusionsDir={settings.objects?.scannerExclusionsDir} scannerExclusionsURL={settings.objects?.scannerExclusionsURL} /> - {/* Legacy way to render settings, step-by-step refactored */} - From 98756655592f16e1606f77d4d3754843b6eb1bd4 Mon Sep 17 00:00:00 2001 From: Glomberg Date: Wed, 19 Aug 2026 15:09:27 +0300 Subject: [PATCH 4/6] Fix. React. A lot of unused code removed. --- .../field-templates/checkbox.php | 32 --- .../field-templates/hidden.php | 11 - .../field-templates/number.php | 33 --- inc/admin-templates/field-templates/radio.php | 51 ---- .../field-templates/select.php | 44 ---- inc/admin-templates/field-templates/text.php | 38 --- .../field-templates/textarea.php | 37 --- inc/admin-templates/field-templates/time.php | 16 -- inc/spbc-admin.php | 1 - inc/spbc-settings.php | 238 +----------------- js/spbc-settings.min.js | 2 +- js/spbc-settings.min.js.map | 2 +- js/spbc-timeline-widget.min.js | 2 +- js/spbc-timeline-widget.min.js.map | 2 +- js/src/spbc-settings.js | 61 ----- psalm.xml | 1 - 16 files changed, 13 insertions(+), 558 deletions(-) delete mode 100644 inc/admin-templates/field-templates/checkbox.php delete mode 100644 inc/admin-templates/field-templates/hidden.php delete mode 100644 inc/admin-templates/field-templates/number.php delete mode 100644 inc/admin-templates/field-templates/radio.php delete mode 100644 inc/admin-templates/field-templates/select.php delete mode 100644 inc/admin-templates/field-templates/text.php delete mode 100644 inc/admin-templates/field-templates/textarea.php delete mode 100644 inc/admin-templates/field-templates/time.php diff --git a/inc/admin-templates/field-templates/checkbox.php b/inc/admin-templates/field-templates/checkbox.php deleted file mode 100644 index 62aaf9576..000000000 --- a/inc/admin-templates/field-templates/checkbox.php +++ /dev/null @@ -1,32 +0,0 @@ - - - - - - settings[ $data['parent'] ] ? ' disabled="disabled"' : ''; ?> - - - -/> - - - - - - - - - - -
- diff --git a/inc/admin-templates/field-templates/hidden.php b/inc/admin-templates/field-templates/hidden.php deleted file mode 100644 index 8c6f4c1c8..000000000 --- a/inc/admin-templates/field-templates/hidden.php +++ /dev/null @@ -1,11 +0,0 @@ - - -/> diff --git a/inc/admin-templates/field-templates/number.php b/inc/admin-templates/field-templates/number.php deleted file mode 100644 index 7b39030ce..000000000 --- a/inc/admin-templates/field-templates/number.php +++ /dev/null @@ -1,33 +0,0 @@ - - - -   - - -settings[ $data['parent'] ] ? ' disabled="disabled"' : '') - . ' value="' . $data['value'] . '" ' - . ' min="' . $data['min'] . '" ' - . ' max="' . $data['max'] . '" ' - . ($data['required'] ? ' required="required"' : '') - . '>'; - -?> - - -   - - - -
- diff --git a/inc/admin-templates/field-templates/radio.php b/inc/admin-templates/field-templates/radio.php deleted file mode 100644 index b42d98504..000000000 --- a/inc/admin-templates/field-templates/radio.php +++ /dev/null @@ -1,51 +0,0 @@ - - - - - - - - - - - -
- - -' . $text . ''; - return; -} - -foreach ($data['options'] as $option) { - echo '' - . '' - . ''; - echo '     '; -} - -?> diff --git a/inc/admin-templates/field-templates/select.php b/inc/admin-templates/field-templates/select.php deleted file mode 100644 index 8a4f86ae5..000000000 --- a/inc/admin-templates/field-templates/select.php +++ /dev/null @@ -1,44 +0,0 @@ - -   - - -'; - -foreach ($data['options'] as $option) { - echo '' - . $option['label'] - . ''; -} -echo ''; -?> - - - - - - -
- diff --git a/inc/admin-templates/field-templates/text.php b/inc/admin-templates/field-templates/text.php deleted file mode 100644 index 18f100574..000000000 --- a/inc/admin-templates/field-templates/text.php +++ /dev/null @@ -1,38 +0,0 @@ - -   - - -data['moderate'] ? ' disabled="disabled"' : '') - . ($data['class'] ? ' class="' . $data['class'] . '"' : '') - . ($data['required'] ? ' required="required"' : '') - . 'value="' . ($data['value'] ?: $affiliate_short_code) . '" ' - . $readonly - . ($data['disabled'] || ($data['parent'] && ! $data['parent_value']) ? ' disabled="disabled"' : '') - . (! $data['children'] ? '' : ' children="' . implode(",", $data['children']) . '"') - . (! $data['children'] ? '' : ' onchange="spbcSettingsDependencies(\'' . implode(",", $data['children']) . '\')"') - . ' />'; -?> - - -   - - - - - - - -
- diff --git a/inc/admin-templates/field-templates/textarea.php b/inc/admin-templates/field-templates/textarea.php deleted file mode 100644 index 804d99907..000000000 --- a/inc/admin-templates/field-templates/textarea.php +++ /dev/null @@ -1,37 +0,0 @@ - - - - - - - -
- - - -settings[ $data['parent'] ] ? ' disabled="disabled"' : '') - . ' class="spbc_setting__textarea"' - . ' >' - . ($data['value'] ?: '') - . ''; -?> - - -   - - - -
- diff --git a/inc/admin-templates/field-templates/time.php b/inc/admin-templates/field-templates/time.php deleted file mode 100644 index 440d86d8f..000000000 --- a/inc/admin-templates/field-templates/time.php +++ /dev/null @@ -1,16 +0,0 @@ -settings[ $data['parent'] ] ? ' disabled="disabled"' : '') - . ' value="' . $data['value'] . '" ' - . ($data['required'] ? ' required="required"' : '') - . '>'; diff --git a/inc/spbc-admin.php b/inc/spbc-admin.php index 5fc6db3cd..8c9ffb112 100644 --- a/inc/spbc-admin.php +++ b/inc/spbc-admin.php @@ -145,7 +145,6 @@ function spbc_admin_init() add_action('wp_ajax_spbc_settings__check_renew_banner', 'spbc_settings__check_renew_banner'); add_action('wp_ajax_spbc_get_key_auto', 'spbc_get_key_auto'); add_action('wp_ajax_spbc_save_key', 'spbc_save_key'); - add_action('wp_ajax_spbc_update_account_email', 'spbc_settings__update_account_email'); add_action('wp_ajax_spbc_create_support_user', 'spbc_settings__spbc_create_support_user'); // Confirm the email to activate 2FA diff --git a/inc/spbc-settings.php b/inc/spbc-settings.php index 92b3ddd3a..2bd945c04 100644 --- a/inc/spbc-settings.php +++ b/inc/spbc-settings.php @@ -6,10 +6,8 @@ use CleantalkSP\SpbctWP\API; use CleantalkSP\SpbctWP\Cron as SpbcCron; use CleantalkSP\SpbctWP\Escape; -use CleantalkSP\SpbctWP\FileEditorDisabler\FileEditorDisabler; use CleantalkSP\SpbctWP\Helpers\Arr; use CleantalkSP\SpbctWP\Helpers\IP; -use CleantalkSP\SpbctWP\HTTP\CDNHeadersChecker; use CleantalkSP\SpbctWP\LinkConstructor; use CleantalkSP\SpbctWP\ListTable; use CleantalkSP\SpbctWP\Scanner; @@ -25,11 +23,8 @@ use CleantalkSP\SpbctWP\SpbcRateLimit\SpbcRateLimiter; use CleantalkSP\SpbctWP\Variables\Cookie; use CleantalkSP\SpbctWP\Views\Settings; -use CleantalkSP\SpbctWP\VulnerabilityAlarm\VulnerabilityAlarmView; use CleantalkSP\Variables\Post; use CleantalkSP\Variables\Server; -use CleantalkSP\SpbctWP\UploadDirPreventPhpExecutionModule\UploadDirPreventPhpExecution; -use CleantalkSP\SpbctWP\UsersPassCheckModule\UserPassCheckView; // Prevent direct call if ( ! defined('ABSPATH') ) { @@ -192,16 +187,6 @@ function spbc_settings__register() ), ), ), - // Settings - 'settings_general' => array( - 'type' => 'tab', - 'title' => __('General Settings', 'security-malware-firewall'), - 'icon' => 'spbc-icon-sliders', - 'class_prefix' => 'spbc', - 'ajax' => true, - 'after' => 'spbc_settings_draw_save_button_bottom_block', - 'sections' => array(), - ), // Summary 'summary' => array( 'type' => 'tab', @@ -291,7 +276,7 @@ function spbc_settings__register() * * @return array Processed elements */ -function spbc_settings__register_sections_and_fields($elems, $_section_name = '') +function spbc_settings__register_sections_and_fields($elems) { global $spbc; @@ -534,111 +519,6 @@ function spbct_settings__the_settings_tab_draw($elem) echo $settings_content; } -/** - * @param $field - * - * @return void - */ -function spbc_settings__field__draw($field) -{ - global $spbc; - - echo '
'; - - try { - spbc_settings__field__draw_field_template($field['input_type'], $field); - } catch (\Exception $exception) { - echo $exception->getMessage(); - } - - echo '
'; -} - -/** - * @throws Exception - */ -function spbc_settings__field__draw_field_template($type_field, array $data) -{ - $template_part = SPBC_PLUGIN_DIR . '/inc/admin-templates/field-templates/' . $type_field . '.php'; - - if (!file_exists($template_part)) { - throw new \Exception('Not founded template part.'); - } - - include $template_part; - unset($data); -} - -function spbc_human_time_to_seconds($human_time) -{ - $human_time = explode(' ', $human_time); - - switch (true) { - case strpos($human_time[1], 'second') !== false: - $seconds = (int)$human_time[0] * 1; - break; - case strpos($human_time[1], 'min') !== false: - $seconds = (int)$human_time[0] * 60; - break; - case strpos($human_time[1], 'hour') !== false: - $seconds = (int)$human_time[0] * 3600; - break; - case strpos($human_time[1], 'day') !== false: - $seconds = (int)$human_time[0] * 86400; - break; - case strpos($human_time[1], 'week') !== false: - $seconds = (int)$human_time[0] * 86400 * 7; - break; - case strpos($human_time[1], 'month') !== false: - $seconds = (int)$human_time[0] * 86400 * 30; - break; - case strpos($human_time[1], 'year') !== false: - $seconds = (int)$human_time[0] * 86400 * 365; - break; - default: - $seconds = (int)$human_time[0]; - break; - } - - return $seconds; -} - -/** - * Convert seconds to a user-friendly text string of % days/hours/minutes. - * @param int $seconds seconds to convert. - * @return string converted string like "2 hours", "5 minutes", "1 day". If seconds can not be rounded, returns "% seconds". - */ -function spbc_seconds_to_human_time($seconds) -{ - if ( $seconds % 60 !== 0 ) { - return $seconds . ' seconds'; - } - - switch (true) { - case $seconds / 60 / 60 < 1: - $output = $seconds / 60; - if ( $output < 1 ) { - $output = $seconds == 1 ? $seconds . ' second' : $seconds . ' seconds'; - break; - } - $output .= $seconds > 60 ? ' minutes' : ' minute'; - break; - case $seconds / 60 / 60 / 24 < 1: - $output = $seconds / 60 / 60; - $output .= $seconds > 3600 ? ' hours' : ' hour'; - break; - case $seconds / 60 / 60 / 24 / 30 < 1: - $output = $seconds / 60 / 60 / 24; - $output .= $seconds > 86400 ? ' days' : ' day'; - break; - default: - $output = $seconds . ' seconds'; - break; - } - - return $output; -} - /** * Messages output for error block. * Since 2.159.1 it returns array of strings. @@ -755,22 +635,6 @@ function spbc_tab__summary() printf($template, ...$content_parts); } -function spbc_field_banners() -{ - global $spbc_tpl; - // Rate banner - // echo sprintf($spbc_tpl['spbc_rate_plugin_tpl'], - // SPBC_NAME - // ); - // Translate banner - if (substr(get_locale(), 0, 2) != 'en') { - echo sprintf( - $spbc_tpl['spbc_translate_banner_tpl'], - substr(get_locale(), 0, 2) - ); - } -} - /** * Displays a compact block with list of all main security features and their on/off status (traffic light). * Grouped by settings block titles. Placed in General Settings tab between the request treatment banner and the Access Key. @@ -3601,58 +3465,6 @@ function spbc_validate_ajax_access_key($apikey, $nonce_check) return $apikey; } -function spbc_settings__update_account_email($direct_call = false) -{ - if ( ! $direct_call) { - spbc_check_ajax_referer('spbc_secret_nonce', 'security'); - } - - global $spbc; - - $account_email = Post::getString('accountEmail'); // validation is done next line - - // not valid email - if (!$account_email || !filter_var(Post::getString('accountEmail'), FILTER_VALIDATE_EMAIL)) { - die( - json_encode( - array( - 'error' => 'Please, enter valid email.' - ) - ) - ); - } - - // email not changed - if (isset($spbc->data['account_email']) && $account_email === $spbc->data['account_email']) { - die( - json_encode( - array( - 'success' => 'ok' - ) - ) - ); - } - - $spbc->data['account_email'] = $account_email; - $spbc->save('data'); - - // Link GET ACCESS KEY MANUALLY - $manually_link = sprintf( - 'https://cleantalk.org/register?platform=wordpress&email=%s&website=%s', - urlencode(spbc_get_admin_email()), - urlencode(get_bloginfo('url')) - ); - - die( - json_encode( - array( - 'success' => 'ok', - 'manuallyLink' => $manually_link - ) - ) - ); -} - function spbc_settings__spbc_create_support_user($direct_call = false) { if ( ! $direct_call) { @@ -4418,7 +4230,7 @@ function spbc_bulk_actions_description() add_action('spbc_before_returning_settings', 'spbc__send_local_settings_to_api'); /** - * Hook action to untrack users outside of UsersPassCheck roles array. + * Hook action to untrack users outside UsersPassCheck roles array. */ add_action('spbc_before_returning_settings', array(\CleantalkSP\SpbctWP\UsersPassCheckModule\UsersPassCheckModel::class, 'hookUntrackUsers')); @@ -4436,7 +4248,6 @@ function spbc__send_local_settings_to_api($settings) function spbc_cdn_checker__run_check_on_settings_change($settings) { if ( isset($settings['secfw__get_ip__enable_cdn_auto_self_check']) && $settings['secfw__get_ip__enable_cdn_auto_self_check'] != 0) { - //CDNHeadersChecker::sendCDNCheckerRequest(); SpbcCron::updateTask('cdn_check', 'spbc_cdn_checker__send_request', 86400, time() + 60); } } @@ -4545,13 +4356,6 @@ function spbc_scanner__cure_log_data_prepare(&$table) } } -function spbc_render_links_to_tag($value) -{ - $pattern = "/(https?:\/\/[^\s]+)/"; - $value = preg_replace($pattern, '$1', $value); - return Escape::escKsesPreset($value, 'spbc_settings__display__notifications'); -} - function spbc_scanner__last_scan_info($direct_call = false) { global $spbc; @@ -4589,19 +4393,13 @@ function spbc_scanner__last_scan_info($direct_call = false) */ function spbc_get_next_scan_launch_time_text() { - global $spbc; - - $task = \CleantalkSP\SpbctWP\Cron::getTask('scanner__launch'); - if ($spbc->settings['scanner__auto_start'] - && isset($task['next_call']) - ) { - return sprintf( - ' ' . __('The next automatic scan is scheduled on %s %s.', 'security-malware-firewall'), - date('M d, Y H:i:s', $task['next_call'] + ((float)get_option('gmt_offset') * 3600)), - spbc_wp_timezone_string() - ); - } - return ''; + $next_scan_time = spbc_get_next_scan_launch_time(); + return $next_scan_time + ? sprintf( + ' ' . __('The next automatic scan is scheduled on %s.', 'security-malware-firewall'), + $next_scan_time + ) + : ''; } /** @@ -4905,24 +4703,6 @@ function spbc_drop_to_defaults_on_key_clearance(\CleantalkSP\SpbctWP\State $spbc return $spbc; } -/** - * Draws custom save button block with navigator backlink button with echo. - * @return void *echo - */ -function spbc_settings_draw_save_button_bottom_block() -{ - $template = ' - '; - printf( - $template, - __('Save Changes', 'security-malware-firewall'), - __('Back to navigation', 'security-malware-firewall') - ); -} - /** * Get HTML content for change role template * @return void *echo diff --git a/js/spbc-settings.min.js b/js/spbc-settings.min.js index af31b2f07..218db75a5 100644 --- a/js/spbc-settings.min.js +++ b/js/spbc-settings.min.js @@ -1,2 +1,2 @@ -function spbcSwitchTab(e,t){var s=e.classList[1].replace("spbc_tab_nav-",""),n=new URLSearchParams(window.location.search),c=(n.set("spbc_tab",s),window.location.hash),c=window.location.pathname+"?"+n.toString()+c;if(history.replaceState(null,"",c),"backups"!==n.get("spbc_tab")&&jQuery(".spbc_tab_nav-backups").hide(),jQuery(".spbc_tab_nav").removeClass("spbc_tab_nav--active"),jQuery(".spbc_tab").removeClass("spbc_tab--active"),jQuery(e).addClass("spbc_tab_nav--active"),jQuery(".spbc_tab-"+s).addClass("spbc_tab--active"),jQuery(e).data("loaded")){if(t&&t.action)switch(t.action){case"highlight":spbcHighlightElement(t.target,t.times);break;case"click":setTimeout(function(){jQuery("#"+t.additional.target).click()},500)}}else{c={action:"spbc_settings__draw_elements",tab_name:s,security:spbcSettings.ajax_nonce};t={callback:spbcDrawSettingsCallback,notJson:!0,additional:t||null},spbcSendAJAXRequest(c,t,e)}}function spbcDrawSettingsCallback(e,t,s,n){jQuery(n).data("loaded",!0),jQuery(".spbc_tab-"+t.tab_name).replaceWith(e);n=jQuery(".spbc_tab-"+t.tab_name);if(n.addClass("spbc_tab--active"),jQuery(document).off("click",".spbc_long_recommendation__show"),jQuery(document).on("click",".spbc_long_recommendation__show",function(){spbcSettingsShowRecommendation(self=jQuery(this),self.attr("data-setting")||self.attr("setting"))}),s.additional)switch(s.additional.action){case"highlight":spbcHighlightElement(s.additional.target,s.additional.times);break;case"click":setTimeout(function(){jQuery("#"+s.additional.target).click()},500)}jQuery(n).on("click",".spbc_hint-send_"+t.tab_name,function(){jQuery(".spbc_hint-send_"+t.tab_name).hide(),spbcSendAJAXRequest({action:"spbc_send_"+t.tab_name,tab_name:t.tab_name},{callback:spbcSendLogsCallback})}),window.location.hash&&null!==(e=document.getElementById(window.location.hash.substring(1)))&&e.scrollIntoView(),"function"==typeof spbcHighlightCriticalFilesNotice&&spbcHighlightCriticalFilesNotice(!1),spbcSaveSettingsButtonMobileLayout(),window.addEventListener("resize",e=>{spbcSaveSettingsButtonMobileLayout()})}function spbcSaveSettingsButtonMobileLayout(){var e=document.querySelector('div[name="spbc_settings--save_button_custom"]');e&&(900");var s=jQuery("#spbc_long_desc__"+t);s.append("").append("
").css({top:e.position().top+25,left:e.position().left+5}),spbcSendAJAXRequest({action:"spbc_settings__get_recommendation",setting_id:t},{spinner:s.children("img"),callback:function(e,t,s,n){n.empty().append("
").append("").append("

"+e.title+"

").append("

"+e.desc+"

"),jQuery(document).on("click",c)}},s)}function spbcStartShowHide(){jQuery(".spbcShortText").off("mouseover").on("mouseover",function(){jQuery(this).next().show()}).off("mouseout").on("mouseout",function(){jQuery(this).next().hide()}),jQuery(".spbcFullText").off("mouseout").on("mouseout",function(){jQuery(this).hide()}).off("mouseover").on("mouseover",function(){jQuery(this).show()})}function spbcScannerReloadScanInfo(){jQuery(".spbc_hint__last_scan_title").text(spbcScaner.look_below_for_scan_res);spbcSendAJAXRequest({action:"spbc_scanner__last_scan_info"},{callback:e=>{document.getElementById("spbc_scanner__last_scan_info").innerText=e.data}})}function spbcBannerCheck(){let c=setInterval(function(){spbcSendAJAXRequest({action:"spbc_settings__check_renew_banner"},{callback:function(e,t,s,n){e.close_renew_banner&&(jQuery("#spbc_renew_notice").length&&jQuery("#spbc_renew_notice").hide("slow"),jQuery("#spbc_trial_notice").length&&jQuery("#spbc_trial_notice").hide("slow"),clearInterval(c))}})},6e4)}function spbcToggleClass(t,s){for(let e=0;e",jQuery(c).insertAfter(jQuery(a)),jQuery("#spbc_settings_templates_import_button .spbc_success").show(300),setTimeout(function(){jQuery("#spbc_settings_templates_import_button .spbc_success").hide(300)},2e3),document.addEventListener("spbcModalClosed",function(e){document.location.reload()}),setTimeout(function(){spbcModal.close()},2e3)):(c="

"+e.data+"

",jQuery(c).insertAfter(jQuery(a)))}})}}),jQuery(document).on("click","#spbc_settings_templates_export_button",function(){jQuery("#spbc-ajax-result").remove();var t=jQuery("option:selected",jQuery("#spbc_settings_templates_export")),s=jQuery("#spbc_settings_templates_export_name");if(s.css("border-color","inherit"),void 0===t.data("id"))console.log('Attribute "data-id" not set for the option.');else{let e;if("new_template"===t.data("id")){var n=s.val();if(""===n)return void s.css("border-color","red");e={template_name:n}}else e={template_id:t.data("id")};let a=this;spbcSendAJAXRequest({action:"spbc_settings_templates_export",data:e},{timeout:25e3,button:a,spinner:jQuery("#spbc_settings_templates_export_button .spbc_preloader_button"),callback:function(e,t,s,n){let c;e.success?(c="

"+e.data+"

",jQuery(c).insertAfter(jQuery(a)),jQuery("#spbc_settings_templates_export_button .spbc_success").show(300),setTimeout(function(){jQuery("#spbc_settings_templates_export_button .spbc_success").hide(300)},2e3),document.addEventListener("spbcModalClosed",function(e){document.location.reload()}),setTimeout(function(){spbcModal.close()},2e3)):(c="

"+e.data+"

",jQuery(c).insertAfter(jQuery(a)))}})}}),jQuery(document).on("click","#spbc_settings_templates_reset_button",function(){let a=this;spbcSendAJAXRequest({action:"spbc_settings_templates_reset"},{timeout:25e3,button:a,spinner:jQuery("#spbc_settings_templates_reset_button .spbc_preloader_button"),callback:function(e,t,s,n){let c;e.success?(c="

"+e.data+"

",jQuery(c).insertAfter(jQuery(a)),jQuery("#spbc_settings_templates_reset_button .spbc_success").show(300),setTimeout(function(){jQuery("#spbc_settings_templates_reset_button .spbc_success").hide(300)},2e3),document.addEventListener("spbcModalClosed",function(e){document.location.reload()}),setTimeout(function(){spbcModal.close()},2e3)):(c="

"+e.data+"

",jQuery(c).insertAfter(jQuery(a)))}})}),jQuery(document).on("change","#spbc_settings_templates_export",function(){"new_template"===jQuery("option:selected",this).data("id")?jQuery(this).parent().parent().find("#spbc_settings_templates_export_name").show():jQuery(this).parent().parent().find("#spbc_settings_templates_export_name").hide()}),jQuery(document).on("click","#spbc-change-account-email",function(e){e.preventDefault();var e=jQuery(this),t=jQuery("#spbc-account-email"),s=t.text();spbcToggleClass(e,"active"),e.hasClass("active")?(e.text(e.data("save-text")),t.attr("contenteditable","true"),t.focus(),t.on("keydown",function(e){"Enter"===e.code&&e.preventDefault()}),t.on("input",function(e){"insertParagraph"===e.inputType&&e.preventDefault()})):(spbcSendAJAXRequest({action:"spbc_update_account_email",accountEmail:s},{timeout:5e3,callback:function(e,t,s,n){void 0!==e.success&&"ok"===e.success&&void 0!==e.manuallyLink&&jQuery("#spbc-key-manually-link").attr("href",e.manuallyLink),void 0!==e.error&&jQuery("#spbc-account-email").css("border-color","red")},errorOutput:function(e){spbcModal.loaded=e,spbcModal.open()}}),t.attr("contenteditable","false"),e.text(e.data("default-text")))}),jQuery(document).on("click",".spbct_notice-dismiss",function(e){var t=this.parentElement.classList;let s=t[t.length-1];document.querySelectorAll("."+s).forEach(e=>{e.parentElement.setAttribute("style","display:none;"),spbcSetCookie(s,1,2592e3)})}),jQuery(document).off("click",".spbc_long_description__show"),jQuery(document).on("click",".spbc_long_description__show",function(){self=jQuery(this),spbcHandleLongDescription(self,self.attr("data-setting")||self.attr("setting"))}),jQuery(document).off("click",".spbc_long_recommendation__show"),jQuery(document).on("click",".spbc_long_recommendation__show",function(){spbcSettingsShowRecommendation(self=jQuery(this),self.attr("data-setting")||self.attr("setting"))}),document.referrer&&-1!==document.referrer.indexOf("page=spbc")||sessionStorage.removeItem("spbc_trial_renew_fullpage_dismissed"),sessionStorage.getItem("spbc_trial_renew_fullpage_dismissed")&&(jQuery(".spbc-trial-renew-fullpage").hide(),jQuery("#spbct-page--react").show()),jQuery("body").on("click",".spbc-trial-renew-fullpage .notice-dismiss",function(e){e.preventDefault(),sessionStorage.setItem("spbc_trial_renew_fullpage_dismissed","1"),jQuery(".spbc-trial-renew-fullpage").hide(),jQuery("#spbct-page--react").show()}),jQuery(document).off("click","#spbc_debug__run_pass_check_btn").on("click","#spbc_debug__run_pass_check_btn",function(){let t=jQuery(this),s=jQuery("#spbc_debug__pass_check_status");t.prop("disabled",!0),s.css("color","").text("Resetting records and running check, please wait..."),jQuery.ajax({type:"POST",url:ajaxurl,data:{action:"spbc_debug_users_pass_check_run",security:spbcSettings.ajax_nonce},success:function(e){t.prop("disabled",!1),e.success?s.css("color","green").html("✓ Done! Total: "+e.data.total+", Checked: "+e.data.checked+", Leaked: "+e.data.leaked+" — You can now test the login form in incognito."):s.css("color","red").text("Error: "+(e.data||"unknown error"))},error:function(e){t.prop("disabled",!1),s.css("color","red").text("Request failed: "+e.status+" "+e.statusText)}})})}); +function spbcSwitchTab(e,t){var s=e.classList[1].replace("spbc_tab_nav-",""),n=new URLSearchParams(window.location.search),c=(n.set("spbc_tab",s),window.location.hash),c=window.location.pathname+"?"+n.toString()+c;if(history.replaceState(null,"",c),"backups"!==n.get("spbc_tab")&&jQuery(".spbc_tab_nav-backups").hide(),jQuery(".spbc_tab_nav").removeClass("spbc_tab_nav--active"),jQuery(".spbc_tab").removeClass("spbc_tab--active"),jQuery(e).addClass("spbc_tab_nav--active"),jQuery(".spbc_tab-"+s).addClass("spbc_tab--active"),jQuery(e).data("loaded")){if(t&&t.action)switch(t.action){case"highlight":spbcHighlightElement(t.target,t.times);break;case"click":setTimeout(function(){jQuery("#"+t.additional.target).click()},500)}}else{c={action:"spbc_settings__draw_elements",tab_name:s,security:spbcSettings.ajax_nonce};t={callback:spbcDrawSettingsCallback,notJson:!0,additional:t||null},spbcSendAJAXRequest(c,t,e)}}function spbcDrawSettingsCallback(e,t,s,n){jQuery(n).data("loaded",!0),jQuery(".spbc_tab-"+t.tab_name).replaceWith(e);n=jQuery(".spbc_tab-"+t.tab_name);if(n.addClass("spbc_tab--active"),jQuery(document).off("click",".spbc_long_recommendation__show"),jQuery(document).on("click",".spbc_long_recommendation__show",function(){spbcSettingsShowRecommendation(self=jQuery(this),self.attr("data-setting")||self.attr("setting"))}),s.additional)switch(s.additional.action){case"highlight":spbcHighlightElement(s.additional.target,s.additional.times);break;case"click":setTimeout(function(){jQuery("#"+s.additional.target).click()},500)}jQuery(n).on("click",".spbc_hint-send_"+t.tab_name,function(){jQuery(".spbc_hint-send_"+t.tab_name).hide(),spbcSendAJAXRequest({action:"spbc_send_"+t.tab_name,tab_name:t.tab_name},{callback:spbcSendLogsCallback})}),window.location.hash&&null!==(e=document.getElementById(window.location.hash.substring(1)))&&e.scrollIntoView(),"function"==typeof spbcHighlightCriticalFilesNotice&&spbcHighlightCriticalFilesNotice(!1),spbcSaveSettingsButtonMobileLayout(),window.addEventListener("resize",e=>{spbcSaveSettingsButtonMobileLayout()})}function spbcSaveSettingsButtonMobileLayout(){var e=document.querySelector('div[name="spbc_settings--save_button_custom"]');e&&(900");var s=jQuery("#spbc_long_desc__"+t);s.append("").append("
").css({top:e.position().top+25,left:e.position().left+5}),spbcSendAJAXRequest({action:"spbc_settings__get_recommendation",setting_id:t},{spinner:s.children("img"),callback:function(e,t,s,n){n.empty().append("
").append("").append("

"+e.title+"

").append("

"+e.desc+"

"),jQuery(document).on("click",c)}},s)}function spbcStartShowHide(){jQuery(".spbcShortText").off("mouseover").on("mouseover",function(){jQuery(this).next().show()}).off("mouseout").on("mouseout",function(){jQuery(this).next().hide()}),jQuery(".spbcFullText").off("mouseout").on("mouseout",function(){jQuery(this).hide()}).off("mouseover").on("mouseover",function(){jQuery(this).show()})}function spbcScannerReloadScanInfo(){jQuery(".spbc_hint__last_scan_title").text(spbcScaner.look_below_for_scan_res);spbcSendAJAXRequest({action:"spbc_scanner__last_scan_info"},{callback:e=>{document.getElementById("spbc_scanner__last_scan_info").innerText=e.data}})}function spbcBannerCheck(){let c=setInterval(function(){spbcSendAJAXRequest({action:"spbc_settings__check_renew_banner"},{callback:function(e,t,s,n){e.close_renew_banner&&(jQuery("#spbc_renew_notice").length&&jQuery("#spbc_renew_notice").hide("slow"),jQuery("#spbc_trial_notice").length&&jQuery("#spbc_trial_notice").hide("slow"),clearInterval(c))}})},6e4)}function spbcToggleClass(t,s){for(let e=0;e",jQuery(c).insertAfter(jQuery(a)),jQuery("#spbc_settings_templates_import_button .spbc_success").show(300),setTimeout(function(){jQuery("#spbc_settings_templates_import_button .spbc_success").hide(300)},2e3),document.addEventListener("spbcModalClosed",function(e){document.location.reload()}),setTimeout(function(){spbcModal.close()},2e3)):(c="

"+e.data+"

",jQuery(c).insertAfter(jQuery(a)))}})}}),jQuery(document).on("click","#spbc_settings_templates_export_button",function(){jQuery("#spbc-ajax-result").remove();var t=jQuery("option:selected",jQuery("#spbc_settings_templates_export")),s=jQuery("#spbc_settings_templates_export_name");if(s.css("border-color","inherit"),void 0===t.data("id"))console.log('Attribute "data-id" not set for the option.');else{let e;if("new_template"===t.data("id")){var n=s.val();if(""===n)return void s.css("border-color","red");e={template_name:n}}else e={template_id:t.data("id")};let a=this;spbcSendAJAXRequest({action:"spbc_settings_templates_export",data:e},{timeout:25e3,button:a,spinner:jQuery("#spbc_settings_templates_export_button .spbc_preloader_button"),callback:function(e,t,s,n){let c;e.success?(c="

"+e.data+"

",jQuery(c).insertAfter(jQuery(a)),jQuery("#spbc_settings_templates_export_button .spbc_success").show(300),setTimeout(function(){jQuery("#spbc_settings_templates_export_button .spbc_success").hide(300)},2e3),document.addEventListener("spbcModalClosed",function(e){document.location.reload()}),setTimeout(function(){spbcModal.close()},2e3)):(c="

"+e.data+"

",jQuery(c).insertAfter(jQuery(a)))}})}}),jQuery(document).on("click","#spbc_settings_templates_reset_button",function(){let a=this;spbcSendAJAXRequest({action:"spbc_settings_templates_reset"},{timeout:25e3,button:a,spinner:jQuery("#spbc_settings_templates_reset_button .spbc_preloader_button"),callback:function(e,t,s,n){let c;e.success?(c="

"+e.data+"

",jQuery(c).insertAfter(jQuery(a)),jQuery("#spbc_settings_templates_reset_button .spbc_success").show(300),setTimeout(function(){jQuery("#spbc_settings_templates_reset_button .spbc_success").hide(300)},2e3),document.addEventListener("spbcModalClosed",function(e){document.location.reload()}),setTimeout(function(){spbcModal.close()},2e3)):(c="

"+e.data+"

",jQuery(c).insertAfter(jQuery(a)))}})}),jQuery(document).on("change","#spbc_settings_templates_export",function(){"new_template"===jQuery("option:selected",this).data("id")?jQuery(this).parent().parent().find("#spbc_settings_templates_export_name").show():jQuery(this).parent().parent().find("#spbc_settings_templates_export_name").hide()}),jQuery(document).on("click",".spbct_notice-dismiss",function(e){var t=this.parentElement.classList;let s=t[t.length-1];document.querySelectorAll("."+s).forEach(e=>{e.parentElement.setAttribute("style","display:none;"),spbcSetCookie(s,1,2592e3)})}),jQuery(document).off("click",".spbc_long_description__show"),jQuery(document).on("click",".spbc_long_description__show",function(){self=jQuery(this),spbcHandleLongDescription(self,self.attr("data-setting")||self.attr("setting"))}),jQuery(document).off("click",".spbc_long_recommendation__show"),jQuery(document).on("click",".spbc_long_recommendation__show",function(){spbcSettingsShowRecommendation(self=jQuery(this),self.attr("data-setting")||self.attr("setting"))}),document.referrer&&-1!==document.referrer.indexOf("page=spbc")||sessionStorage.removeItem("spbc_trial_renew_fullpage_dismissed"),sessionStorage.getItem("spbc_trial_renew_fullpage_dismissed")&&(jQuery(".spbc-trial-renew-fullpage").hide(),jQuery("#spbct-page--react").show()),jQuery("body").on("click",".spbc-trial-renew-fullpage .notice-dismiss",function(e){e.preventDefault(),sessionStorage.setItem("spbc_trial_renew_fullpage_dismissed","1"),jQuery(".spbc-trial-renew-fullpage").hide(),jQuery("#spbct-page--react").show()}),jQuery(document).off("click","#spbc_debug__run_pass_check_btn").on("click","#spbc_debug__run_pass_check_btn",function(){let t=jQuery(this),s=jQuery("#spbc_debug__pass_check_status");t.prop("disabled",!0),s.css("color","").text("Resetting records and running check, please wait..."),jQuery.ajax({type:"POST",url:ajaxurl,data:{action:"spbc_debug_users_pass_check_run",security:spbcSettings.ajax_nonce},success:function(e){t.prop("disabled",!1),e.success?s.css("color","green").html("✓ Done! Total: "+e.data.total+", Checked: "+e.data.checked+", Leaked: "+e.data.leaked+" — You can now test the login form in incognito."):s.css("color","red").text("Error: "+(e.data||"unknown error"))},error:function(e){t.prop("disabled",!1),s.css("color","red").text("Request failed: "+e.status+" "+e.statusText)}})})}); //# sourceMappingURL=spbc-settings.min.js.map diff --git a/js/spbc-settings.min.js.map b/js/spbc-settings.min.js.map index bb24bc3a9..018894135 100644 --- a/js/spbc-settings.min.js.map +++ b/js/spbc-settings.min.js.map @@ -1 +1 @@ -{"version":3,"file":"spbc-settings.min.js","sources":["spbc-settings.js"],"sourcesContent":["// Printf for JS\nString.prototype.printf = function() { // eslint-disable-line no-extend-native\n let formatted = this;\n /* eslint-disable guard-for-in */\n for ( let arg in arguments ) { // eslint-disable-line prefer-rest-params\n let beforeFormatted = formatted.substring(0, formatted.indexOf('%s', 0));\n let afterFormatted = formatted.substring(formatted.indexOf('%s', 0)+2, formatted.length);\n formatted = beforeFormatted + arguments[arg] + afterFormatted; // eslint-disable-line prefer-rest-params\n }\n return formatted;\n};\n\n/**\n * Switching tabs\n *\n * @param {object} tab\n * @param {object} params\n */\nfunction spbcSwitchTab(tab, params) {\n let tabName = tab.classList[1].replace('spbc_tab_nav-', '');\n\n // update url\n let searchParams = new URLSearchParams(window.location.search);\n searchParams.set('spbc_tab', tabName);\n let hashParam = window.location.hash;\n let newRelativePathQuery = window.location.pathname + '?' + searchParams.toString() + hashParam;\n\n history.replaceState(null, '', newRelativePathQuery);\n\n // Hiding a tab 'Backups' if exist\n if (searchParams.get('spbc_tab') !== 'backups') {\n jQuery('.spbc_tab_nav-backups').hide();\n }\n\n // update active tab\n jQuery('.spbc_tab_nav').removeClass('spbc_tab_nav--active');\n jQuery('.spbc_tab').removeClass('spbc_tab--active');\n jQuery(tab).addClass('spbc_tab_nav--active');\n jQuery('.spbc_tab-' + tabName).addClass('spbc_tab--active');\n\n if (!jQuery(tab).data('loaded')) {\n let data = {\n action: 'spbc_settings__draw_elements',\n tab_name: tabName,\n security: spbcSettings.ajax_nonce,\n };\n params = {\n callback: spbcDrawSettingsCallback,\n notJson: true,\n additional: params || null,\n };\n spbcSendAJAXRequest( data, params, tab );\n } else if (params && params.action) {\n switch (params.action) {\n case 'highlight':\n spbcHighlightElement(params.target, params.times);\n break;\n case 'click':\n setTimeout(function() {\n jQuery('#'+params.additional.target).click();\n }, 500);\n break;\n }\n }\n}\n\n/**\n * Draws settings\n *\n * @param {*} result\n * @param {*} data\n * @param {*} params\n * @param {*} obj\n */\nfunction spbcDrawSettingsCallback(result, data, params, obj) {\n jQuery(obj).data('loaded', true);\n jQuery('.spbc_tab-'+data.tab_name).replaceWith(result);\n let tab = jQuery('.spbc_tab-'+data.tab_name);\n tab.addClass('spbc_tab--active');\n\n jQuery(document).off( 'click', '.spbc_long_recommendation__show');\n jQuery(document).on('click', '.spbc_long_recommendation__show', function() {\n self = jQuery(this);\n spbcSettingsShowRecommendation(self, self.attr('data-setting') || self.attr('setting'));\n });\n\n if (params.additional) {\n switch (params.additional.action) {\n case 'highlight':\n spbcHighlightElement(params.additional.target, params.additional.times);\n break;\n case 'click':\n setTimeout(function() {\n jQuery('#'+params.additional.target).click();\n }, 500);\n break;\n }\n }\n\n jQuery(tab).on('click', '.spbc_hint-send_'+data.tab_name, function() {\n jQuery('.spbc_hint-send_'+data.tab_name).hide();\n spbcSendAJAXRequest(\n {action: 'spbc_send_'+data.tab_name, tab_name: data.tab_name},\n {callback: spbcSendLogsCallback},\n );\n });\n\n // scroll to anchor\n if (window.location.hash) {\n let scrollToTag = document.getElementById(window.location.hash.substring(1));\n if (scrollToTag !== null) {\n scrollToTag.scrollIntoView();\n }\n }\n\n if (typeof spbcHighlightCriticalFilesNotice === 'function') {\n spbcHighlightCriticalFilesNotice(false);\n }\n\n spbcSaveSettingsButtonMobileLayout();\n window.addEventListener('resize', (event) => {\n spbcSaveSettingsButtonMobileLayout();\n });\n}\n\n/**\n * A chunk to hide the custom element \"submit button\" on large screen or make sticky on mobile\n */\nfunction spbcSaveSettingsButtonMobileLayout() {\n const selector = document.querySelector('div[name=\"spbc_settings--save_button_custom\"]');\n if (selector) {\n if (window.innerWidth > 900) {\n selector.style.display = 'none';\n } else {\n selector.style.display = 'flex';\n }\n }\n}\n\n/**\n * Sends logs\n *\n * @param {*} result\n * @param {*} data\n * @param {*} params\n * @param {*} obj\n */\nfunction spbcSendLogsCallback(result, data, params, obj) {\n jQuery('.spbc_tab_nav-'+data.tab_name).data('loaded', false);\n spbcSwitchTab(document.getElementsByClassName('spbc_tab_nav-'+data.tab_name)[0]);\n}\n\n/**\n * Disables/enables settings by ID\n *\n * @param {array} settingsIDs\n * @param {number} enable\n */\nfunction spbcSettingsDependenciesbyId(settingsIDs, enable) { // eslint-disable-line no-unused-vars\n if (typeof settingsIDs === 'string') {\n tmp = [];\n tmp.push(settingsIDs);\n settingsIDs = tmp;\n }\n\n enable = typeof enable === 'undefined' ? null : +enable;\n\n settingsIDs.forEach(function(settingID, i, arr) {\n let elem = document.getElementById('spbc_setting_'+settingID);\n let doDisable = function() {\n elem.setAttribute('disabled', 'disabled');\n };\n let doEnable = function() {\n elem.removeAttribute('disabled');\n };\n\n if (enable !== null) {\n // Set\n enable === 1 ? doEnable() : doDisable();\n } else {\n // Switch\n elem.getAttribute('disabled') === null ? doDisable() : doEnable();\n }\n });\n}\n\n/**\n * Recursive in case children found!\n *\n * Settings dependencies\n *\n * @param {array} settingNames\n * @param {number} enable\n */\nfunction spbcSettingsDependencies(settingNames, enable) { // eslint-disable-line no-unused-vars\n // Cast settingNames to array\n const eventTarget = window.event && window.event.target ? window.event.target : null;\n const isRadio = eventTarget && eventTarget.type === 'radio';\n const radioEnabled = isRadio && eventTarget.value == '1';\n const targetEnableFlag = eventTarget ?\n ((isRadio && !radioEnabled) || +eventTarget.checked) :\n null;\n const doEnableFlag = typeof enable === 'undefined' ? targetEnableFlag : +enable;\n\n settingNames = typeof settingNames === 'string' ? settingNames.split(',') : settingNames;\n\n settingNames.forEach(function(settingName, i, arr) {\n let elements = document.getElementsByName('spbc_settings['+settingName+']');\n if (elements.length === 0) {\n // multiple selector case\n elements = document.getElementsByName('spbc_settings['+settingName+'][]');\n }\n\n elements\n .forEach(function( elem, i, arr ) {\n let doDisable = function() {\n elem.setAttribute('disabled', 'disabled');\n };\n let doEnable = function() {\n elem.removeAttribute('disabled');\n };\n if (doEnableFlag !== null) {\n // Set\n doEnableFlag === 1 ? doEnable() : doDisable();\n } else {\n // Switch\n elem.getAttribute('disabled') === null ? doDisable() : doEnable();\n }\n\n let children = elem.getAttribute('data-children');\n if ( children !== null ) {\n spbcSettingsDependencies(children, doEnableFlag && elem.checked);\n }\n });\n });\n}\n\n/**\n * Shows recommendation\n *\n * @param {object} label\n * @param {string} settingId\n */\nfunction spbcSettingsShowRecommendation(label, settingId) {\n let removeRecommendationFunc = function(e) {\n if (\n typeof e === 'undefined' ||\n (\n jQuery(e.target).parent('.spbc_long_desc').length == 0 ||\n jQuery(e.target).hasClass('spbc_long_desc__cancel')\n ) && !jQuery(e.target).hasClass('spbc_long_recommendation__show')\n ) {\n jQuery('.spbc_long_desc').remove();\n jQuery(document).off('click', removeRecommendationFunc);\n }\n };\n\n removeRecommendationFunc();\n\n label.after('
');\n let obj = jQuery('#spbc_long_desc__'+settingId);\n obj.append('')\n .append('
')\n .css({\n top: label.position().top + 25,\n left: label.position().left + 5,\n });\n\n\n spbcSendAJAXRequest(\n {action: 'spbc_settings__get_recommendation', setting_id: settingId},\n {\n spinner: obj.children('img'),\n callback: function(result, data, params, obj) {\n obj.empty()\n .append('
')\n .append('')\n .append('

'+result.title+'

')\n .append('

'+result.desc+'

');\n\n jQuery(document).on('click', removeRecommendationFunc);\n },\n },\n obj,\n );\n}\n\n/**\n * Shows/hides full text\n */\nfunction spbcStartShowHide() { // eslint-disable-line no-unused-vars\n jQuery('.spbcShortText')\n .off('mouseover' )\n .on('mouseover', function() {\n jQuery(this).next().show();\n })\n .off('mouseout' )\n .on('mouseout', function() {\n jQuery(this).next().hide();\n });\n jQuery('.spbcFullText')\n .off('mouseout' )\n .on('mouseout', function() {\n jQuery(this).hide();\n })\n .off('mouseover' )\n .on('mouseover', function() {\n jQuery(this).show();\n });\n}\n\n/**\n * Reloads last scan info\n */\nfunction spbcScannerReloadScanInfo() { // eslint-disable-line no-unused-vars\n jQuery('.spbc_hint__last_scan_title').text(spbcScaner.look_below_for_scan_res);\n const data = {\n action: 'spbc_scanner__last_scan_info',\n };\n const params = {\n callback: (result) => {\n document.getElementById('spbc_scanner__last_scan_info').innerText = result.data;\n },\n };\n spbcSendAJAXRequest(data, params);\n}\n\n/**\n * Checking current account status for renew notice\n */\nfunction spbcBannerCheck() {\n let bannerChecker = setInterval( function() {\n spbcSendAJAXRequest(\n {action: 'spbc_settings__check_renew_banner'},\n {\n callback: function(result, data, params, obj) {\n if (result.close_renew_banner) {\n if (jQuery('#spbc_renew_notice').length) {\n jQuery('#spbc_renew_notice').hide('slow');\n }\n if (jQuery('#spbc_trial_notice').length) {\n jQuery('#spbc_trial_notice').hide('slow');\n }\n clearInterval(bannerChecker);\n }\n },\n },\n );\n }, 60000);\n}\n\njQuery(document).ready(function() {\n if (jQuery('#spbc_renew_notice').length || jQuery('#spbc_trial_notice').length) {\n spbcBannerCheck();\n }\n\n // Sync button migrated to react\n\n if ( spbcSettings.key_changed ) {\n jQuery('#spbc_button__sync').click();\n }\n\n // Get Key Auto button migrated to Signup Wizard\n\n // Import settings\n jQuery( document ).on('click', '#spbc_settings_templates_import_button', function() {\n jQuery('#spbc-ajax-result').remove();\n let optionSelected = jQuery('option:selected', jQuery('#spbc_settings_templates_import'));\n let templateNameInput = jQuery('#spbc_settings_templates_import_name');\n templateNameInput.css('border-color', 'inherit');\n if ( typeof optionSelected.data('id') === 'undefined' ) {\n console.log( 'Attribute \"data-id\" not set for the option.' );\n return;\n }\n let data = {\n 'template_id': optionSelected.data('id'),\n 'template_name': optionSelected.data('name'),\n 'settings': optionSelected.data('settings'),\n };\n let button = this;\n spbcSendAJAXRequest(\n {action: 'spbc_settings_templates_import', data: data},\n {\n timeout: 25000,\n button: button,\n spinner: jQuery('#spbc_settings_templates_import_button .spbc_preloader_button' ),\n callback: function(result, data, params, obj) {\n let selector;\n if (result.success) {\n selector = '

' + result.data + '

';\n jQuery( selector ).insertAfter( jQuery(button) );\n jQuery('#spbc_settings_templates_import_button .spbc_success').show(300);\n setTimeout(function() {\n jQuery('#spbc_settings_templates_import_button .spbc_success').hide(300);\n }, 2000);\n document.addEventListener('spbcModalClosed', function( e ) {\n document.location.reload();\n });\n setTimeout(function() {\n spbcModal.close();\n }, 2000);\n } else {\n selector = '

' + result.data + '

';\n jQuery( selector ).insertAfter( jQuery(button) );\n }\n },\n },\n );\n });\n\n // Export settings\n jQuery( document ).on('click', '#spbc_settings_templates_export_button', function() {\n jQuery('#spbc-ajax-result').remove();\n let optionSelected = jQuery('option:selected', jQuery('#spbc_settings_templates_export'));\n let templateNameInput = jQuery('#spbc_settings_templates_export_name');\n templateNameInput.css('border-color', 'inherit');\n if ( typeof optionSelected.data('id') === 'undefined' ) {\n console.log( 'Attribute \"data-id\" not set for the option.' );\n return;\n }\n let data;\n if ( optionSelected.data('id') === 'new_template' ) {\n let templateName = templateNameInput.val();\n if ( templateName === '' ) {\n templateNameInput.css('border-color', 'red');\n return;\n }\n data = {\n 'template_name': templateName,\n };\n } else {\n data = {\n 'template_id': optionSelected.data('id'),\n };\n }\n let button = this;\n spbcSendAJAXRequest(\n {action: 'spbc_settings_templates_export', data: data},\n {\n timeout: 25000,\n button: button,\n spinner: jQuery('#spbc_settings_templates_export_button .spbc_preloader_button' ),\n callback: function(result, data, params, obj) {\n let selector;\n if (result.success) {\n selector = '

' + result.data + '

';\n jQuery( selector ).insertAfter( jQuery(button) );\n jQuery('#spbc_settings_templates_export_button .spbc_success').show(300);\n setTimeout(function() {\n jQuery('#spbc_settings_templates_export_button .spbc_success').hide(300);\n }, 2000);\n document.addEventListener('spbcModalClosed', function( e ) {\n document.location.reload();\n });\n setTimeout(function() {\n spbcModal.close();\n }, 2000);\n } else {\n selector = '

' + result.data + '

';\n jQuery( selector ).insertAfter( jQuery(button) );\n }\n },\n },\n );\n });\n\n // Reset settings\n jQuery( document ).on('click', '#spbc_settings_templates_reset_button', function() {\n let button = this;\n spbcSendAJAXRequest(\n {action: 'spbc_settings_templates_reset'},\n {\n timeout: 25000,\n button: button,\n spinner: jQuery('#spbc_settings_templates_reset_button .spbc_preloader_button' ),\n callback: function(result, data, params, obj) {\n let selector;\n if (result.success) {\n selector = '

' + result.data + '

';\n jQuery( selector ).insertAfter( jQuery(button) );\n jQuery('#spbc_settings_templates_reset_button .spbc_success').show(300);\n setTimeout(function() {\n jQuery('#spbc_settings_templates_reset_button .spbc_success').hide(300);\n }, 2000);\n document.addEventListener('spbcModalClosed', function( e ) {\n document.location.reload();\n });\n setTimeout(function() {\n spbcModal.close();\n }, 2000);\n } else {\n selector = '

' + result.data + '

';\n jQuery( selector ).insertAfter( jQuery(button) );\n }\n },\n },\n );\n });\n\n jQuery(document).on('change', '#spbc_settings_templates_export', function() {\n let optionSelected = jQuery('option:selected', this);\n if ( optionSelected.data('id') === 'new_template' ) {\n jQuery(this).parent().parent().find('#spbc_settings_templates_export_name').show();\n } else {\n jQuery(this).parent().parent().find('#spbc_settings_templates_export_name').hide();\n }\n });\n\n //* TAB_CONTROL\n // migrated to react\n //* / REFRESH TAB IF ctrl+F5 END\n\n /**\n * Change cleantalk account email\n */\n jQuery(document).on('click', '#spbc-change-account-email', function(e) {\n e.preventDefault();\n\n let $this = jQuery(this);\n let accountEmailField = jQuery('#spbc-account-email');\n let accountEmail = accountEmailField.text();\n\n spbcToggleClass($this, 'active');\n\n if ($this.hasClass('active')) {\n $this.text($this.data('save-text'));\n accountEmailField.attr('contenteditable', 'true');\n accountEmailField.focus();\n accountEmailField.on('keydown', function(e) {\n if (e.code === 'Enter') {\n e.preventDefault();\n }\n });\n accountEmailField.on('input', function(e) {\n if (e.inputType === 'insertParagraph') {\n e.preventDefault();\n }\n });\n } else {\n spbcSendAJAXRequest(\n {\n action: 'spbc_update_account_email',\n accountEmail: accountEmail,\n },\n {\n timeout: 5000,\n callback: function(result, data, params, obj) {\n if (result.success !== undefined && result.success === 'ok') {\n if (result.manuallyLink !== undefined) {\n jQuery('#spbc-key-manually-link').attr('href', result.manuallyLink);\n }\n }\n\n if (result.error !== undefined) {\n jQuery('#spbc-account-email').css('border-color', 'red');\n }\n },\n errorOutput: function(msg) {\n spbcModal.loaded = msg;\n spbcModal.open();\n },\n },\n );\n\n accountEmailField.attr('contenteditable', 'false');\n $this.text($this.data('default-text'));\n }\n });\n\n // Only handle legacy banners, not React Components\n // @ToDo remove the outdated way to close notice after transferring all tabs to components.\n jQuery(document).on('click', '.spbct_notice-dismiss', function(e) {\n let classListNotice = this.parentElement.classList;\n let classNoticeForClose = classListNotice[classListNotice.length - 1];\n\n document.querySelectorAll('.' + classNoticeForClose).forEach((element) => {\n element.parentElement.setAttribute('style', 'display:none;');\n spbcSetCookie(classNoticeForClose, 1, 86400 * 30);\n });\n });\n\n // @ToDo need to be refactored - this is the code dublication\n jQuery(document).off( 'click', '.spbc_long_description__show');\n jQuery(document).on('click', '.spbc_long_description__show', function() {\n self = jQuery(this);\n spbcHandleLongDescription(self, self.attr('data-setting') || self.attr('setting'));\n });\n\n // @ToDo need to be refactored - this is the code dublication\n jQuery(document).off( 'click', '.spbc_long_recommendation__show');\n jQuery(document).on('click', '.spbc_long_recommendation__show', function() {\n self = jQuery(this);\n spbcSettingsShowRecommendation(self, self.attr('data-setting') || self.attr('setting'));\n });\n\n // Reset dismiss flag if user came from outside plugin settings\n if (!document.referrer || document.referrer.indexOf('page=spbc') === -1) {\n sessionStorage.removeItem('spbc_trial_renew_fullpage_dismissed');\n }\n\n // Hide fullpage trial banner if it was already dismissed in this browser session\n if (sessionStorage.getItem('spbc_trial_renew_fullpage_dismissed')) {\n jQuery('.spbc-trial-renew-fullpage').hide();\n jQuery('#spbct-page--react').show();\n }\n\n jQuery('body').on('click', '.spbc-trial-renew-fullpage .notice-dismiss', function(e) {\n e.preventDefault();\n sessionStorage.setItem('spbc_trial_renew_fullpage_dismissed', '1');\n jQuery('.spbc-trial-renew-fullpage').hide();\n jQuery('#spbct-page--react').show();\n });\n\n jQuery(document)\n .off('click', '#spbc_debug__run_pass_check_btn')\n .on('click', '#spbc_debug__run_pass_check_btn', function() {\n let $btn = jQuery(this);\n let $st = jQuery('#spbc_debug__pass_check_status');\n $btn.prop('disabled', true);\n $st.css('color', '').text('Resetting records and running check, please wait...');\n jQuery.ajax({\n type: 'POST',\n url: ajaxurl,\n data: {\n action: 'spbc_debug_users_pass_check_run',\n security: spbcSettings.ajax_nonce,\n },\n success: function(response) {\n $btn.prop('disabled', false);\n if (response.success) {\n $st.css('color', 'green').html(\n '✓ Done! Total: ' + response.data.total +\n ', Checked: ' + response.data.checked +\n ', Leaked: ' + response.data.leaked +\n ' — You can now test the login form in incognito.',\n );\n } else {\n $st.css('color', 'red').text('Error: ' + (response.data || 'unknown error'));\n }\n },\n error: function(xhr) {\n $btn.prop('disabled', false);\n $st.css('color', 'red').text('Request failed: ' + xhr.status + ' ' + xhr.statusText);\n },\n });\n });\n});\n\n/**\n * Toggle class for DOM elements\n * @param {Array} arr\n * @param {string} className\n * @return {void}\n */\nfunction spbcToggleClass(arr, className) {\n for (let i=0; i {\n spbcSaveSettingsButtonMobileLayout();\n });\n}\n\n/**\n * A chunk to hide the custom element \"submit button\" on large screen or make sticky on mobile\n */\nfunction spbcSaveSettingsButtonMobileLayout() {\n const selector = document.querySelector('div[name=\"spbc_settings--save_button_custom\"]');\n if (selector) {\n if (window.innerWidth > 900) {\n selector.style.display = 'none';\n } else {\n selector.style.display = 'flex';\n }\n }\n}\n\n/**\n * Sends logs\n *\n * @param {*} result\n * @param {*} data\n * @param {*} params\n * @param {*} obj\n */\nfunction spbcSendLogsCallback(result, data, params, obj) {\n jQuery('.spbc_tab_nav-'+data.tab_name).data('loaded', false);\n spbcSwitchTab(document.getElementsByClassName('spbc_tab_nav-'+data.tab_name)[0]);\n}\n\n/**\n * Disables/enables settings by ID\n *\n * @param {array} settingsIDs\n * @param {number} enable\n */\nfunction spbcSettingsDependenciesbyId(settingsIDs, enable) { // eslint-disable-line no-unused-vars\n if (typeof settingsIDs === 'string') {\n tmp = [];\n tmp.push(settingsIDs);\n settingsIDs = tmp;\n }\n\n enable = typeof enable === 'undefined' ? null : +enable;\n\n settingsIDs.forEach(function(settingID, i, arr) {\n let elem = document.getElementById('spbc_setting_'+settingID);\n let doDisable = function() {\n elem.setAttribute('disabled', 'disabled');\n };\n let doEnable = function() {\n elem.removeAttribute('disabled');\n };\n\n if (enable !== null) {\n // Set\n enable === 1 ? doEnable() : doDisable();\n } else {\n // Switch\n elem.getAttribute('disabled') === null ? doDisable() : doEnable();\n }\n });\n}\n\n/**\n * Recursive in case children found!\n *\n * Settings dependencies\n *\n * @param {array} settingNames\n * @param {number} enable\n */\nfunction spbcSettingsDependencies(settingNames, enable) { // eslint-disable-line no-unused-vars\n // Cast settingNames to array\n const eventTarget = window.event && window.event.target ? window.event.target : null;\n const isRadio = eventTarget && eventTarget.type === 'radio';\n const radioEnabled = isRadio && eventTarget.value == '1';\n const targetEnableFlag = eventTarget ?\n ((isRadio && !radioEnabled) || +eventTarget.checked) :\n null;\n const doEnableFlag = typeof enable === 'undefined' ? targetEnableFlag : +enable;\n\n settingNames = typeof settingNames === 'string' ? settingNames.split(',') : settingNames;\n\n settingNames.forEach(function(settingName, i, arr) {\n let elements = document.getElementsByName('spbc_settings['+settingName+']');\n if (elements.length === 0) {\n // multiple selector case\n elements = document.getElementsByName('spbc_settings['+settingName+'][]');\n }\n\n elements\n .forEach(function( elem, i, arr ) {\n let doDisable = function() {\n elem.setAttribute('disabled', 'disabled');\n };\n let doEnable = function() {\n elem.removeAttribute('disabled');\n };\n if (doEnableFlag !== null) {\n // Set\n doEnableFlag === 1 ? doEnable() : doDisable();\n } else {\n // Switch\n elem.getAttribute('disabled') === null ? doDisable() : doEnable();\n }\n\n let children = elem.getAttribute('data-children');\n if ( children !== null ) {\n spbcSettingsDependencies(children, doEnableFlag && elem.checked);\n }\n });\n });\n}\n\n/**\n * Shows recommendation\n *\n * @param {object} label\n * @param {string} settingId\n */\nfunction spbcSettingsShowRecommendation(label, settingId) {\n let removeRecommendationFunc = function(e) {\n if (\n typeof e === 'undefined' ||\n (\n jQuery(e.target).parent('.spbc_long_desc').length == 0 ||\n jQuery(e.target).hasClass('spbc_long_desc__cancel')\n ) && !jQuery(e.target).hasClass('spbc_long_recommendation__show')\n ) {\n jQuery('.spbc_long_desc').remove();\n jQuery(document).off('click', removeRecommendationFunc);\n }\n };\n\n removeRecommendationFunc();\n\n label.after('
');\n let obj = jQuery('#spbc_long_desc__'+settingId);\n obj.append('')\n .append('
')\n .css({\n top: label.position().top + 25,\n left: label.position().left + 5,\n });\n\n\n spbcSendAJAXRequest(\n {action: 'spbc_settings__get_recommendation', setting_id: settingId},\n {\n spinner: obj.children('img'),\n callback: function(result, data, params, obj) {\n obj.empty()\n .append('
')\n .append('')\n .append('

'+result.title+'

')\n .append('

'+result.desc+'

');\n\n jQuery(document).on('click', removeRecommendationFunc);\n },\n },\n obj,\n );\n}\n\n/**\n * Shows/hides full text\n */\nfunction spbcStartShowHide() { // eslint-disable-line no-unused-vars\n jQuery('.spbcShortText')\n .off('mouseover' )\n .on('mouseover', function() {\n jQuery(this).next().show();\n })\n .off('mouseout' )\n .on('mouseout', function() {\n jQuery(this).next().hide();\n });\n jQuery('.spbcFullText')\n .off('mouseout' )\n .on('mouseout', function() {\n jQuery(this).hide();\n })\n .off('mouseover' )\n .on('mouseover', function() {\n jQuery(this).show();\n });\n}\n\n/**\n * Reloads last scan info\n */\nfunction spbcScannerReloadScanInfo() { // eslint-disable-line no-unused-vars\n jQuery('.spbc_hint__last_scan_title').text(spbcScaner.look_below_for_scan_res);\n const data = {\n action: 'spbc_scanner__last_scan_info',\n };\n const params = {\n callback: (result) => {\n document.getElementById('spbc_scanner__last_scan_info').innerText = result.data;\n },\n };\n spbcSendAJAXRequest(data, params);\n}\n\n/**\n * Checking current account status for renew notice\n */\nfunction spbcBannerCheck() {\n let bannerChecker = setInterval( function() {\n spbcSendAJAXRequest(\n {action: 'spbc_settings__check_renew_banner'},\n {\n callback: function(result, data, params, obj) {\n if (result.close_renew_banner) {\n if (jQuery('#spbc_renew_notice').length) {\n jQuery('#spbc_renew_notice').hide('slow');\n }\n if (jQuery('#spbc_trial_notice').length) {\n jQuery('#spbc_trial_notice').hide('slow');\n }\n clearInterval(bannerChecker);\n }\n },\n },\n );\n }, 60000);\n}\n\njQuery(document).ready(function() {\n if (jQuery('#spbc_renew_notice').length || jQuery('#spbc_trial_notice').length) {\n spbcBannerCheck();\n }\n\n // Sync button migrated to react\n\n if ( spbcSettings.key_changed ) {\n jQuery('#spbc_button__sync').click();\n }\n\n // Get Key Auto button migrated to Signup Wizard\n\n // Import settings\n jQuery( document ).on('click', '#spbc_settings_templates_import_button', function() {\n jQuery('#spbc-ajax-result').remove();\n let optionSelected = jQuery('option:selected', jQuery('#spbc_settings_templates_import'));\n let templateNameInput = jQuery('#spbc_settings_templates_import_name');\n templateNameInput.css('border-color', 'inherit');\n if ( typeof optionSelected.data('id') === 'undefined' ) {\n console.log( 'Attribute \"data-id\" not set for the option.' );\n return;\n }\n let data = {\n 'template_id': optionSelected.data('id'),\n 'template_name': optionSelected.data('name'),\n 'settings': optionSelected.data('settings'),\n };\n let button = this;\n spbcSendAJAXRequest(\n {action: 'spbc_settings_templates_import', data: data},\n {\n timeout: 25000,\n button: button,\n spinner: jQuery('#spbc_settings_templates_import_button .spbc_preloader_button' ),\n callback: function(result, data, params, obj) {\n let selector;\n if (result.success) {\n selector = '

' + result.data + '

';\n jQuery( selector ).insertAfter( jQuery(button) );\n jQuery('#spbc_settings_templates_import_button .spbc_success').show(300);\n setTimeout(function() {\n jQuery('#spbc_settings_templates_import_button .spbc_success').hide(300);\n }, 2000);\n document.addEventListener('spbcModalClosed', function( e ) {\n document.location.reload();\n });\n setTimeout(function() {\n spbcModal.close();\n }, 2000);\n } else {\n selector = '

' + result.data + '

';\n jQuery( selector ).insertAfter( jQuery(button) );\n }\n },\n },\n );\n });\n\n // Export settings\n jQuery( document ).on('click', '#spbc_settings_templates_export_button', function() {\n jQuery('#spbc-ajax-result').remove();\n let optionSelected = jQuery('option:selected', jQuery('#spbc_settings_templates_export'));\n let templateNameInput = jQuery('#spbc_settings_templates_export_name');\n templateNameInput.css('border-color', 'inherit');\n if ( typeof optionSelected.data('id') === 'undefined' ) {\n console.log( 'Attribute \"data-id\" not set for the option.' );\n return;\n }\n let data;\n if ( optionSelected.data('id') === 'new_template' ) {\n let templateName = templateNameInput.val();\n if ( templateName === '' ) {\n templateNameInput.css('border-color', 'red');\n return;\n }\n data = {\n 'template_name': templateName,\n };\n } else {\n data = {\n 'template_id': optionSelected.data('id'),\n };\n }\n let button = this;\n spbcSendAJAXRequest(\n {action: 'spbc_settings_templates_export', data: data},\n {\n timeout: 25000,\n button: button,\n spinner: jQuery('#spbc_settings_templates_export_button .spbc_preloader_button' ),\n callback: function(result, data, params, obj) {\n let selector;\n if (result.success) {\n selector = '

' + result.data + '

';\n jQuery( selector ).insertAfter( jQuery(button) );\n jQuery('#spbc_settings_templates_export_button .spbc_success').show(300);\n setTimeout(function() {\n jQuery('#spbc_settings_templates_export_button .spbc_success').hide(300);\n }, 2000);\n document.addEventListener('spbcModalClosed', function( e ) {\n document.location.reload();\n });\n setTimeout(function() {\n spbcModal.close();\n }, 2000);\n } else {\n selector = '

' + result.data + '

';\n jQuery( selector ).insertAfter( jQuery(button) );\n }\n },\n },\n );\n });\n\n // Reset settings\n jQuery( document ).on('click', '#spbc_settings_templates_reset_button', function() {\n let button = this;\n spbcSendAJAXRequest(\n {action: 'spbc_settings_templates_reset'},\n {\n timeout: 25000,\n button: button,\n spinner: jQuery('#spbc_settings_templates_reset_button .spbc_preloader_button' ),\n callback: function(result, data, params, obj) {\n let selector;\n if (result.success) {\n selector = '

' + result.data + '

';\n jQuery( selector ).insertAfter( jQuery(button) );\n jQuery('#spbc_settings_templates_reset_button .spbc_success').show(300);\n setTimeout(function() {\n jQuery('#spbc_settings_templates_reset_button .spbc_success').hide(300);\n }, 2000);\n document.addEventListener('spbcModalClosed', function( e ) {\n document.location.reload();\n });\n setTimeout(function() {\n spbcModal.close();\n }, 2000);\n } else {\n selector = '

' + result.data + '

';\n jQuery( selector ).insertAfter( jQuery(button) );\n }\n },\n },\n );\n });\n\n jQuery(document).on('change', '#spbc_settings_templates_export', function() {\n let optionSelected = jQuery('option:selected', this);\n if ( optionSelected.data('id') === 'new_template' ) {\n jQuery(this).parent().parent().find('#spbc_settings_templates_export_name').show();\n } else {\n jQuery(this).parent().parent().find('#spbc_settings_templates_export_name').hide();\n }\n });\n\n // Only handle legacy banners, not React Components\n // @ToDo remove the outdated way to close notice after transferring all tabs to components.\n jQuery(document).on('click', '.spbct_notice-dismiss', function(e) {\n let classListNotice = this.parentElement.classList;\n let classNoticeForClose = classListNotice[classListNotice.length - 1];\n\n document.querySelectorAll('.' + classNoticeForClose).forEach((element) => {\n element.parentElement.setAttribute('style', 'display:none;');\n spbcSetCookie(classNoticeForClose, 1, 86400 * 30);\n });\n });\n\n // @ToDo need to be refactored - this is the code dublication\n jQuery(document).off( 'click', '.spbc_long_description__show');\n jQuery(document).on('click', '.spbc_long_description__show', function() {\n self = jQuery(this);\n spbcHandleLongDescription(self, self.attr('data-setting') || self.attr('setting'));\n });\n\n // @ToDo need to be refactored - this is the code dublication\n jQuery(document).off( 'click', '.spbc_long_recommendation__show');\n jQuery(document).on('click', '.spbc_long_recommendation__show', function() {\n self = jQuery(this);\n spbcSettingsShowRecommendation(self, self.attr('data-setting') || self.attr('setting'));\n });\n\n // Reset dismiss flag if user came from outside plugin settings\n if (!document.referrer || document.referrer.indexOf('page=spbc') === -1) {\n sessionStorage.removeItem('spbc_trial_renew_fullpage_dismissed');\n }\n\n // Hide fullpage trial banner if it was already dismissed in this browser session\n if (sessionStorage.getItem('spbc_trial_renew_fullpage_dismissed')) {\n jQuery('.spbc-trial-renew-fullpage').hide();\n jQuery('#spbct-page--react').show();\n }\n\n jQuery('body').on('click', '.spbc-trial-renew-fullpage .notice-dismiss', function(e) {\n e.preventDefault();\n sessionStorage.setItem('spbc_trial_renew_fullpage_dismissed', '1');\n jQuery('.spbc-trial-renew-fullpage').hide();\n jQuery('#spbct-page--react').show();\n });\n\n jQuery(document)\n .off('click', '#spbc_debug__run_pass_check_btn')\n .on('click', '#spbc_debug__run_pass_check_btn', function() {\n let $btn = jQuery(this);\n let $st = jQuery('#spbc_debug__pass_check_status');\n $btn.prop('disabled', true);\n $st.css('color', '').text('Resetting records and running check, please wait...');\n jQuery.ajax({\n type: 'POST',\n url: ajaxurl,\n data: {\n action: 'spbc_debug_users_pass_check_run',\n security: spbcSettings.ajax_nonce,\n },\n success: function(response) {\n $btn.prop('disabled', false);\n if (response.success) {\n $st.css('color', 'green').html(\n '✓ Done! Total: ' + response.data.total +\n ', Checked: ' + response.data.checked +\n ', Leaked: ' + response.data.leaked +\n ' — You can now test the login form in incognito.',\n );\n } else {\n $st.css('color', 'red').text('Error: ' + (response.data || 'unknown error'));\n }\n },\n error: function(xhr) {\n $btn.prop('disabled', false);\n $st.css('color', 'red').text('Request failed: ' + xhr.status + ' ' + xhr.statusText);\n },\n });\n });\n});\n\n/**\n * Toggle class for DOM elements\n * @param {Array} arr\n * @param {string} className\n * @return {void}\n */\nfunction spbcToggleClass(arr, className) {\n for (let i=0; i{"security_log"===e.detail.tab&&(console.log(e.detail.tab),this.initTimeline())}),jQuery("#spbc-timeline-tab").on("click",e=>{e.hasOwnProperty("originalEvent")&&("1"!==localStorage.getItem("spbc_timeline_closed")?localStorage.setItem("spbc_timeline_closed","1"):localStorage.setItem("spbc_timeline_closed","0"))}),window.addEventListener("resize",()=>{this.selectedUserValue&&(this.userLegendExpanded=!1,"most_active__action"===this.selectedUserValue?this.drawUserTimelineMostActive():this.drawUserTimeline(this.selectedUserValue))}))}initTimeline(){let i=document.getElementById("spbc-timeline-user-select-wrapper");var e=document.getElementById("spbc-timeline-user-select-trigger");let s=document.getElementById("spbc-timeline-user-select-dropdown"),n=document.getElementById("spbc-timeline-user-select-value");var t,r,a,l=[];for(t in this.usersCount=0,this.summaryEventsCount=0,this.eventsData)this.eventsData.hasOwnProperty(t)&&(r=this.eventsData[t],0<(a=(Array.isArray(r)?r:r.events||[]).filter(e=>e.time>=this.timelineStartTimestamp&&e.time<=this.currentTimestamp)).length)&&(this.usersCount++,this.summaryEventsCount+=a.length,l.push({user:t,role:r.role||"unknown",eventsCount:a.length}));s.innerHTML="";let c=(e,t)=>{var i=document.createElement("div");return i.className="spbc-timeline-select-option",i.dataset.value=t,i.textContent=e,i};var o=c("Most Active Users","most_active__action"),o=(o.classList.add("selected"),s.appendChild(o),l.forEach(e=>{var t=e.user+` (${e.role}) (${e.eventsCount} events)`;s.appendChild(c(t,e.user))}),e.addEventListener("click",e=>{e.stopPropagation(),i.classList.toggle("open")}),document.addEventListener("click",e=>{i.contains(e.target)||i.classList.remove("open")}),s.addEventListener("click",e=>{var t,e=e.target.closest(".spbc-timeline-select-option");e&&(t=e.dataset.value,this.selectedUserValue=t,this.userLegendExpanded=!1,n.textContent=e.textContent,s.querySelectorAll(".spbc-timeline-select-option").forEach(e=>{e.classList.remove("selected")}),e.classList.add("selected"),i.classList.remove("open"),"most_active__action"===t?(this.drawUserTimelineMostActive(),(e=new URL(window.location)).searchParams.delete("users"),window.history.pushState({},"",e)):(this.drawUserTimeline(t),(e=new URL(window.location)).searchParams.set("users",t),window.history.pushState({},"",e)),updateSecurityLogTable())}),0{"security_log"===e.detail.tab&&this.initTimeline()}),jQuery("#spbc-timeline-tab").on("click",e=>{e.hasOwnProperty("originalEvent")&&("1"!==localStorage.getItem("spbc_timeline_closed")?localStorage.setItem("spbc_timeline_closed","1"):localStorage.setItem("spbc_timeline_closed","0"))}),window.addEventListener("resize",()=>{this.selectedUserValue&&(this.userLegendExpanded=!1,"most_active__action"===this.selectedUserValue?this.drawUserTimelineMostActive():this.drawUserTimeline(this.selectedUserValue))}))}initTimeline(){let i=document.getElementById("spbc-timeline-user-select-wrapper");var e=document.getElementById("spbc-timeline-user-select-trigger");let s=document.getElementById("spbc-timeline-user-select-dropdown"),n=document.getElementById("spbc-timeline-user-select-value");var t,r,a,l=[];for(t in this.usersCount=0,this.summaryEventsCount=0,this.eventsData)this.eventsData.hasOwnProperty(t)&&(r=this.eventsData[t],0<(a=(Array.isArray(r)?r:r.events||[]).filter(e=>e.time>=this.timelineStartTimestamp&&e.time<=this.currentTimestamp)).length)&&(this.usersCount++,this.summaryEventsCount+=a.length,l.push({user:t,role:r.role||"unknown",eventsCount:a.length}));s.innerHTML="";let c=(e,t)=>{var i=document.createElement("div");return i.className="spbc-timeline-select-option",i.dataset.value=t,i.textContent=e,i};var o=c("Most Active Users","most_active__action"),o=(o.classList.add("selected"),s.appendChild(o),l.forEach(e=>{var t=e.user+` (${e.role}) (${e.eventsCount} events)`;s.appendChild(c(t,e.user))}),e.addEventListener("click",e=>{e.stopPropagation(),i.classList.toggle("open")}),document.addEventListener("click",e=>{i.contains(e.target)||i.classList.remove("open")}),s.addEventListener("click",e=>{var t,e=e.target.closest(".spbc-timeline-select-option");e&&(t=e.dataset.value,this.selectedUserValue=t,this.userLegendExpanded=!1,n.textContent=e.textContent,s.querySelectorAll(".spbc-timeline-select-option").forEach(e=>{e.classList.remove("selected")}),e.classList.add("selected"),i.classList.remove("open"),"most_active__action"===t?(this.drawUserTimelineMostActive(),(e=new URL(window.location)).searchParams.delete("users"),window.history.pushState({},"",e)):(this.drawUserTimeline(t),(e=new URL(window.location)).searchParams.set("users",t),window.history.pushState({},"",e)),updateSecurityLogTable())}),0{e.classList.remove("selected"),e.dataset.value===t&&(e.classList.add("selected"),i.textContent=e.textContent)})),this.drawUserTimeline(t);e=new URL(window.location);e.searchParams.set("users",t),window.history.pushState({},"",e),updateSecurityLogTable()}getMostActiveUsers(e){let t=this.currentTimestamp-86400;var i,s,n,r=[];for(i in e)e.hasOwnProperty(i)&&(s=e[i],0<(n=(Array.isArray(s)?s:s.events||[]).filter(e=>e.time>=t)).length)&&r.push({user:i,events:n,count:n.length,role:s.role||"unknown"});return r.sort((e,t)=>t.count-e.count),r.slice(0,15)}groupByHour(i,e){let s={};return e.forEach(e=>{var t=3600*Math.floor(e.time/3600);s[t]||(s[t]={timestamp:1800+t,user:i,count:0,byType:{}}),s[t].count++,s[t].byType[e.event]=(s[t].byType[e.event]||0)+1}),Object.values(s)}drawUserTimeline(n){var e=document.getElementById("spbc-timeline-svg"),t=document.getElementById("spbc-timeline-no-data");e.innerHTML="",t.style.display="none";let i=0;5e.time>=this.timelineStartTimestamp&&e.time<=this.currentTimestamp);if(0===r.length)t.style.display="block";else{let s=this.currentTimestamp-this.timelineStartTimestamp;var p=spbcTimelineWidgetData.found_types.length;for(let e=0;e{var t=(e.time-this.timelineStartTimestamp)/s*d,i=m+this.timelineMarkersData[e.event].upstep;u.appendChild(this.createEventElement(e,t,i,n))})}}measureTextWidth(e,t,i="spbc-timeline-legend-text"){var s=document.createElementNS("http://www.w3.org/2000/svg","text"),i=(s.setAttribute("class",i),s.textContent=t,s.style.visibility="hidden",e.appendChild(s),s.getComputedTextLength());return e.removeChild(s),i}calculateUserLegendRowsCount(t,i,s,n=null){var r=SpbcTimelineWidget.userLegendItemGap,a=SpbcTimelineWidget.markerRadius,l=this.measureTextWidth(t,"and 99 more")+r;let c=0,o=0;for(let e=0;em&&0=n)return n;c+=d}return o+1}drawUserTimelineMostActive(){var n=document.getElementById("spbc-timeline-svg"),r=document.getElementById("spbc-timeline-no-data"),a=(n.innerHTML="",r.style.display="none",this.getMostActiveUsers(this.eventsData));if(0===a.length)r.style.display="block";else{let e=0,i=(5{s[e.user]=i[(t+1)%i.length]});r=n.parentElement.clientWidth;const v=10+e,w=40,y=40,T=140;let t=r-T-w;var l=SpbcTimelineWidget.userLegendRowHeight,m=this.userLegendExpanded?null:SpbcTimelineWidget.userLegendMaxRows,m=this.calculateUserLegendRowsCount(n,a,t,m)*l,l=150+e+SpbcTimelineWidget.userLegendAxisOffset,m=l+m;let c=(l-v-y)/2,o=(n.setAttribute("width",r),n.setAttribute("height",m),document.createElementNS("http://www.w3.org/2000/svg","g")),d=(o.setAttribute("transform",`translate(${T}, ${v+30})`),n.appendChild(o),this.drawTimeAxis(o,t,10+c,!0),this.currentTimestamp-this.timelineStartTimestamp);var u=spbcTimelineWidgetData.found_types.length;for(let e=0;e{let l=s[a.user];this.groupByHour(a.user,a.events).forEach(r=>{if(0!==r.count){let n=Math.max(0,Math.min((r.timestamp-this.timelineStartTimestamp)/d*t,t));Object.entries(r.byType).forEach(([e,t])=>{var i=c+this.timelineMarkersData[e].upstep,s=SpbcTimelineWidget.markerRadius+Math.min(2*Math.sqrt(t-1),8),e={event:e,time:r.timestamp,count:t};o.appendChild(this.createEventElement(e,n,i,a.user,s,!0,l))})}})})}}drawUserLegend(t,i,s,n,r,a){var l=SpbcTimelineWidget.userLegendRowHeight,c=SpbcTimelineWidget.userLegendItemGap,o=SpbcTimelineWidget.markerRadius,d=this.userLegendExpanded?null:SpbcTimelineWidget.userLegendMaxRows;let m=0,u=0,p=0,h=0;var g=this.measureTextWidth(t,"and 99 more")+c;for(let e=0;ey&&0=d)return void(0<(y=s.length-p)&&(w=`and ${y} more`,y=r+(d-1)*l,(T=document.createElementNS("http://www.w3.org/2000/svg","text")).setAttribute("class","spbc-timeline-legend-text spbc-timeline-more-link"),T.setAttribute("x",h),T.setAttribute("y",y),T.setAttribute("text-anchor","start"),T.setAttribute("dominant-baseline","middle"),T.textContent=w,T.style.cursor="pointer",T.style.fill="#0073aa",T.style.textDecoration="underline",T.addEventListener("click",()=>{this.userLegendExpanded=!0,this.drawUserTimelineMostActive()}),i.appendChild(T)));null!==d&&u===d-1&&(h=m+v);var y=r+u*l,w=m+o,T=m+2*o+6,A=document.createElementNS("http://www.w3.org/2000/svg","circle"),w=(A.setAttribute("cx",w),A.setAttribute("cy",y),A.setAttribute("r",o),A.style.fill=n[b.user],i.appendChild(A),document.createElementNS("http://www.w3.org/2000/svg","text"));w.setAttribute("class","spbc-timeline-legend-text"),w.setAttribute("x",T),w.setAttribute("y",y),w.setAttribute("text-anchor","start"),w.setAttribute("dominant-baseline","middle"),w.textContent=b.user,i.appendChild(w),m+=v,p++}}generateMarkersData(e){let t={},i=0,s="default";return e.forEach(e=>{this.timelineKnownEventsData[e]&&this.timelineKnownEventsData[e].color_scheme&&(s=this.timelineKnownEventsData[e].color_scheme),t[e]={color:s,upstep:i},i-=SpbcTimelineWidget.userActionsUpstep}),t}}function spbcTimelineWidgetRender(){var e=new SpbcTimelineWidget;jQuery(function(){jQuery("#spbc-timeline-accordion").accordion({heightStyle:"content",collapsible:!0,active:!1})}),e.render()} diff --git a/js/spbc-timeline-widget.min.js.map b/js/spbc-timeline-widget.min.js.map index fc07f6169..75b1bfe25 100644 --- a/js/spbc-timeline-widget.min.js.map +++ b/js/spbc-timeline-widget.min.js.map @@ -1 +1 @@ -{"version":3,"file":"spbc-timeline-widget.min.js","sources":["spbc-timeline-widget.js"],"sourcesContent":["/**\n * Class to render a timeline widget for Security log\n */\nclass SpbcTimelineWidget {\n // Static constants\n static markerRadius = 4;\n static hoursRangeMax = 24;\n static axisStepsCount = 6;\n static userActionsUpstep = 24;\n static legendCircleX = -120;\n static legendTextX = -105;\n static userLegendMaxRows = 2;\n static userLegendRowHeight = 18;\n static userLegendItemGap = 15;\n static userLegendAxisOffset = 60; // Offset from axis to user legend\n\n /**\n * Constructor.\n */\n constructor() {\n // Current time properties\n this.now = new Date();\n this.currentTimestamp = Math.floor(this.now.getTime() / 1000);\n\n // Data properties\n this.eventsData = null;\n this.timelineMarkersData = [];\n this.timelineKnownEventsData = [];\n this.timelineEventTooltip = null;\n this.timelineStartTimestamp = this.currentTimestamp - SpbcTimelineWidget.hoursRangeMax * 3600;\n this.usersCount = 0;\n this.summaryEventsCount = 0;\n this.userLegendExpanded = false;\n this.selectedUserValue = 'most_active__action';\n }\n\n /**\n * Initialize and render the timeline widget\n */\n render() {\n if (typeof spbcTimelineWidgetData === 'undefined') {\n return;\n }\n\n this.timelineEventTooltip = document.getElementById('spbc-timeline-tooltip');\n this.eventsData = spbcTimelineWidgetData.users_actions;\n if (spbcTimelineWidgetData.current_timestamp_local) {\n this.currentTimestamp = Number(spbcTimelineWidgetData.current_timestamp_local);\n }\n this.timelineStartTimestamp = Number(spbcTimelineWidgetData.first_event_timestamp);\n this.timelineKnownEventsData = spbcTimelineWidgetData.known_events_data;\n this.timelineMarkersData = this.generateMarkersData(spbcTimelineWidgetData.found_types);\n\n // Initialize timeline when tab is loaded\n window.addEventListener('spbctTabLoaded', (event) => {\n if (event.detail.tab === 'security_log') {\n console.log(event.detail.tab);\n this.initTimeline();\n }\n });\n\n jQuery('#spbc-timeline-tab').on('click', (e)=> {\n if (e.hasOwnProperty('originalEvent')) {\n if (localStorage.getItem('spbc_timeline_closed') !== '1') {\n localStorage.setItem('spbc_timeline_closed', '1');\n } else {\n localStorage.setItem('spbc_timeline_closed', '0');\n }\n }\n });\n\n // Handle window resize\n window.addEventListener('resize', () => {\n if (!this.selectedUserValue) {\n return;\n }\n this.userLegendExpanded = false;\n if (this.selectedUserValue === 'most_active__action') {\n this.drawUserTimelineMostActive();\n } else {\n this.drawUserTimeline(this.selectedUserValue);\n }\n });\n }\n\n /**\n * Initialize timeline with user selection\n */\n initTimeline() {\n const wrapper = document.getElementById('spbc-timeline-user-select-wrapper');\n const trigger = document.getElementById('spbc-timeline-user-select-trigger');\n const dropdown = document.getElementById('spbc-timeline-user-select-dropdown');\n const valueDisplay = document.getElementById('spbc-timeline-user-select-value');\n const usersWithRecentEvents = [];\n\n // Calculate summary statistics for the last 24 hours only\n this.usersCount = 0;\n this.summaryEventsCount = 0;\n\n for (let key in this.eventsData) {\n if (this.eventsData.hasOwnProperty(key)) {\n const userData = this.eventsData[key];\n const eventsArray = Array.isArray(userData) ? userData : (userData.events || []);\n const recentEvents = eventsArray.filter((event) =>\n event.time >= this.timelineStartTimestamp &&\n event.time <= this.currentTimestamp,\n );\n\n if (recentEvents.length > 0) {\n this.usersCount++;\n this.summaryEventsCount += recentEvents.length;\n usersWithRecentEvents.push({\n user: key,\n role: userData.role || 'unknown',\n eventsCount: recentEvents.length,\n });\n }\n }\n }\n\n // Reset dropdown options\n dropdown.innerHTML = '';\n\n // Helper to create option element\n const createOption = (text, value) => {\n const option = document.createElement('div');\n option.className = 'spbc-timeline-select-option';\n option.dataset.value = value;\n option.textContent = text;\n return option;\n };\n\n // Add \"Most Active Users\" option\n const mostActiveOption = createOption('Most Active Users', 'most_active__action');\n mostActiveOption.classList.add('selected');\n dropdown.appendChild(mostActiveOption);\n\n // Populate user options with role and event count\n usersWithRecentEvents.forEach((userData) => {\n const displayText = `${userData.user} (${userData.role}) (${userData.eventsCount} events)`;\n dropdown.appendChild(createOption(displayText, userData.user));\n });\n\n // Toggle dropdown on trigger click\n trigger.addEventListener('click', (e) => {\n e.stopPropagation();\n wrapper.classList.toggle('open');\n });\n\n // Close dropdown on outside click\n document.addEventListener('click', (e) => {\n if (!wrapper.contains(e.target)) {\n wrapper.classList.remove('open');\n }\n });\n\n // Handle option selection\n dropdown.addEventListener('click', (e) => {\n const option = e.target.closest('.spbc-timeline-select-option');\n if (!option) return;\n\n const selectedValue = option.dataset.value;\n this.selectedUserValue = selectedValue;\n this.userLegendExpanded = false;\n\n // Update display and selection state\n valueDisplay.textContent = option.textContent;\n dropdown.querySelectorAll('.spbc-timeline-select-option').forEach((opt) => {\n opt.classList.remove('selected');\n });\n option.classList.add('selected');\n\n // Close dropdown\n wrapper.classList.remove('open');\n\n if (selectedValue === 'most_active__action') {\n this.drawUserTimelineMostActive();\n const url = new URL(window.location);\n url.searchParams.delete('users');\n window.history.pushState({}, '', url);\n } else {\n this.drawUserTimeline(selectedValue);\n const url = new URL(window.location);\n url.searchParams.set('users', selectedValue);\n window.history.pushState({}, '', url);\n }\n\n // Update security log table with selected user\n updateSecurityLogTable();\n });\n\n // Draw initial timeline or show message\n if (usersWithRecentEvents.length > 0) {\n this.drawUserTimelineMostActive();\n this.selectedUserValue = 'most_active__action';\n valueDisplay.textContent = 'Most Active Users';\n } else {\n document.querySelector('.spbc-timeline-container').style.display = 'none';\n document.querySelector('.spbc-timeline-controls').style.display = 'none';\n document.getElementById('spbc-timeline-no-data').style.display = 'block';\n }\n\n const activitySelector = jQuery('#spbc_timeline_activity_counters')[0];\n activitySelector.innerText = activitySelector.innerText\n .replace('%users_count%', this.usersCount.toString())\n .replace('%events_count%', this.summaryEventsCount.toString());\n\n const isWidgetClosedByUser = (\n localStorage.getItem('spbc_timeline_closed') === '1'\n );\n if (!isWidgetClosedByUser) {\n setTimeout(function() {\n jQuery('#spbc-timeline-tab').trigger('click');\n }, 200);\n }\n }\n\n /**\n * Draw time axis on the timeline\n * @param {SVGElement} container - SVG container element\n * @param {number} width - Drawing area width\n * @param {number} height - Drawing area height\n * @param {boolean} roundToHour - Whether to round ticks to full hours\n */\n drawTimeAxis(container, width, height, roundToHour = false) {\n const timeRange = this.currentTimestamp - this.timelineStartTimestamp;\n const axis = document.createElementNS('http://www.w3.org/2000/svg', 'line');\n height = height + 10;\n // Create main axis line\n axis.setAttribute('class', 'spbc-timeline-axis');\n axis.setAttribute('x1', '0');\n axis.setAttribute('y1', height);\n axis.setAttribute('x2', width);\n axis.setAttribute('y2', height);\n container.appendChild(axis);\n\n // Create axis ticks and labels\n if (roundToHour) {\n const firstHourTs = Math.ceil(this.timelineStartTimestamp / 3600) * 3600;\n for (let ts = firstHourTs; ts <= this.currentTimestamp; ts += 3600) {\n const x = ((ts - this.timelineStartTimestamp) / timeRange) * width;\n\n // Create tick mark\n const tick = document.createElementNS('http://www.w3.org/2000/svg', 'line');\n tick.setAttribute('class', 'spbc-timeline-axis');\n tick.setAttribute('x1', x);\n tick.setAttribute('y1', height);\n tick.setAttribute('x2', x);\n tick.setAttribute('y2', height + 5);\n container.appendChild(tick);\n\n const label = document.createElementNS('http://www.w3.org/2000/svg', 'text');\n label.setAttribute('class', 'spbc-timeline-axis-text');\n label.setAttribute('x', x);\n label.setAttribute('y', height + 20);\n const h = new Date(ts * 1000).getUTCHours();\n label.textContent = `${h.toString().padStart(2, '0')}:00`;\n container.appendChild(label);\n }\n } else {\n for (let i = 0; i <= SpbcTimelineWidget.axisStepsCount; i++) {\n const timestamp = this.timelineStartTimestamp + (i / SpbcTimelineWidget.axisStepsCount) * timeRange;\n const x = (i / SpbcTimelineWidget.axisStepsCount) * width;\n\n // Create tick mark\n const tick = document.createElementNS('http://www.w3.org/2000/svg', 'line');\n tick.setAttribute('class', 'spbc-timeline-axis');\n tick.setAttribute('x1', x);\n tick.setAttribute('y1', height);\n tick.setAttribute('x2', x);\n tick.setAttribute('y2', height + 5);\n container.appendChild(tick);\n\n // Create time label\n const label = document.createElementNS('http://www.w3.org/2000/svg', 'text');\n label.setAttribute('class', 'spbc-timeline-axis-text');\n label.setAttribute('x', x);\n label.setAttribute('y', height + 20);\n const date = new Date(timestamp * 1000);\n label.textContent =\n `\n ${date.getUTCHours().toString().padStart(2, '0')}:\n ${date.getUTCMinutes().toString().padStart(2, '0')}\n `;\n container.appendChild(label);\n }\n }\n }\n\n /**\n * Create SVG element for an event\n * @param {Object} event - Event data\n * @param {number} x - X coordinate\n * @param {number} y - Y coordinate\n * @param {string} user - User name\n * @param {number} radius - Marker radius\n * @param {boolean} isMostActive - Whether this is a synthetic marker for most active users view\n * @param {string} color - Optional color override for the marker\n * @return {SVGElement} Event group element\n */\n createEventElement(event, x, y, user, radius, isMostActive= false, color) {\n const group = document.createElementNS('http://www.w3.org/2000/svg', 'g');\n group.setAttribute('transform', `translate(${x}, ${y})`);\n group.setAttribute('data-event', JSON.stringify({...event, user, isMostActive}));\n\n // Create event marker\n const marker = document.createElementNS('http://www.w3.org/2000/svg', 'circle');\n marker.setAttribute('class', 'spbc-timeline-event-marker');\n marker.setAttribute('r', radius || SpbcTimelineWidget.markerRadius);\n marker.style.fill = color || this.timelineMarkersData[event.event].color;\n group.appendChild(marker);\n\n // Add event handlers\n group.addEventListener('mouseenter', this.showEventTooltip.bind(this));\n group.addEventListener('mouseleave', this.hideEventTooltip.bind(this));\n\n if (isMostActive) {\n group.addEventListener('click', this.handleEventClick.bind(this, user));\n }\n\n return group;\n }\n\n /**\n * Show event tooltip\n * @param {Event} event - Mouse event\n */\n showEventTooltip(event) {\n const eventData = JSON.parse(event.currentTarget.getAttribute('data-event'));\n const {user, event: eventName, time, isMostActive} = eventData;\n\n // Format date for display\n const formatDate = (timestamp) => {\n const date = new Date(timestamp * 1000);\n\n const months = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun',\n 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'];\n\n const month = months[date.getUTCMonth()];\n const day = date.getUTCDate();\n const dayWithZero = String(day).padStart(2, '0');\n const year = date.getUTCFullYear();\n\n const hours = date.getUTCHours().toString().padStart(2, '0');\n const minutes = date.getUTCMinutes().toString().padStart(2, '0');\n const seconds = date.getUTCSeconds().toString().padStart(2, '0');\n\n return `${month} ${dayWithZero} ${year} ${hours}:${minutes}:${seconds}`;\n };\n\n // Update tooltip content\n document.getElementById('spbc-timeline-tooltip--user').innerText = user;\n document.getElementById('spbc-timeline-tooltip--event').innerText =\n eventData.count > 1 ? `${eventName} (${eventData.count})` : eventName;\n\n const timeRow = document.getElementById('spbc-timeline-tooltip--time').parentElement;\n if (isMostActive) {\n timeRow.style.display = 'none';\n } else {\n timeRow.style.display = '';\n document.getElementById('spbc-timeline-tooltip--time').innerText = formatDate(time);\n }\n\n const tooltip= document.getElementById('spbc-timeline-tooltip');\n const fixedTop = event.offsetY + 35;\n const tooltipY = fixedTop.toString() + 'px';\n const screenWidth = window.innerWidth;\n const tooltipWidth = tooltip.offsetWidth ? tooltip.offsetWidth : 165;\n const tooltipEventPositionX = event.clientX;\n let tooltipX = tooltipEventPositionX - tooltipWidth;\n if (tooltipEventPositionX + (tooltipWidth * 2) > screenWidth) {\n tooltipX = tooltipEventPositionX - (tooltipWidth * 2);\n }\n\n tooltipX = tooltipX.toString() + 'px';\n tooltip.style.position = 'absolute';\n tooltip.style.top = tooltipY;\n tooltip.style.left = tooltipX;\n\n // Show tooltip\n this.timelineEventTooltip.classList.remove('spbc-timeline-hidden-element');\n jQuery('[data-event-type]').filter(function() {\n return jQuery(this).data('event-type') === eventName;\n }).addClass('spbc-timelime-highlight-on-tooltip');\n }\n\n /**\n * Hide event tooltip\n */\n hideEventTooltip() {\n this.timelineEventTooltip.classList.add('spbc-timeline-hidden-element');\n jQuery('[data-event-type]').removeClass('spbc-timelime-highlight-on-tooltip');\n }\n\n /**\n * Handle click on event marker - filter security log table by user\n * @param {string} user - User name\n * @param {Event} event - Click event\n */\n handleEventClick(user) {\n this.hideEventTooltip();\n\n // Update custom dropdown selector to show selected user\n const dropdown = document.getElementById('spbc-timeline-user-select-dropdown');\n const valueDisplay = document.getElementById('spbc-timeline-user-select-value');\n if (dropdown && valueDisplay) {\n this.selectedUserValue = user;\n // Find the option with matching value\n const options = dropdown.querySelectorAll('.spbc-timeline-select-option');\n options.forEach((opt) => {\n opt.classList.remove('selected');\n if (opt.dataset.value === user) {\n opt.classList.add('selected');\n valueDisplay.textContent = opt.textContent;\n }\n });\n }\n\n // Redraw timeline for the selected user\n this.drawUserTimeline(user);\n\n // Update URL with users parameter\n const url = new URL(window.location);\n url.searchParams.set('users', user);\n window.history.pushState({}, '', url);\n\n // Update security log table with selected user\n updateSecurityLogTable();\n }\n\n /**\n * Get most active users from last 24 hours (max 15 users)\n * @param {Object} eventsData - User events data\n * @return {Array} Array of user objects with their events\n */\n getMostActiveUsers(eventsData) {\n const currentTime = this.currentTimestamp;\n const twentyFourHoursAgo = currentTime - (24 * 3600);\n\n // Filter users with events in last 24 hours and count their events\n const usersWithRecentEvents = [];\n\n for (let user in eventsData) {\n if (eventsData.hasOwnProperty(user)) {\n const userData = eventsData[user];\n const eventsArray = Array.isArray(userData) ? userData : (userData.events || []);\n\n // Filter events from last 24 hours\n const recentEvents = eventsArray.filter((event) => event.time >= twentyFourHoursAgo);\n\n if (recentEvents.length > 0) {\n usersWithRecentEvents.push({\n user: user,\n events: recentEvents,\n count: recentEvents.length,\n role: userData.role || 'unknown',\n });\n }\n }\n }\n\n // Sort by event count (descending) and take top 15\n usersWithRecentEvents.sort((a, b) => b.count - a.count);\n\n return usersWithRecentEvents.slice(0, 15);\n }\n\n /**\n * Group events into a 24-element array (one slot per calendar hour).\n * @param {string} user\n * @param {Array} events - array of {event, time}\n * @return {Array} 24 items: {hour, user, count, byType}\n */\n groupByHour(user, events) {\n // Group events by actual hour slots (using real timestamps, not just hour of day)\n const slots = {};\n\n events.forEach((ev) => {\n // Round down to the start of the hour\n const hourStart = Math.floor(ev.time / 3600) * 3600;\n\n if (!slots[hourStart]) {\n slots[hourStart] = {\n timestamp: hourStart + 1800, // midpoint of the hour\n user: user,\n count: 0,\n byType: {},\n };\n }\n\n slots[hourStart].count++;\n slots[hourStart].byType[ev.event] = (slots[hourStart].byType[ev.event] || 0) + 1;\n });\n\n return Object.values(slots);\n }\n\n /**\n * Draw timeline for specific user\n * @param {string} user - Selected user name\n */\n drawUserTimeline(user) {\n const svg = document.getElementById('spbc-timeline-svg');\n const noDataMessage = document.getElementById('spbc-timeline-no-data');\n\n // Clear previous content\n svg.innerHTML = '';\n noDataMessage.style.display = 'none';\n\n let additionalEventsHeight = 0;\n if (spbcTimelineWidgetData.found_types.length > 5) {\n additionalEventsHeight = (spbcTimelineWidgetData.found_types.length - 5) * 12;\n }\n\n // Calculate dimensions\n const width = svg.parentElement.clientWidth;\n const height = 150 + additionalEventsHeight + SpbcTimelineWidget.userLegendAxisOffset;\n\n // Setup drawing area\n const margin = {top: 10 + additionalEventsHeight, right: 40, bottom: 40, left: 140};\n const contentWidth = width - margin.left - margin.right;\n const contentHeight = height - margin.top - margin.bottom;\n const baseLineY = contentHeight / 2;\n\n svg.setAttribute('width', width);\n svg.setAttribute('height', height);\n\n const g = document.createElementNS('http://www.w3.org/2000/svg', 'g');\n g.setAttribute('transform', `translate(${margin.left}, ${margin.top+30})`);\n svg.appendChild(g);\n\n // Calculate base positions\n\n\n // Draw axis\n this.drawTimeAxis(g, contentWidth, baseLineY + 10);\n\n // Filter user events\n const userData = this.eventsData[user];\n const userEvents = (Array.isArray(userData) ? userData : (userData.events || [])).filter((event) =>\n event.time >= this.timelineStartTimestamp &&\n event.time <= this.currentTimestamp,\n );\n\n // Handle no events case\n if (userEvents.length === 0) {\n noDataMessage.style.display = 'block';\n return;\n }\n\n const timeRange = this.currentTimestamp - this.timelineStartTimestamp;\n const uniqueEventsCount = spbcTimelineWidgetData.found_types.length;\n\n // Draw event lines and legends\n for (let i = 0; i < uniqueEventsCount; i++) {\n const userLineY = baseLineY - (SpbcTimelineWidget.userActionsUpstep * i);\n const eventType = spbcTimelineWidgetData.found_types[i];\n const color = this.timelineMarkersData[eventType].color;\n\n // Draw event line\n const userLine = document.createElementNS('http://www.w3.org/2000/svg', 'line');\n userLine.setAttribute('class', 'spbc-timeline-user-line');\n userLine.setAttribute('x1', '0');\n userLine.setAttribute('y1', userLineY);\n userLine.setAttribute('x2', contentWidth);\n userLine.setAttribute('y2', userLineY);\n userLine.setAttribute('data-event-type', eventType);\n g.appendChild(userLine);\n\n // Draw legend circle\n const legendCircle = document.createElementNS('http://www.w3.org/2000/svg', 'circle');\n legendCircle.setAttribute('class', 'spbc-timeline-legend-circle');\n legendCircle.setAttribute('cx', SpbcTimelineWidget.legendCircleX);\n legendCircle.setAttribute('cy', userLineY);\n legendCircle.setAttribute('r', SpbcTimelineWidget.markerRadius);\n legendCircle.style.fill = color;\n g.appendChild(legendCircle);\n\n // Draw legend text\n const legendText = document.createElementNS('http://www.w3.org/2000/svg', 'text');\n legendText.setAttribute('class', 'spbc-timeline-legend-text');\n legendText.setAttribute('x', SpbcTimelineWidget.legendTextX);\n legendText.setAttribute('y', userLineY);\n legendText.setAttribute('text-anchor', 'start');\n legendText.setAttribute('dominant-baseline', 'middle');\n legendText.setAttribute('data-event-type', eventType);\n legendText.textContent = eventType;\n g.appendChild(legendText);\n }\n\n // Draw events\n userEvents.forEach((userEvent) => {\n const x = ((userEvent.time - this.timelineStartTimestamp) / timeRange) * contentWidth;\n const eventY = baseLineY + this.timelineMarkersData[userEvent.event].upstep;\n g.appendChild(this.createEventElement(userEvent, x, eventY, user));\n });\n }\n\n /**\n * Measure text width in SVG\n * @param {SVGElement} svg - SVG container element\n * @param {string} text - Text to measure\n * @param {string} className - CSS class for text styling\n * @return {number} Text width in pixels\n */\n measureTextWidth(svg, text, className = 'spbc-timeline-legend-text') {\n const tempText = document.createElementNS('http://www.w3.org/2000/svg', 'text');\n tempText.setAttribute('class', className);\n tempText.textContent = text;\n tempText.style.visibility = 'hidden';\n svg.appendChild(tempText);\n const width = tempText.getComputedTextLength();\n svg.removeChild(tempText);\n return width;\n }\n\n /**\n * Calculate how many rows the user legend will occupy\n * @param {SVGElement} svg - SVG container element\n * @param {Array} users - Array of user data objects\n * @param {number} maxWidth - Maximum width for the legend area\n * @param {number|null} maxRows - Maximum rows limit (null for unlimited)\n * @return {number} Number of rows needed\n */\n calculateUserLegendRowsCount(svg, users, maxWidth, maxRows = null) {\n const itemGap = SpbcTimelineWidget.userLegendItemGap;\n const circleRadius = SpbcTimelineWidget.markerRadius;\n const circleTextGap = 6;\n const startX = 0;\n\n // Calculate \"and N more\" text width for reservation\n const moreTextTemplate = 'and 99 more';\n const moreTextWidth = this.measureTextWidth(svg, moreTextTemplate) + itemGap;\n\n let currentX = startX;\n let currentRow = 0;\n\n for (let i = 0; i < users.length; i++) {\n const userData = users[i];\n const textWidth = this.measureTextWidth(svg, userData.user);\n const itemWidth = circleRadius * 2 + circleTextGap + textWidth + itemGap;\n\n // Check if we need to reserve space for \"and N more\" on last allowed row\n const isLastRow = maxRows !== null && currentRow === maxRows - 1;\n const reserveForMore = isLastRow && (i < users.length - 1);\n const effectiveMaxWidth = reserveForMore ? maxWidth - moreTextWidth : maxWidth;\n\n if (currentX + itemWidth > effectiveMaxWidth && currentX > startX) {\n currentRow++;\n currentX = startX;\n\n // If we hit maxRows limit, stop counting\n if (maxRows !== null && currentRow >= maxRows) {\n return maxRows;\n }\n }\n\n currentX += itemWidth;\n }\n\n return currentRow + 1; // +1 because rows are 0-indexed\n }\n\n /**\n * Draw timeline for most active users\n */\n drawUserTimelineMostActive() {\n const svg = document.getElementById('spbc-timeline-svg');\n const noDataMessage = document.getElementById('spbc-timeline-no-data');\n\n // Clear previous content\n svg.innerHTML = '';\n noDataMessage.style.display = 'none';\n\n // Get most active users\n const mostActiveUsers = this.getMostActiveUsers(this.eventsData);\n\n if (mostActiveUsers.length === 0) {\n noDataMessage.style.display = 'block';\n return;\n }\n\n let additionalEventsHeight = 0;\n if (spbcTimelineWidgetData.found_types.length > 5) {\n additionalEventsHeight = (spbcTimelineWidgetData.found_types.length - 5) * 12;\n }\n\n // Pre-assign a color to each user\n const randomColors = [\n '#0284A8', '#E74C3C', '#2ECC71', '#F39C12', '#9B59B6',\n '#1ABC9C', '#E67E22', '#3498DB', '#E91E63', '#00BCD4',\n '#8BC34A', '#FF5722', '#607D8B', '#795548', '#673AB7',\n ];\n const userColors = {};\n mostActiveUsers.forEach((userData, i) => {\n userColors[userData.user] = randomColors[(i + 1) % randomColors.length];\n });\n\n // Calculate dimensions - need to know content width first for row calculation\n const width = svg.parentElement.clientWidth;\n const margin = {top: 10 + additionalEventsHeight, right: 40, bottom: 40, left: 140};\n const contentWidth = width - margin.left - margin.right;\n\n // Calculate user legend rows count based on expanded state\n const userLegendRowHeight = SpbcTimelineWidget.userLegendRowHeight;\n const maxRows = this.userLegendExpanded ? null : SpbcTimelineWidget.userLegendMaxRows;\n const actualRowsCount = this.calculateUserLegendRowsCount(svg, mostActiveUsers, contentWidth, maxRows);\n const userLegendHeight = actualRowsCount * userLegendRowHeight;\n\n // Calculate height: base height (like drawUserTimeline) + user legend space\n const baseHeight = 150 + additionalEventsHeight + SpbcTimelineWidget.userLegendAxisOffset;\n const height = baseHeight + userLegendHeight;\n\n // Setup drawing area - baseLineY calculated from baseHeight (same as drawUserTimeline)\n const baseContentHeight = baseHeight - margin.top - margin.bottom;\n const baseLineY = baseContentHeight / 2;\n\n svg.setAttribute('width', width);\n svg.setAttribute('height', height);\n\n const g = document.createElementNS('http://www.w3.org/2000/svg', 'g');\n g.setAttribute('transform', `translate(${margin.left}, ${margin.top+30})`);\n svg.appendChild(g);\n\n // Draw axis\n this.drawTimeAxis(g, contentWidth, baseLineY + 10, true);\n\n const timeRange = this.currentTimestamp - this.timelineStartTimestamp;\n const uniqueEventsCount = spbcTimelineWidgetData.found_types.length;\n\n // Draw event lines and legends\n for (let i = 0; i < uniqueEventsCount; i++) {\n const userLineY = baseLineY - (SpbcTimelineWidget.userActionsUpstep * i);\n const eventType = spbcTimelineWidgetData.found_types[i];\n const color = this.timelineMarkersData[eventType].color;\n\n // Draw event line\n const userLine = document.createElementNS('http://www.w3.org/2000/svg', 'line');\n userLine.setAttribute('class', 'spbc-timeline-user-line');\n userLine.setAttribute('x1', '0');\n userLine.setAttribute('y1', userLineY);\n userLine.setAttribute('x2', contentWidth);\n userLine.setAttribute('y2', userLineY);\n userLine.setAttribute('data-event-type', eventType);\n g.appendChild(userLine);\n\n // Draw legend circle\n const legendCircle = document.createElementNS('http://www.w3.org/2000/svg', 'circle');\n legendCircle.setAttribute('class', 'spbc-timeline-legend-circle');\n legendCircle.setAttribute('cx', SpbcTimelineWidget.legendCircleX);\n legendCircle.setAttribute('cy', userLineY);\n legendCircle.setAttribute('r', SpbcTimelineWidget.markerRadius);\n legendCircle.style.fill = color;\n g.appendChild(legendCircle);\n\n // Draw legend text\n const legendText = document.createElementNS('http://www.w3.org/2000/svg', 'text');\n legendText.setAttribute('class', 'spbc-timeline-legend-text');\n legendText.setAttribute('x', SpbcTimelineWidget.legendTextX);\n legendText.setAttribute('y', userLineY);\n legendText.setAttribute('text-anchor', 'start');\n legendText.setAttribute('dominant-baseline', 'middle');\n legendText.setAttribute('data-event-type', eventType);\n legendText.textContent = eventType;\n g.appendChild(legendText);\n }\n\n // Draw user color legend (below axis)\n const userLegendStartY = baseLineY + SpbcTimelineWidget.userLegendAxisOffset;\n this.drawUserLegend(svg, g, mostActiveUsers, userColors, userLegendStartY, contentWidth);\n\n // Draw events for all most active users, grouped by hour\n mostActiveUsers.forEach((userData) => {\n const eventColor = userColors[userData.user];\n\n const hourly = this.groupByHour(userData.user, userData.events);\n hourly.forEach((slot) => {\n if (slot.count === 0) return;\n const x = Math.max(\n 0,\n Math.min(\n ((slot.timestamp - this.timelineStartTimestamp) / timeRange) * contentWidth, contentWidth,\n ),\n );\n Object.entries(slot.byType).forEach(([eventType, cnt]) => {\n const eventY = baseLineY + this.timelineMarkersData[eventType].upstep;\n const radius = SpbcTimelineWidget.markerRadius + Math.min(Math.sqrt(cnt - 1) * 2, 8);\n const syntheticEvent = {event: eventType, time: slot.timestamp, count: cnt};\n g.appendChild(\n this.createEventElement(\n syntheticEvent,\n x,\n eventY,\n userData.user,\n radius,\n true,\n eventColor,\n ),\n );\n });\n });\n });\n }\n\n /**\n * Draw user legend with horizontal layout and row limit\n * @param {SVGElement} svg - SVG element\n * @param {SVGElement} g - Group element to append to\n * @param {Array} users - Array of user data objects\n * @param {Object} userColors - Map of user to color\n * @param {number} startY - Starting Y position\n * @param {number} maxWidth - Maximum width for the legend area\n */\n drawUserLegend(svg, g, users, userColors, startY, maxWidth) {\n const rowHeight = SpbcTimelineWidget.userLegendRowHeight;\n const itemGap = SpbcTimelineWidget.userLegendItemGap;\n const circleRadius = SpbcTimelineWidget.markerRadius;\n const circleTextGap = 6;\n const startX = 0;\n\n // Determine max rows: null for expanded (unlimited), or limit for collapsed\n const maxRows = this.userLegendExpanded ? null : SpbcTimelineWidget.userLegendMaxRows;\n\n // Horizontal layout with optional row limit\n let currentX = startX;\n let currentRow = 0;\n let visibleCount = 0;\n let lastRowEndX = startX; // Track end position on the last allowed row for \"and N more\" button\n\n // Calculate \"and N more\" text width for reservation\n const moreTextTemplate = 'and 99 more';\n const moreTextWidth = this.measureTextWidth(svg, moreTextTemplate) + itemGap;\n\n for (let i = 0; i < users.length; i++) {\n const userData = users[i];\n const textWidth = this.measureTextWidth(svg, userData.user);\n const itemWidth = circleRadius * 2 + circleTextGap + textWidth + itemGap;\n\n // Check if we need to wrap to next row\n const isLastRow = maxRows !== null && currentRow === maxRows - 1;\n const reserveForMore = isLastRow && (i < users.length - 1);\n const effectiveMaxWidth = reserveForMore ? maxWidth - moreTextWidth : maxWidth;\n\n if (currentX + itemWidth > effectiveMaxWidth && currentX > startX) {\n // Before moving to next row, save position if we're on the last allowed row\n if (isLastRow) {\n lastRowEndX = currentX;\n }\n\n currentRow++;\n currentX = startX;\n\n // Check if we exceeded max rows (only when maxRows is set)\n if (maxRows !== null && currentRow >= maxRows) {\n // Draw \"and N more\" link at the end of the last row (continuation of the line)\n const remainingCount = users.length - visibleCount;\n if (remainingCount > 0) {\n const moreText = `and ${remainingCount} more`;\n const ly = startY + (maxRows - 1) * rowHeight;\n\n const moreLink = document.createElementNS('http://www.w3.org/2000/svg', 'text');\n moreLink.setAttribute('class', 'spbc-timeline-legend-text spbc-timeline-more-link');\n moreLink.setAttribute('x', lastRowEndX);\n moreLink.setAttribute('y', ly);\n moreLink.setAttribute('text-anchor', 'start');\n moreLink.setAttribute('dominant-baseline', 'middle');\n moreLink.textContent = moreText;\n moreLink.style.cursor = 'pointer';\n moreLink.style.fill = '#0073aa';\n moreLink.style.textDecoration = 'underline';\n moreLink.addEventListener('click', () => {\n this.userLegendExpanded = true;\n this.drawUserTimelineMostActive();\n });\n g.appendChild(moreLink);\n }\n return;\n }\n }\n\n // Track position on the last allowed row\n if (maxRows !== null && currentRow === maxRows - 1) {\n lastRowEndX = currentX + itemWidth;\n }\n\n // Draw user legend item\n const ly = startY + currentRow * rowHeight;\n const cx = currentX + circleRadius;\n const tx = currentX + circleRadius * 2 + circleTextGap;\n\n const circle = document.createElementNS('http://www.w3.org/2000/svg', 'circle');\n circle.setAttribute('cx', cx);\n circle.setAttribute('cy', ly);\n circle.setAttribute('r', circleRadius);\n circle.style.fill = userColors[userData.user];\n g.appendChild(circle);\n\n const text = document.createElementNS('http://www.w3.org/2000/svg', 'text');\n text.setAttribute('class', 'spbc-timeline-legend-text');\n text.setAttribute('x', tx);\n text.setAttribute('y', ly);\n text.setAttribute('text-anchor', 'start');\n text.setAttribute('dominant-baseline', 'middle');\n text.textContent = userData.user;\n g.appendChild(text);\n\n currentX += itemWidth;\n visibleCount++;\n }\n }\n\n /*\n /!**\n * Updates the security log table based on selected user\n * @param {string} selectedUser - The selected user or 'most_active__action'\n *!/\n updateSecurityLogTable(selectedUser) {\n const data= {};\n const urlParams = new URLSearchParams(window.location.search);\n const usersParam = urlParams.get('users');\n if (usersParam) {\n data.users = usersParam;\n }\n spbcSendAJAXRequest(data, params, obj);\n }*/\n\n /**\n * Generate marker data for events\n * @param {Array} events - List of event types\n * @return {Object} Marker configuration data\n */\n generateMarkersData(events) {\n const colorMap = {};\n let upstep = 0;\n let colorScheme = 'default';\n\n events.forEach((userEvent) => {\n if (this.timelineKnownEventsData[userEvent] && this.timelineKnownEventsData[userEvent].color_scheme) {\n colorScheme = this.timelineKnownEventsData[userEvent].color_scheme;\n }\n colorMap[userEvent] = {\n color: colorScheme,\n upstep: upstep,\n };\n upstep -= SpbcTimelineWidget.userActionsUpstep;\n });\n\n return colorMap;\n }\n}\n\n/**\n * Wrapper\n */\nfunction spbcTimelineWidgetRender() { // eslint-disable-line no-unused-vars\n const timelineWidget = new SpbcTimelineWidget();\n jQuery( function() {\n jQuery('#spbc-timeline-accordion').accordion({\n heightStyle: 'content',\n collapsible: true,\n active: false,\n });\n } );\n timelineWidget.render();\n}\n\n\n"],"names":["SpbcTimelineWidget","markerRadius","hoursRangeMax","axisStepsCount","userActionsUpstep","legendCircleX","legendTextX","userLegendMaxRows","userLegendRowHeight","userLegendItemGap","userLegendAxisOffset","constructor","this","now","Date","currentTimestamp","Math","floor","getTime","eventsData","timelineMarkersData","timelineKnownEventsData","timelineEventTooltip","timelineStartTimestamp","usersCount","summaryEventsCount","userLegendExpanded","selectedUserValue","render","spbcTimelineWidgetData","document","getElementById","users_actions","current_timestamp_local","Number","first_event_timestamp","known_events_data","generateMarkersData","found_types","window","addEventListener","event","detail","tab","console","log","initTimeline","jQuery","on","e","hasOwnProperty","localStorage","getItem","setItem","drawUserTimelineMostActive","drawUserTimeline","wrapper","trigger","dropdown","valueDisplay","key","userData","recentEvents","usersWithRecentEvents","Array","isArray","events","filter","time","length","push","user","role","eventsCount","innerHTML","createOption","text","value","option","createElement","className","dataset","textContent","mostActiveOption","activitySelector","classList","add","appendChild","forEach","displayText","stopPropagation","toggle","contains","target","remove","selectedValue","closest","querySelectorAll","opt","url","URL","location","searchParams","delete","history","pushState","set","updateSecurityLogTable","querySelector","style","display","isWidgetClosedByUser","innerText","replace","toString","setTimeout","drawTimeAxis","container","width","height","roundToHour","timeRange","axis","createElementNS","setAttribute","let","ts","ceil","x","tick","label","h","getUTCHours","padStart","i","timestamp","date","getUTCMinutes","createEventElement","y","radius","isMostActive","color","group","marker","JSON","stringify","fill","showEventTooltip","bind","hideEventTooltip","handleEventClick","eventData","parse","currentTarget","getAttribute","eventName","count","timeRow","parentElement","tooltip","month","getUTCMonth","day","getUTCDate","String","getUTCFullYear","getUTCSeconds","tooltipY","offsetY","screenWidth","innerWidth","tooltipWidth","offsetWidth","tooltipEventPositionX","clientX","tooltipX","position","top","left","data","addClass","removeClass","getMostActiveUsers","twentyFourHoursAgo","sort","a","b","slice","groupByHour","slots","hourStart","ev","byType","Object","values","svg","noDataMessage","additionalEventsHeight","clientWidth","margin","contentWidth","baseLineY","g","userEvents","uniqueEventsCount","userLineY","eventType","userLine","legendCircle","legendText","userEvent","eventY","upstep","measureTextWidth","tempText","visibility","getComputedTextLength","removeChild","calculateUserLegendRowsCount","users","maxWidth","maxRows","itemGap","circleRadius","moreTextWidth","currentX","currentRow","itemWidth","effectiveMaxWidth","mostActiveUsers","randomColors","userColors","userLegendHeight","baseHeight","userLegendStartY","drawUserLegend","eventColor","slot","max","min","entries","cnt","sqrt","syntheticEvent","startY","rowHeight","visibleCount","lastRowEndX","isLastRow","remainingCount","moreText","ly","moreLink","cursor","textDecoration","cx","tx","circle","colorMap","colorScheme","color_scheme","spbcTimelineWidgetRender","timelineWidget","accordion","heightStyle","collapsible","active"],"mappings":"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"} \ No newline at end of file +{"version":3,"file":"spbc-timeline-widget.min.js","sources":["spbc-timeline-widget.js"],"sourcesContent":["/**\n * Class to render a timeline widget for Security log\n */\nclass SpbcTimelineWidget {\n // Static constants\n static markerRadius = 4;\n static hoursRangeMax = 24;\n static axisStepsCount = 6;\n static userActionsUpstep = 24;\n static legendCircleX = -120;\n static legendTextX = -105;\n static userLegendMaxRows = 2;\n static userLegendRowHeight = 18;\n static userLegendItemGap = 15;\n static userLegendAxisOffset = 60; // Offset from axis to user legend\n\n /**\n * Constructor.\n */\n constructor() {\n // Current time properties\n this.now = new Date();\n this.currentTimestamp = Math.floor(this.now.getTime() / 1000);\n\n // Data properties\n this.eventsData = null;\n this.timelineMarkersData = [];\n this.timelineKnownEventsData = [];\n this.timelineEventTooltip = null;\n this.timelineStartTimestamp = this.currentTimestamp - SpbcTimelineWidget.hoursRangeMax * 3600;\n this.usersCount = 0;\n this.summaryEventsCount = 0;\n this.userLegendExpanded = false;\n this.selectedUserValue = 'most_active__action';\n }\n\n /**\n * Initialize and render the timeline widget\n */\n render() {\n if (typeof spbcTimelineWidgetData === 'undefined') {\n return;\n }\n\n this.timelineEventTooltip = document.getElementById('spbc-timeline-tooltip');\n this.eventsData = spbcTimelineWidgetData.users_actions;\n if (spbcTimelineWidgetData.current_timestamp_local) {\n this.currentTimestamp = Number(spbcTimelineWidgetData.current_timestamp_local);\n }\n this.timelineStartTimestamp = Number(spbcTimelineWidgetData.first_event_timestamp);\n this.timelineKnownEventsData = spbcTimelineWidgetData.known_events_data;\n this.timelineMarkersData = this.generateMarkersData(spbcTimelineWidgetData.found_types);\n\n // Initialize timeline when tab is loaded\n window.addEventListener('spbctTabLoaded', (event) => {\n if (event.detail.tab === 'security_log') {\n this.initTimeline();\n }\n });\n\n jQuery('#spbc-timeline-tab').on('click', (e)=> {\n if (e.hasOwnProperty('originalEvent')) {\n if (localStorage.getItem('spbc_timeline_closed') !== '1') {\n localStorage.setItem('spbc_timeline_closed', '1');\n } else {\n localStorage.setItem('spbc_timeline_closed', '0');\n }\n }\n });\n\n // Handle window resize\n window.addEventListener('resize', () => {\n if (!this.selectedUserValue) {\n return;\n }\n this.userLegendExpanded = false;\n if (this.selectedUserValue === 'most_active__action') {\n this.drawUserTimelineMostActive();\n } else {\n this.drawUserTimeline(this.selectedUserValue);\n }\n });\n }\n\n /**\n * Initialize timeline with user selection\n */\n initTimeline() {\n const wrapper = document.getElementById('spbc-timeline-user-select-wrapper');\n const trigger = document.getElementById('spbc-timeline-user-select-trigger');\n const dropdown = document.getElementById('spbc-timeline-user-select-dropdown');\n const valueDisplay = document.getElementById('spbc-timeline-user-select-value');\n const usersWithRecentEvents = [];\n\n // Calculate summary statistics for the last 24 hours only\n this.usersCount = 0;\n this.summaryEventsCount = 0;\n\n for (let key in this.eventsData) {\n if (this.eventsData.hasOwnProperty(key)) {\n const userData = this.eventsData[key];\n const eventsArray = Array.isArray(userData) ? userData : (userData.events || []);\n const recentEvents = eventsArray.filter((event) =>\n event.time >= this.timelineStartTimestamp &&\n event.time <= this.currentTimestamp,\n );\n\n if (recentEvents.length > 0) {\n this.usersCount++;\n this.summaryEventsCount += recentEvents.length;\n usersWithRecentEvents.push({\n user: key,\n role: userData.role || 'unknown',\n eventsCount: recentEvents.length,\n });\n }\n }\n }\n\n // Reset dropdown options\n dropdown.innerHTML = '';\n\n // Helper to create option element\n const createOption = (text, value) => {\n const option = document.createElement('div');\n option.className = 'spbc-timeline-select-option';\n option.dataset.value = value;\n option.textContent = text;\n return option;\n };\n\n // Add \"Most Active Users\" option\n const mostActiveOption = createOption('Most Active Users', 'most_active__action');\n mostActiveOption.classList.add('selected');\n dropdown.appendChild(mostActiveOption);\n\n // Populate user options with role and event count\n usersWithRecentEvents.forEach((userData) => {\n const displayText = `${userData.user} (${userData.role}) (${userData.eventsCount} events)`;\n dropdown.appendChild(createOption(displayText, userData.user));\n });\n\n // Toggle dropdown on trigger click\n trigger.addEventListener('click', (e) => {\n e.stopPropagation();\n wrapper.classList.toggle('open');\n });\n\n // Close dropdown on outside click\n document.addEventListener('click', (e) => {\n if (!wrapper.contains(e.target)) {\n wrapper.classList.remove('open');\n }\n });\n\n // Handle option selection\n dropdown.addEventListener('click', (e) => {\n const option = e.target.closest('.spbc-timeline-select-option');\n if (!option) return;\n\n const selectedValue = option.dataset.value;\n this.selectedUserValue = selectedValue;\n this.userLegendExpanded = false;\n\n // Update display and selection state\n valueDisplay.textContent = option.textContent;\n dropdown.querySelectorAll('.spbc-timeline-select-option').forEach((opt) => {\n opt.classList.remove('selected');\n });\n option.classList.add('selected');\n\n // Close dropdown\n wrapper.classList.remove('open');\n\n if (selectedValue === 'most_active__action') {\n this.drawUserTimelineMostActive();\n const url = new URL(window.location);\n url.searchParams.delete('users');\n window.history.pushState({}, '', url);\n } else {\n this.drawUserTimeline(selectedValue);\n const url = new URL(window.location);\n url.searchParams.set('users', selectedValue);\n window.history.pushState({}, '', url);\n }\n\n // Update security log table with selected user\n updateSecurityLogTable();\n });\n\n // Draw initial timeline or show message\n if (usersWithRecentEvents.length > 0) {\n this.drawUserTimelineMostActive();\n this.selectedUserValue = 'most_active__action';\n valueDisplay.textContent = 'Most Active Users';\n } else {\n document.querySelector('.spbc-timeline-container').style.display = 'none';\n document.querySelector('.spbc-timeline-controls').style.display = 'none';\n document.getElementById('spbc-timeline-no-data').style.display = 'block';\n }\n\n const activitySelector = jQuery('#spbc_timeline_activity_counters')[0];\n activitySelector.innerText = activitySelector.innerText\n .replace('%users_count%', this.usersCount.toString())\n .replace('%events_count%', this.summaryEventsCount.toString());\n\n const isWidgetClosedByUser = (\n localStorage.getItem('spbc_timeline_closed') === '1'\n );\n if (!isWidgetClosedByUser) {\n setTimeout(function() {\n jQuery('#spbc-timeline-tab').trigger('click');\n }, 200);\n }\n }\n\n /**\n * Draw time axis on the timeline\n * @param {SVGElement} container - SVG container element\n * @param {number} width - Drawing area width\n * @param {number} height - Drawing area height\n * @param {boolean} roundToHour - Whether to round ticks to full hours\n */\n drawTimeAxis(container, width, height, roundToHour = false) {\n const timeRange = this.currentTimestamp - this.timelineStartTimestamp;\n const axis = document.createElementNS('http://www.w3.org/2000/svg', 'line');\n height = height + 10;\n // Create main axis line\n axis.setAttribute('class', 'spbc-timeline-axis');\n axis.setAttribute('x1', '0');\n axis.setAttribute('y1', height);\n axis.setAttribute('x2', width);\n axis.setAttribute('y2', height);\n container.appendChild(axis);\n\n // Create axis ticks and labels\n if (roundToHour) {\n const firstHourTs = Math.ceil(this.timelineStartTimestamp / 3600) * 3600;\n for (let ts = firstHourTs; ts <= this.currentTimestamp; ts += 3600) {\n const x = ((ts - this.timelineStartTimestamp) / timeRange) * width;\n\n // Create tick mark\n const tick = document.createElementNS('http://www.w3.org/2000/svg', 'line');\n tick.setAttribute('class', 'spbc-timeline-axis');\n tick.setAttribute('x1', x);\n tick.setAttribute('y1', height);\n tick.setAttribute('x2', x);\n tick.setAttribute('y2', height + 5);\n container.appendChild(tick);\n\n const label = document.createElementNS('http://www.w3.org/2000/svg', 'text');\n label.setAttribute('class', 'spbc-timeline-axis-text');\n label.setAttribute('x', x);\n label.setAttribute('y', height + 20);\n const h = new Date(ts * 1000).getUTCHours();\n label.textContent = `${h.toString().padStart(2, '0')}:00`;\n container.appendChild(label);\n }\n } else {\n for (let i = 0; i <= SpbcTimelineWidget.axisStepsCount; i++) {\n const timestamp = this.timelineStartTimestamp + (i / SpbcTimelineWidget.axisStepsCount) * timeRange;\n const x = (i / SpbcTimelineWidget.axisStepsCount) * width;\n\n // Create tick mark\n const tick = document.createElementNS('http://www.w3.org/2000/svg', 'line');\n tick.setAttribute('class', 'spbc-timeline-axis');\n tick.setAttribute('x1', x);\n tick.setAttribute('y1', height);\n tick.setAttribute('x2', x);\n tick.setAttribute('y2', height + 5);\n container.appendChild(tick);\n\n // Create time label\n const label = document.createElementNS('http://www.w3.org/2000/svg', 'text');\n label.setAttribute('class', 'spbc-timeline-axis-text');\n label.setAttribute('x', x);\n label.setAttribute('y', height + 20);\n const date = new Date(timestamp * 1000);\n label.textContent =\n `\n ${date.getUTCHours().toString().padStart(2, '0')}:\n ${date.getUTCMinutes().toString().padStart(2, '0')}\n `;\n container.appendChild(label);\n }\n }\n }\n\n /**\n * Create SVG element for an event\n * @param {Object} event - Event data\n * @param {number} x - X coordinate\n * @param {number} y - Y coordinate\n * @param {string} user - User name\n * @param {number} radius - Marker radius\n * @param {boolean} isMostActive - Whether this is a synthetic marker for most active users view\n * @param {string} color - Optional color override for the marker\n * @return {SVGElement} Event group element\n */\n createEventElement(event, x, y, user, radius, isMostActive= false, color) {\n const group = document.createElementNS('http://www.w3.org/2000/svg', 'g');\n group.setAttribute('transform', `translate(${x}, ${y})`);\n group.setAttribute('data-event', JSON.stringify({...event, user, isMostActive}));\n\n // Create event marker\n const marker = document.createElementNS('http://www.w3.org/2000/svg', 'circle');\n marker.setAttribute('class', 'spbc-timeline-event-marker');\n marker.setAttribute('r', radius || SpbcTimelineWidget.markerRadius);\n marker.style.fill = color || this.timelineMarkersData[event.event].color;\n group.appendChild(marker);\n\n // Add event handlers\n group.addEventListener('mouseenter', this.showEventTooltip.bind(this));\n group.addEventListener('mouseleave', this.hideEventTooltip.bind(this));\n\n if (isMostActive) {\n group.addEventListener('click', this.handleEventClick.bind(this, user));\n }\n\n return group;\n }\n\n /**\n * Show event tooltip\n * @param {Event} event - Mouse event\n */\n showEventTooltip(event) {\n const eventData = JSON.parse(event.currentTarget.getAttribute('data-event'));\n const {user, event: eventName, time, isMostActive} = eventData;\n\n // Format date for display\n const formatDate = (timestamp) => {\n const date = new Date(timestamp * 1000);\n\n const months = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun',\n 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'];\n\n const month = months[date.getUTCMonth()];\n const day = date.getUTCDate();\n const dayWithZero = String(day).padStart(2, '0');\n const year = date.getUTCFullYear();\n\n const hours = date.getUTCHours().toString().padStart(2, '0');\n const minutes = date.getUTCMinutes().toString().padStart(2, '0');\n const seconds = date.getUTCSeconds().toString().padStart(2, '0');\n\n return `${month} ${dayWithZero} ${year} ${hours}:${minutes}:${seconds}`;\n };\n\n // Update tooltip content\n document.getElementById('spbc-timeline-tooltip--user').innerText = user;\n document.getElementById('spbc-timeline-tooltip--event').innerText =\n eventData.count > 1 ? `${eventName} (${eventData.count})` : eventName;\n\n const timeRow = document.getElementById('spbc-timeline-tooltip--time').parentElement;\n if (isMostActive) {\n timeRow.style.display = 'none';\n } else {\n timeRow.style.display = '';\n document.getElementById('spbc-timeline-tooltip--time').innerText = formatDate(time);\n }\n\n const tooltip= document.getElementById('spbc-timeline-tooltip');\n const fixedTop = event.offsetY + 35;\n const tooltipY = fixedTop.toString() + 'px';\n const screenWidth = window.innerWidth;\n const tooltipWidth = tooltip.offsetWidth ? tooltip.offsetWidth : 165;\n const tooltipEventPositionX = event.clientX;\n let tooltipX = tooltipEventPositionX - tooltipWidth;\n if (tooltipEventPositionX + (tooltipWidth * 2) > screenWidth) {\n tooltipX = tooltipEventPositionX - (tooltipWidth * 2);\n }\n\n tooltipX = tooltipX.toString() + 'px';\n tooltip.style.position = 'absolute';\n tooltip.style.top = tooltipY;\n tooltip.style.left = tooltipX;\n\n // Show tooltip\n this.timelineEventTooltip.classList.remove('spbc-timeline-hidden-element');\n jQuery('[data-event-type]').filter(function() {\n return jQuery(this).data('event-type') === eventName;\n }).addClass('spbc-timelime-highlight-on-tooltip');\n }\n\n /**\n * Hide event tooltip\n */\n hideEventTooltip() {\n this.timelineEventTooltip.classList.add('spbc-timeline-hidden-element');\n jQuery('[data-event-type]').removeClass('spbc-timelime-highlight-on-tooltip');\n }\n\n /**\n * Handle click on event marker - filter security log table by user\n * @param {string} user - User name\n * @param {Event} event - Click event\n */\n handleEventClick(user) {\n this.hideEventTooltip();\n\n // Update custom dropdown selector to show selected user\n const dropdown = document.getElementById('spbc-timeline-user-select-dropdown');\n const valueDisplay = document.getElementById('spbc-timeline-user-select-value');\n if (dropdown && valueDisplay) {\n this.selectedUserValue = user;\n // Find the option with matching value\n const options = dropdown.querySelectorAll('.spbc-timeline-select-option');\n options.forEach((opt) => {\n opt.classList.remove('selected');\n if (opt.dataset.value === user) {\n opt.classList.add('selected');\n valueDisplay.textContent = opt.textContent;\n }\n });\n }\n\n // Redraw timeline for the selected user\n this.drawUserTimeline(user);\n\n // Update URL with users parameter\n const url = new URL(window.location);\n url.searchParams.set('users', user);\n window.history.pushState({}, '', url);\n\n // Update security log table with selected user\n updateSecurityLogTable();\n }\n\n /**\n * Get most active users from last 24 hours (max 15 users)\n * @param {Object} eventsData - User events data\n * @return {Array} Array of user objects with their events\n */\n getMostActiveUsers(eventsData) {\n const currentTime = this.currentTimestamp;\n const twentyFourHoursAgo = currentTime - (24 * 3600);\n\n // Filter users with events in last 24 hours and count their events\n const usersWithRecentEvents = [];\n\n for (let user in eventsData) {\n if (eventsData.hasOwnProperty(user)) {\n const userData = eventsData[user];\n const eventsArray = Array.isArray(userData) ? userData : (userData.events || []);\n\n // Filter events from last 24 hours\n const recentEvents = eventsArray.filter((event) => event.time >= twentyFourHoursAgo);\n\n if (recentEvents.length > 0) {\n usersWithRecentEvents.push({\n user: user,\n events: recentEvents,\n count: recentEvents.length,\n role: userData.role || 'unknown',\n });\n }\n }\n }\n\n // Sort by event count (descending) and take top 15\n usersWithRecentEvents.sort((a, b) => b.count - a.count);\n\n return usersWithRecentEvents.slice(0, 15);\n }\n\n /**\n * Group events into a 24-element array (one slot per calendar hour).\n * @param {string} user\n * @param {Array} events - array of {event, time}\n * @return {Array} 24 items: {hour, user, count, byType}\n */\n groupByHour(user, events) {\n // Group events by actual hour slots (using real timestamps, not just hour of day)\n const slots = {};\n\n events.forEach((ev) => {\n // Round down to the start of the hour\n const hourStart = Math.floor(ev.time / 3600) * 3600;\n\n if (!slots[hourStart]) {\n slots[hourStart] = {\n timestamp: hourStart + 1800, // midpoint of the hour\n user: user,\n count: 0,\n byType: {},\n };\n }\n\n slots[hourStart].count++;\n slots[hourStart].byType[ev.event] = (slots[hourStart].byType[ev.event] || 0) + 1;\n });\n\n return Object.values(slots);\n }\n\n /**\n * Draw timeline for specific user\n * @param {string} user - Selected user name\n */\n drawUserTimeline(user) {\n const svg = document.getElementById('spbc-timeline-svg');\n const noDataMessage = document.getElementById('spbc-timeline-no-data');\n\n // Clear previous content\n svg.innerHTML = '';\n noDataMessage.style.display = 'none';\n\n let additionalEventsHeight = 0;\n if (spbcTimelineWidgetData.found_types.length > 5) {\n additionalEventsHeight = (spbcTimelineWidgetData.found_types.length - 5) * 12;\n }\n\n // Calculate dimensions\n const width = svg.parentElement.clientWidth;\n const height = 150 + additionalEventsHeight + SpbcTimelineWidget.userLegendAxisOffset;\n\n // Setup drawing area\n const margin = {top: 10 + additionalEventsHeight, right: 40, bottom: 40, left: 140};\n const contentWidth = width - margin.left - margin.right;\n const contentHeight = height - margin.top - margin.bottom;\n const baseLineY = contentHeight / 2;\n\n svg.setAttribute('width', width);\n svg.setAttribute('height', height);\n\n const g = document.createElementNS('http://www.w3.org/2000/svg', 'g');\n g.setAttribute('transform', `translate(${margin.left}, ${margin.top+30})`);\n svg.appendChild(g);\n\n // Calculate base positions\n\n\n // Draw axis\n this.drawTimeAxis(g, contentWidth, baseLineY + 10);\n\n // Filter user events\n const userData = this.eventsData[user];\n const userEvents = (Array.isArray(userData) ? userData : (userData.events || [])).filter((event) =>\n event.time >= this.timelineStartTimestamp &&\n event.time <= this.currentTimestamp,\n );\n\n // Handle no events case\n if (userEvents.length === 0) {\n noDataMessage.style.display = 'block';\n return;\n }\n\n const timeRange = this.currentTimestamp - this.timelineStartTimestamp;\n const uniqueEventsCount = spbcTimelineWidgetData.found_types.length;\n\n // Draw event lines and legends\n for (let i = 0; i < uniqueEventsCount; i++) {\n const userLineY = baseLineY - (SpbcTimelineWidget.userActionsUpstep * i);\n const eventType = spbcTimelineWidgetData.found_types[i];\n const color = this.timelineMarkersData[eventType].color;\n\n // Draw event line\n const userLine = document.createElementNS('http://www.w3.org/2000/svg', 'line');\n userLine.setAttribute('class', 'spbc-timeline-user-line');\n userLine.setAttribute('x1', '0');\n userLine.setAttribute('y1', userLineY);\n userLine.setAttribute('x2', contentWidth);\n userLine.setAttribute('y2', userLineY);\n userLine.setAttribute('data-event-type', eventType);\n g.appendChild(userLine);\n\n // Draw legend circle\n const legendCircle = document.createElementNS('http://www.w3.org/2000/svg', 'circle');\n legendCircle.setAttribute('class', 'spbc-timeline-legend-circle');\n legendCircle.setAttribute('cx', SpbcTimelineWidget.legendCircleX);\n legendCircle.setAttribute('cy', userLineY);\n legendCircle.setAttribute('r', SpbcTimelineWidget.markerRadius);\n legendCircle.style.fill = color;\n g.appendChild(legendCircle);\n\n // Draw legend text\n const legendText = document.createElementNS('http://www.w3.org/2000/svg', 'text');\n legendText.setAttribute('class', 'spbc-timeline-legend-text');\n legendText.setAttribute('x', SpbcTimelineWidget.legendTextX);\n legendText.setAttribute('y', userLineY);\n legendText.setAttribute('text-anchor', 'start');\n legendText.setAttribute('dominant-baseline', 'middle');\n legendText.setAttribute('data-event-type', eventType);\n legendText.textContent = eventType;\n g.appendChild(legendText);\n }\n\n // Draw events\n userEvents.forEach((userEvent) => {\n const x = ((userEvent.time - this.timelineStartTimestamp) / timeRange) * contentWidth;\n const eventY = baseLineY + this.timelineMarkersData[userEvent.event].upstep;\n g.appendChild(this.createEventElement(userEvent, x, eventY, user));\n });\n }\n\n /**\n * Measure text width in SVG\n * @param {SVGElement} svg - SVG container element\n * @param {string} text - Text to measure\n * @param {string} className - CSS class for text styling\n * @return {number} Text width in pixels\n */\n measureTextWidth(svg, text, className = 'spbc-timeline-legend-text') {\n const tempText = document.createElementNS('http://www.w3.org/2000/svg', 'text');\n tempText.setAttribute('class', className);\n tempText.textContent = text;\n tempText.style.visibility = 'hidden';\n svg.appendChild(tempText);\n const width = tempText.getComputedTextLength();\n svg.removeChild(tempText);\n return width;\n }\n\n /**\n * Calculate how many rows the user legend will occupy\n * @param {SVGElement} svg - SVG container element\n * @param {Array} users - Array of user data objects\n * @param {number} maxWidth - Maximum width for the legend area\n * @param {number|null} maxRows - Maximum rows limit (null for unlimited)\n * @return {number} Number of rows needed\n */\n calculateUserLegendRowsCount(svg, users, maxWidth, maxRows = null) {\n const itemGap = SpbcTimelineWidget.userLegendItemGap;\n const circleRadius = SpbcTimelineWidget.markerRadius;\n const circleTextGap = 6;\n const startX = 0;\n\n // Calculate \"and N more\" text width for reservation\n const moreTextTemplate = 'and 99 more';\n const moreTextWidth = this.measureTextWidth(svg, moreTextTemplate) + itemGap;\n\n let currentX = startX;\n let currentRow = 0;\n\n for (let i = 0; i < users.length; i++) {\n const userData = users[i];\n const textWidth = this.measureTextWidth(svg, userData.user);\n const itemWidth = circleRadius * 2 + circleTextGap + textWidth + itemGap;\n\n // Check if we need to reserve space for \"and N more\" on last allowed row\n const isLastRow = maxRows !== null && currentRow === maxRows - 1;\n const reserveForMore = isLastRow && (i < users.length - 1);\n const effectiveMaxWidth = reserveForMore ? maxWidth - moreTextWidth : maxWidth;\n\n if (currentX + itemWidth > effectiveMaxWidth && currentX > startX) {\n currentRow++;\n currentX = startX;\n\n // If we hit maxRows limit, stop counting\n if (maxRows !== null && currentRow >= maxRows) {\n return maxRows;\n }\n }\n\n currentX += itemWidth;\n }\n\n return currentRow + 1; // +1 because rows are 0-indexed\n }\n\n /**\n * Draw timeline for most active users\n */\n drawUserTimelineMostActive() {\n const svg = document.getElementById('spbc-timeline-svg');\n const noDataMessage = document.getElementById('spbc-timeline-no-data');\n\n // Clear previous content\n svg.innerHTML = '';\n noDataMessage.style.display = 'none';\n\n // Get most active users\n const mostActiveUsers = this.getMostActiveUsers(this.eventsData);\n\n if (mostActiveUsers.length === 0) {\n noDataMessage.style.display = 'block';\n return;\n }\n\n let additionalEventsHeight = 0;\n if (spbcTimelineWidgetData.found_types.length > 5) {\n additionalEventsHeight = (spbcTimelineWidgetData.found_types.length - 5) * 12;\n }\n\n // Pre-assign a color to each user\n const randomColors = [\n '#0284A8', '#E74C3C', '#2ECC71', '#F39C12', '#9B59B6',\n '#1ABC9C', '#E67E22', '#3498DB', '#E91E63', '#00BCD4',\n '#8BC34A', '#FF5722', '#607D8B', '#795548', '#673AB7',\n ];\n const userColors = {};\n mostActiveUsers.forEach((userData, i) => {\n userColors[userData.user] = randomColors[(i + 1) % randomColors.length];\n });\n\n // Calculate dimensions - need to know content width first for row calculation\n const width = svg.parentElement.clientWidth;\n const margin = {top: 10 + additionalEventsHeight, right: 40, bottom: 40, left: 140};\n const contentWidth = width - margin.left - margin.right;\n\n // Calculate user legend rows count based on expanded state\n const userLegendRowHeight = SpbcTimelineWidget.userLegendRowHeight;\n const maxRows = this.userLegendExpanded ? null : SpbcTimelineWidget.userLegendMaxRows;\n const actualRowsCount = this.calculateUserLegendRowsCount(svg, mostActiveUsers, contentWidth, maxRows);\n const userLegendHeight = actualRowsCount * userLegendRowHeight;\n\n // Calculate height: base height (like drawUserTimeline) + user legend space\n const baseHeight = 150 + additionalEventsHeight + SpbcTimelineWidget.userLegendAxisOffset;\n const height = baseHeight + userLegendHeight;\n\n // Setup drawing area - baseLineY calculated from baseHeight (same as drawUserTimeline)\n const baseContentHeight = baseHeight - margin.top - margin.bottom;\n const baseLineY = baseContentHeight / 2;\n\n svg.setAttribute('width', width);\n svg.setAttribute('height', height);\n\n const g = document.createElementNS('http://www.w3.org/2000/svg', 'g');\n g.setAttribute('transform', `translate(${margin.left}, ${margin.top+30})`);\n svg.appendChild(g);\n\n // Draw axis\n this.drawTimeAxis(g, contentWidth, baseLineY + 10, true);\n\n const timeRange = this.currentTimestamp - this.timelineStartTimestamp;\n const uniqueEventsCount = spbcTimelineWidgetData.found_types.length;\n\n // Draw event lines and legends\n for (let i = 0; i < uniqueEventsCount; i++) {\n const userLineY = baseLineY - (SpbcTimelineWidget.userActionsUpstep * i);\n const eventType = spbcTimelineWidgetData.found_types[i];\n const color = this.timelineMarkersData[eventType].color;\n\n // Draw event line\n const userLine = document.createElementNS('http://www.w3.org/2000/svg', 'line');\n userLine.setAttribute('class', 'spbc-timeline-user-line');\n userLine.setAttribute('x1', '0');\n userLine.setAttribute('y1', userLineY);\n userLine.setAttribute('x2', contentWidth);\n userLine.setAttribute('y2', userLineY);\n userLine.setAttribute('data-event-type', eventType);\n g.appendChild(userLine);\n\n // Draw legend circle\n const legendCircle = document.createElementNS('http://www.w3.org/2000/svg', 'circle');\n legendCircle.setAttribute('class', 'spbc-timeline-legend-circle');\n legendCircle.setAttribute('cx', SpbcTimelineWidget.legendCircleX);\n legendCircle.setAttribute('cy', userLineY);\n legendCircle.setAttribute('r', SpbcTimelineWidget.markerRadius);\n legendCircle.style.fill = color;\n g.appendChild(legendCircle);\n\n // Draw legend text\n const legendText = document.createElementNS('http://www.w3.org/2000/svg', 'text');\n legendText.setAttribute('class', 'spbc-timeline-legend-text');\n legendText.setAttribute('x', SpbcTimelineWidget.legendTextX);\n legendText.setAttribute('y', userLineY);\n legendText.setAttribute('text-anchor', 'start');\n legendText.setAttribute('dominant-baseline', 'middle');\n legendText.setAttribute('data-event-type', eventType);\n legendText.textContent = eventType;\n g.appendChild(legendText);\n }\n\n // Draw user color legend (below axis)\n const userLegendStartY = baseLineY + SpbcTimelineWidget.userLegendAxisOffset;\n this.drawUserLegend(svg, g, mostActiveUsers, userColors, userLegendStartY, contentWidth);\n\n // Draw events for all most active users, grouped by hour\n mostActiveUsers.forEach((userData) => {\n const eventColor = userColors[userData.user];\n\n const hourly = this.groupByHour(userData.user, userData.events);\n hourly.forEach((slot) => {\n if (slot.count === 0) return;\n const x = Math.max(\n 0,\n Math.min(\n ((slot.timestamp - this.timelineStartTimestamp) / timeRange) * contentWidth, contentWidth,\n ),\n );\n Object.entries(slot.byType).forEach(([eventType, cnt]) => {\n const eventY = baseLineY + this.timelineMarkersData[eventType].upstep;\n const radius = SpbcTimelineWidget.markerRadius + Math.min(Math.sqrt(cnt - 1) * 2, 8);\n const syntheticEvent = {event: eventType, time: slot.timestamp, count: cnt};\n g.appendChild(\n this.createEventElement(\n syntheticEvent,\n x,\n eventY,\n userData.user,\n radius,\n true,\n eventColor,\n ),\n );\n });\n });\n });\n }\n\n /**\n * Draw user legend with horizontal layout and row limit\n * @param {SVGElement} svg - SVG element\n * @param {SVGElement} g - Group element to append to\n * @param {Array} users - Array of user data objects\n * @param {Object} userColors - Map of user to color\n * @param {number} startY - Starting Y position\n * @param {number} maxWidth - Maximum width for the legend area\n */\n drawUserLegend(svg, g, users, userColors, startY, maxWidth) {\n const rowHeight = SpbcTimelineWidget.userLegendRowHeight;\n const itemGap = SpbcTimelineWidget.userLegendItemGap;\n const circleRadius = SpbcTimelineWidget.markerRadius;\n const circleTextGap = 6;\n const startX = 0;\n\n // Determine max rows: null for expanded (unlimited), or limit for collapsed\n const maxRows = this.userLegendExpanded ? null : SpbcTimelineWidget.userLegendMaxRows;\n\n // Horizontal layout with optional row limit\n let currentX = startX;\n let currentRow = 0;\n let visibleCount = 0;\n let lastRowEndX = startX; // Track end position on the last allowed row for \"and N more\" button\n\n // Calculate \"and N more\" text width for reservation\n const moreTextTemplate = 'and 99 more';\n const moreTextWidth = this.measureTextWidth(svg, moreTextTemplate) + itemGap;\n\n for (let i = 0; i < users.length; i++) {\n const userData = users[i];\n const textWidth = this.measureTextWidth(svg, userData.user);\n const itemWidth = circleRadius * 2 + circleTextGap + textWidth + itemGap;\n\n // Check if we need to wrap to next row\n const isLastRow = maxRows !== null && currentRow === maxRows - 1;\n const reserveForMore = isLastRow && (i < users.length - 1);\n const effectiveMaxWidth = reserveForMore ? maxWidth - moreTextWidth : maxWidth;\n\n if (currentX + itemWidth > effectiveMaxWidth && currentX > startX) {\n // Before moving to next row, save position if we're on the last allowed row\n if (isLastRow) {\n lastRowEndX = currentX;\n }\n\n currentRow++;\n currentX = startX;\n\n // Check if we exceeded max rows (only when maxRows is set)\n if (maxRows !== null && currentRow >= maxRows) {\n // Draw \"and N more\" link at the end of the last row (continuation of the line)\n const remainingCount = users.length - visibleCount;\n if (remainingCount > 0) {\n const moreText = `and ${remainingCount} more`;\n const ly = startY + (maxRows - 1) * rowHeight;\n\n const moreLink = document.createElementNS('http://www.w3.org/2000/svg', 'text');\n moreLink.setAttribute('class', 'spbc-timeline-legend-text spbc-timeline-more-link');\n moreLink.setAttribute('x', lastRowEndX);\n moreLink.setAttribute('y', ly);\n moreLink.setAttribute('text-anchor', 'start');\n moreLink.setAttribute('dominant-baseline', 'middle');\n moreLink.textContent = moreText;\n moreLink.style.cursor = 'pointer';\n moreLink.style.fill = '#0073aa';\n moreLink.style.textDecoration = 'underline';\n moreLink.addEventListener('click', () => {\n this.userLegendExpanded = true;\n this.drawUserTimelineMostActive();\n });\n g.appendChild(moreLink);\n }\n return;\n }\n }\n\n // Track position on the last allowed row\n if (maxRows !== null && currentRow === maxRows - 1) {\n lastRowEndX = currentX + itemWidth;\n }\n\n // Draw user legend item\n const ly = startY + currentRow * rowHeight;\n const cx = currentX + circleRadius;\n const tx = currentX + circleRadius * 2 + circleTextGap;\n\n const circle = document.createElementNS('http://www.w3.org/2000/svg', 'circle');\n circle.setAttribute('cx', cx);\n circle.setAttribute('cy', ly);\n circle.setAttribute('r', circleRadius);\n circle.style.fill = userColors[userData.user];\n g.appendChild(circle);\n\n const text = document.createElementNS('http://www.w3.org/2000/svg', 'text');\n text.setAttribute('class', 'spbc-timeline-legend-text');\n text.setAttribute('x', tx);\n text.setAttribute('y', ly);\n text.setAttribute('text-anchor', 'start');\n text.setAttribute('dominant-baseline', 'middle');\n text.textContent = userData.user;\n g.appendChild(text);\n\n currentX += itemWidth;\n visibleCount++;\n }\n }\n\n /*\n /!**\n * Updates the security log table based on selected user\n * @param {string} selectedUser - The selected user or 'most_active__action'\n *!/\n updateSecurityLogTable(selectedUser) {\n const data= {};\n const urlParams = new URLSearchParams(window.location.search);\n const usersParam = urlParams.get('users');\n if (usersParam) {\n data.users = usersParam;\n }\n spbcSendAJAXRequest(data, params, obj);\n }*/\n\n /**\n * Generate marker data for events\n * @param {Array} events - List of event types\n * @return {Object} Marker configuration data\n */\n generateMarkersData(events) {\n const colorMap = {};\n let upstep = 0;\n let colorScheme = 'default';\n\n events.forEach((userEvent) => {\n if (this.timelineKnownEventsData[userEvent] && this.timelineKnownEventsData[userEvent].color_scheme) {\n colorScheme = this.timelineKnownEventsData[userEvent].color_scheme;\n }\n colorMap[userEvent] = {\n color: colorScheme,\n upstep: upstep,\n };\n upstep -= SpbcTimelineWidget.userActionsUpstep;\n });\n\n return colorMap;\n }\n}\n\n/**\n * Wrapper\n */\nfunction spbcTimelineWidgetRender() { // eslint-disable-line no-unused-vars\n const timelineWidget = new SpbcTimelineWidget();\n jQuery( function() {\n jQuery('#spbc-timeline-accordion').accordion({\n heightStyle: 'content',\n collapsible: true,\n active: false,\n });\n } );\n timelineWidget.render();\n}\n\n\n"],"names":["SpbcTimelineWidget","markerRadius","hoursRangeMax","axisStepsCount","userActionsUpstep","legendCircleX","legendTextX","userLegendMaxRows","userLegendRowHeight","userLegendItemGap","userLegendAxisOffset","constructor","this","now","Date","currentTimestamp","Math","floor","getTime","eventsData","timelineMarkersData","timelineKnownEventsData","timelineEventTooltip","timelineStartTimestamp","usersCount","summaryEventsCount","userLegendExpanded","selectedUserValue","render","spbcTimelineWidgetData","document","getElementById","users_actions","current_timestamp_local","Number","first_event_timestamp","known_events_data","generateMarkersData","found_types","window","addEventListener","event","detail","tab","initTimeline","jQuery","on","e","hasOwnProperty","localStorage","getItem","setItem","drawUserTimelineMostActive","drawUserTimeline","wrapper","trigger","dropdown","valueDisplay","key","userData","recentEvents","usersWithRecentEvents","Array","isArray","events","filter","time","length","push","user","role","eventsCount","innerHTML","createOption","text","value","option","createElement","className","dataset","textContent","mostActiveOption","activitySelector","classList","add","appendChild","forEach","displayText","stopPropagation","toggle","contains","target","remove","selectedValue","closest","querySelectorAll","opt","url","URL","location","searchParams","delete","history","pushState","set","updateSecurityLogTable","querySelector","style","display","isWidgetClosedByUser","innerText","replace","toString","setTimeout","drawTimeAxis","container","width","height","roundToHour","timeRange","axis","createElementNS","setAttribute","let","ts","ceil","x","tick","label","h","getUTCHours","padStart","i","timestamp","date","getUTCMinutes","createEventElement","y","radius","isMostActive","color","group","marker","JSON","stringify","fill","showEventTooltip","bind","hideEventTooltip","handleEventClick","eventData","parse","currentTarget","getAttribute","eventName","count","timeRow","parentElement","tooltip","month","getUTCMonth","day","getUTCDate","String","getUTCFullYear","getUTCSeconds","tooltipY","offsetY","screenWidth","innerWidth","tooltipWidth","offsetWidth","tooltipEventPositionX","clientX","tooltipX","position","top","left","data","addClass","removeClass","getMostActiveUsers","twentyFourHoursAgo","sort","a","b","slice","groupByHour","slots","hourStart","ev","byType","Object","values","svg","noDataMessage","additionalEventsHeight","clientWidth","margin","contentWidth","baseLineY","g","userEvents","uniqueEventsCount","userLineY","eventType","userLine","legendCircle","legendText","userEvent","eventY","upstep","measureTextWidth","tempText","visibility","getComputedTextLength","removeChild","calculateUserLegendRowsCount","users","maxWidth","maxRows","itemGap","circleRadius","moreTextWidth","currentX","currentRow","itemWidth","effectiveMaxWidth","mostActiveUsers","randomColors","userColors","userLegendHeight","baseHeight","userLegendStartY","drawUserLegend","eventColor","slot","max","min","entries","cnt","sqrt","syntheticEvent","startY","rowHeight","visibleCount","lastRowEndX","isLastRow","remainingCount","moreText","ly","moreLink","cursor","textDecoration","cx","tx","circle","colorMap","colorScheme","color_scheme","spbcTimelineWidgetRender","timelineWidget","accordion","heightStyle","collapsible","active"],"mappings":"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"} \ No newline at end of file diff --git a/js/src/spbc-settings.js b/js/src/spbc-settings.js index 4b3ee5a7d..accb89b0a 100644 --- a/js/src/spbc-settings.js +++ b/js/src/spbc-settings.js @@ -506,67 +506,6 @@ jQuery(document).ready(function() { } }); - //* TAB_CONTROL - // migrated to react - //* / REFRESH TAB IF ctrl+F5 END - - /** - * Change cleantalk account email - */ - jQuery(document).on('click', '#spbc-change-account-email', function(e) { - e.preventDefault(); - - let $this = jQuery(this); - let accountEmailField = jQuery('#spbc-account-email'); - let accountEmail = accountEmailField.text(); - - spbcToggleClass($this, 'active'); - - if ($this.hasClass('active')) { - $this.text($this.data('save-text')); - accountEmailField.attr('contenteditable', 'true'); - accountEmailField.focus(); - accountEmailField.on('keydown', function(e) { - if (e.code === 'Enter') { - e.preventDefault(); - } - }); - accountEmailField.on('input', function(e) { - if (e.inputType === 'insertParagraph') { - e.preventDefault(); - } - }); - } else { - spbcSendAJAXRequest( - { - action: 'spbc_update_account_email', - accountEmail: accountEmail, - }, - { - timeout: 5000, - callback: function(result, data, params, obj) { - if (result.success !== undefined && result.success === 'ok') { - if (result.manuallyLink !== undefined) { - jQuery('#spbc-key-manually-link').attr('href', result.manuallyLink); - } - } - - if (result.error !== undefined) { - jQuery('#spbc-account-email').css('border-color', 'red'); - } - }, - errorOutput: function(msg) { - spbcModal.loaded = msg; - spbcModal.open(); - }, - }, - ); - - accountEmailField.attr('contenteditable', 'false'); - $this.text($this.data('default-text')); - } - }); - // Only handle legacy banners, not React Components // @ToDo remove the outdated way to close notice after transferring all tabs to components. jQuery(document).on('click', '.spbct_notice-dismiss', function(e) { diff --git a/psalm.xml b/psalm.xml index 2d24fbdf7..5f52b30f4 100644 --- a/psalm.xml +++ b/psalm.xml @@ -24,7 +24,6 @@ - From 11689aef8c5e2ea9d85ec41693e1dcd86ed4cef3 Mon Sep 17 00:00:00 2001 From: Glomberg Date: Wed, 19 Aug 2026 15:14:40 +0300 Subject: [PATCH 5/6] Fix. Code. ESLint notice fixed. --- js/spbc-settings.min.js | 2 +- js/spbc-settings.min.js.map | 2 +- js/src/spbc-settings.js | 12 ------------ 3 files changed, 2 insertions(+), 14 deletions(-) diff --git a/js/spbc-settings.min.js b/js/spbc-settings.min.js index 218db75a5..dcc079615 100644 --- a/js/spbc-settings.min.js +++ b/js/spbc-settings.min.js @@ -1,2 +1,2 @@ -function spbcSwitchTab(e,t){var s=e.classList[1].replace("spbc_tab_nav-",""),n=new URLSearchParams(window.location.search),c=(n.set("spbc_tab",s),window.location.hash),c=window.location.pathname+"?"+n.toString()+c;if(history.replaceState(null,"",c),"backups"!==n.get("spbc_tab")&&jQuery(".spbc_tab_nav-backups").hide(),jQuery(".spbc_tab_nav").removeClass("spbc_tab_nav--active"),jQuery(".spbc_tab").removeClass("spbc_tab--active"),jQuery(e).addClass("spbc_tab_nav--active"),jQuery(".spbc_tab-"+s).addClass("spbc_tab--active"),jQuery(e).data("loaded")){if(t&&t.action)switch(t.action){case"highlight":spbcHighlightElement(t.target,t.times);break;case"click":setTimeout(function(){jQuery("#"+t.additional.target).click()},500)}}else{c={action:"spbc_settings__draw_elements",tab_name:s,security:spbcSettings.ajax_nonce};t={callback:spbcDrawSettingsCallback,notJson:!0,additional:t||null},spbcSendAJAXRequest(c,t,e)}}function spbcDrawSettingsCallback(e,t,s,n){jQuery(n).data("loaded",!0),jQuery(".spbc_tab-"+t.tab_name).replaceWith(e);n=jQuery(".spbc_tab-"+t.tab_name);if(n.addClass("spbc_tab--active"),jQuery(document).off("click",".spbc_long_recommendation__show"),jQuery(document).on("click",".spbc_long_recommendation__show",function(){spbcSettingsShowRecommendation(self=jQuery(this),self.attr("data-setting")||self.attr("setting"))}),s.additional)switch(s.additional.action){case"highlight":spbcHighlightElement(s.additional.target,s.additional.times);break;case"click":setTimeout(function(){jQuery("#"+s.additional.target).click()},500)}jQuery(n).on("click",".spbc_hint-send_"+t.tab_name,function(){jQuery(".spbc_hint-send_"+t.tab_name).hide(),spbcSendAJAXRequest({action:"spbc_send_"+t.tab_name,tab_name:t.tab_name},{callback:spbcSendLogsCallback})}),window.location.hash&&null!==(e=document.getElementById(window.location.hash.substring(1)))&&e.scrollIntoView(),"function"==typeof spbcHighlightCriticalFilesNotice&&spbcHighlightCriticalFilesNotice(!1),spbcSaveSettingsButtonMobileLayout(),window.addEventListener("resize",e=>{spbcSaveSettingsButtonMobileLayout()})}function spbcSaveSettingsButtonMobileLayout(){var e=document.querySelector('div[name="spbc_settings--save_button_custom"]');e&&(900");var s=jQuery("#spbc_long_desc__"+t);s.append("").append("
").css({top:e.position().top+25,left:e.position().left+5}),spbcSendAJAXRequest({action:"spbc_settings__get_recommendation",setting_id:t},{spinner:s.children("img"),callback:function(e,t,s,n){n.empty().append("
").append("").append("

"+e.title+"

").append("

"+e.desc+"

"),jQuery(document).on("click",c)}},s)}function spbcStartShowHide(){jQuery(".spbcShortText").off("mouseover").on("mouseover",function(){jQuery(this).next().show()}).off("mouseout").on("mouseout",function(){jQuery(this).next().hide()}),jQuery(".spbcFullText").off("mouseout").on("mouseout",function(){jQuery(this).hide()}).off("mouseover").on("mouseover",function(){jQuery(this).show()})}function spbcScannerReloadScanInfo(){jQuery(".spbc_hint__last_scan_title").text(spbcScaner.look_below_for_scan_res);spbcSendAJAXRequest({action:"spbc_scanner__last_scan_info"},{callback:e=>{document.getElementById("spbc_scanner__last_scan_info").innerText=e.data}})}function spbcBannerCheck(){let c=setInterval(function(){spbcSendAJAXRequest({action:"spbc_settings__check_renew_banner"},{callback:function(e,t,s,n){e.close_renew_banner&&(jQuery("#spbc_renew_notice").length&&jQuery("#spbc_renew_notice").hide("slow"),jQuery("#spbc_trial_notice").length&&jQuery("#spbc_trial_notice").hide("slow"),clearInterval(c))}})},6e4)}function spbcToggleClass(t,s){for(let e=0;e",jQuery(c).insertAfter(jQuery(a)),jQuery("#spbc_settings_templates_import_button .spbc_success").show(300),setTimeout(function(){jQuery("#spbc_settings_templates_import_button .spbc_success").hide(300)},2e3),document.addEventListener("spbcModalClosed",function(e){document.location.reload()}),setTimeout(function(){spbcModal.close()},2e3)):(c="

"+e.data+"

",jQuery(c).insertAfter(jQuery(a)))}})}}),jQuery(document).on("click","#spbc_settings_templates_export_button",function(){jQuery("#spbc-ajax-result").remove();var t=jQuery("option:selected",jQuery("#spbc_settings_templates_export")),s=jQuery("#spbc_settings_templates_export_name");if(s.css("border-color","inherit"),void 0===t.data("id"))console.log('Attribute "data-id" not set for the option.');else{let e;if("new_template"===t.data("id")){var n=s.val();if(""===n)return void s.css("border-color","red");e={template_name:n}}else e={template_id:t.data("id")};let a=this;spbcSendAJAXRequest({action:"spbc_settings_templates_export",data:e},{timeout:25e3,button:a,spinner:jQuery("#spbc_settings_templates_export_button .spbc_preloader_button"),callback:function(e,t,s,n){let c;e.success?(c="

"+e.data+"

",jQuery(c).insertAfter(jQuery(a)),jQuery("#spbc_settings_templates_export_button .spbc_success").show(300),setTimeout(function(){jQuery("#spbc_settings_templates_export_button .spbc_success").hide(300)},2e3),document.addEventListener("spbcModalClosed",function(e){document.location.reload()}),setTimeout(function(){spbcModal.close()},2e3)):(c="

"+e.data+"

",jQuery(c).insertAfter(jQuery(a)))}})}}),jQuery(document).on("click","#spbc_settings_templates_reset_button",function(){let a=this;spbcSendAJAXRequest({action:"spbc_settings_templates_reset"},{timeout:25e3,button:a,spinner:jQuery("#spbc_settings_templates_reset_button .spbc_preloader_button"),callback:function(e,t,s,n){let c;e.success?(c="

"+e.data+"

",jQuery(c).insertAfter(jQuery(a)),jQuery("#spbc_settings_templates_reset_button .spbc_success").show(300),setTimeout(function(){jQuery("#spbc_settings_templates_reset_button .spbc_success").hide(300)},2e3),document.addEventListener("spbcModalClosed",function(e){document.location.reload()}),setTimeout(function(){spbcModal.close()},2e3)):(c="

"+e.data+"

",jQuery(c).insertAfter(jQuery(a)))}})}),jQuery(document).on("change","#spbc_settings_templates_export",function(){"new_template"===jQuery("option:selected",this).data("id")?jQuery(this).parent().parent().find("#spbc_settings_templates_export_name").show():jQuery(this).parent().parent().find("#spbc_settings_templates_export_name").hide()}),jQuery(document).on("click",".spbct_notice-dismiss",function(e){var t=this.parentElement.classList;let s=t[t.length-1];document.querySelectorAll("."+s).forEach(e=>{e.parentElement.setAttribute("style","display:none;"),spbcSetCookie(s,1,2592e3)})}),jQuery(document).off("click",".spbc_long_description__show"),jQuery(document).on("click",".spbc_long_description__show",function(){self=jQuery(this),spbcHandleLongDescription(self,self.attr("data-setting")||self.attr("setting"))}),jQuery(document).off("click",".spbc_long_recommendation__show"),jQuery(document).on("click",".spbc_long_recommendation__show",function(){spbcSettingsShowRecommendation(self=jQuery(this),self.attr("data-setting")||self.attr("setting"))}),document.referrer&&-1!==document.referrer.indexOf("page=spbc")||sessionStorage.removeItem("spbc_trial_renew_fullpage_dismissed"),sessionStorage.getItem("spbc_trial_renew_fullpage_dismissed")&&(jQuery(".spbc-trial-renew-fullpage").hide(),jQuery("#spbct-page--react").show()),jQuery("body").on("click",".spbc-trial-renew-fullpage .notice-dismiss",function(e){e.preventDefault(),sessionStorage.setItem("spbc_trial_renew_fullpage_dismissed","1"),jQuery(".spbc-trial-renew-fullpage").hide(),jQuery("#spbct-page--react").show()}),jQuery(document).off("click","#spbc_debug__run_pass_check_btn").on("click","#spbc_debug__run_pass_check_btn",function(){let t=jQuery(this),s=jQuery("#spbc_debug__pass_check_status");t.prop("disabled",!0),s.css("color","").text("Resetting records and running check, please wait..."),jQuery.ajax({type:"POST",url:ajaxurl,data:{action:"spbc_debug_users_pass_check_run",security:spbcSettings.ajax_nonce},success:function(e){t.prop("disabled",!1),e.success?s.css("color","green").html("✓ Done! Total: "+e.data.total+", Checked: "+e.data.checked+", Leaked: "+e.data.leaked+" — You can now test the login form in incognito."):s.css("color","red").text("Error: "+(e.data||"unknown error"))},error:function(e){t.prop("disabled",!1),s.css("color","red").text("Request failed: "+e.status+" "+e.statusText)}})})}); +function spbcSwitchTab(e,t){var s=e.classList[1].replace("spbc_tab_nav-",""),n=new URLSearchParams(window.location.search),c=(n.set("spbc_tab",s),window.location.hash),c=window.location.pathname+"?"+n.toString()+c;if(history.replaceState(null,"",c),"backups"!==n.get("spbc_tab")&&jQuery(".spbc_tab_nav-backups").hide(),jQuery(".spbc_tab_nav").removeClass("spbc_tab_nav--active"),jQuery(".spbc_tab").removeClass("spbc_tab--active"),jQuery(e).addClass("spbc_tab_nav--active"),jQuery(".spbc_tab-"+s).addClass("spbc_tab--active"),jQuery(e).data("loaded")){if(t&&t.action)switch(t.action){case"highlight":spbcHighlightElement(t.target,t.times);break;case"click":setTimeout(function(){jQuery("#"+t.additional.target).click()},500)}}else{c={action:"spbc_settings__draw_elements",tab_name:s,security:spbcSettings.ajax_nonce};t={callback:spbcDrawSettingsCallback,notJson:!0,additional:t||null},spbcSendAJAXRequest(c,t,e)}}function spbcDrawSettingsCallback(e,t,s,n){jQuery(n).data("loaded",!0),jQuery(".spbc_tab-"+t.tab_name).replaceWith(e);n=jQuery(".spbc_tab-"+t.tab_name);if(n.addClass("spbc_tab--active"),jQuery(document).off("click",".spbc_long_recommendation__show"),jQuery(document).on("click",".spbc_long_recommendation__show",function(){spbcSettingsShowRecommendation(self=jQuery(this),self.attr("data-setting")||self.attr("setting"))}),s.additional)switch(s.additional.action){case"highlight":spbcHighlightElement(s.additional.target,s.additional.times);break;case"click":setTimeout(function(){jQuery("#"+s.additional.target).click()},500)}jQuery(n).on("click",".spbc_hint-send_"+t.tab_name,function(){jQuery(".spbc_hint-send_"+t.tab_name).hide(),spbcSendAJAXRequest({action:"spbc_send_"+t.tab_name,tab_name:t.tab_name},{callback:spbcSendLogsCallback})}),window.location.hash&&null!==(e=document.getElementById(window.location.hash.substring(1)))&&e.scrollIntoView(),"function"==typeof spbcHighlightCriticalFilesNotice&&spbcHighlightCriticalFilesNotice(!1),spbcSaveSettingsButtonMobileLayout(),window.addEventListener("resize",e=>{spbcSaveSettingsButtonMobileLayout()})}function spbcSaveSettingsButtonMobileLayout(){var e=document.querySelector('div[name="spbc_settings--save_button_custom"]');e&&(900");var s=jQuery("#spbc_long_desc__"+t);s.append("").append("
").css({top:e.position().top+25,left:e.position().left+5}),spbcSendAJAXRequest({action:"spbc_settings__get_recommendation",setting_id:t},{spinner:s.children("img"),callback:function(e,t,s,n){n.empty().append("
").append("").append("

"+e.title+"

").append("

"+e.desc+"

"),jQuery(document).on("click",c)}},s)}function spbcStartShowHide(){jQuery(".spbcShortText").off("mouseover").on("mouseover",function(){jQuery(this).next().show()}).off("mouseout").on("mouseout",function(){jQuery(this).next().hide()}),jQuery(".spbcFullText").off("mouseout").on("mouseout",function(){jQuery(this).hide()}).off("mouseover").on("mouseover",function(){jQuery(this).show()})}function spbcScannerReloadScanInfo(){jQuery(".spbc_hint__last_scan_title").text(spbcScaner.look_below_for_scan_res);spbcSendAJAXRequest({action:"spbc_scanner__last_scan_info"},{callback:e=>{document.getElementById("spbc_scanner__last_scan_info").innerText=e.data}})}function spbcBannerCheck(){let c=setInterval(function(){spbcSendAJAXRequest({action:"spbc_settings__check_renew_banner"},{callback:function(e,t,s,n){e.close_renew_banner&&(jQuery("#spbc_renew_notice").length&&jQuery("#spbc_renew_notice").hide("slow"),jQuery("#spbc_trial_notice").length&&jQuery("#spbc_trial_notice").hide("slow"),clearInterval(c))}})},6e4)}function spbcCreateSupportUser(e){e.preventDefault();e=spbcSettings.support_user_creation_msg_array;spbcModal.open(),spbcModal.confirm(e.confirm_header,e.confirm_text,"",spbcCreateUserCallback)}function spbcCreateUserCallback(){spbcSendAJAXRequest({action:"spbc_create_support_user"},{timeout:5e3,callback:function(s,e,t,n){var c=spbcSettings.support_user_creation_msg_array;let a=c.default_error;if("object"==typeof s&&s.hasOwnProperty("success")&&s.hasOwnProperty("user_created")&&s.hasOwnProperty("mail_sent")&&s.hasOwnProperty("cron_updated")&&s.hasOwnProperty("user_data")&&s.hasOwnProperty("result_code")&&"object"==typeof s.user_data&&s.user_data.hasOwnProperty("username")&&s.user_data.hasOwnProperty("email")&&s.user_data.hasOwnProperty("password")&&s.success){if(s.user_created){let e="",t="";var o=c.cron_updated;return e=s.mail_sent?c.mail_sent_success:c.mail_sent_error,t=0===s.result_code?c.user_updated:c.user_created,jQuery("#spbc_stats_user_creation_username").text(s.user_data.username),jQuery("#spbc_stats_user_creation_email").text(s.user_data.email),jQuery("#spbc_stats_user_creation_password").text(s.user_data.password),jQuery("#spbc_stats_user_creation_mail_sent").text(e),jQuery("#spbc_stats_user_creation_title").text(t),jQuery("#spbc_stats_user_creation_cron_updated").text(o),void jQuery(".spbc_stats_user_creation_result").css("display","block")}-2===s.result_code?a=c.invalid_permission:-1===s.result_code?a=c.unknown_creation_error:-4===s.result_code?a=c.on_cooldown:-5===s.result_code&&(a=c.email_is_busy)}spbcModal.loaded=a,spbcModal.open()},errorOutput:function(e){spbcModal.loaded=e,spbcModal.open()}})}String.prototype.printf=function(){let e=this;for(var t in arguments){var s=e.substring(0,e.indexOf("%s",0)),n=e.substring(e.indexOf("%s",0)+2,e.length);e=s+arguments[t]+n}return e},jQuery(document).ready(function(){(jQuery("#spbc_renew_notice").length||jQuery("#spbc_trial_notice").length)&&spbcBannerCheck(),spbcSettings.key_changed&&jQuery("#spbc_button__sync").click(),jQuery(document).on("click","#spbc_settings_templates_import_button",function(){jQuery("#spbc-ajax-result").remove();var e=jQuery("option:selected",jQuery("#spbc_settings_templates_import"));if(jQuery("#spbc_settings_templates_import_name").css("border-color","inherit"),void 0===e.data("id"))console.log('Attribute "data-id" not set for the option.');else{e={template_id:e.data("id"),template_name:e.data("name"),settings:e.data("settings")};let a=this;spbcSendAJAXRequest({action:"spbc_settings_templates_import",data:e},{timeout:25e3,button:a,spinner:jQuery("#spbc_settings_templates_import_button .spbc_preloader_button"),callback:function(e,t,s,n){let c;e.success?(c="

"+e.data+"

",jQuery(c).insertAfter(jQuery(a)),jQuery("#spbc_settings_templates_import_button .spbc_success").show(300),setTimeout(function(){jQuery("#spbc_settings_templates_import_button .spbc_success").hide(300)},2e3),document.addEventListener("spbcModalClosed",function(e){document.location.reload()}),setTimeout(function(){spbcModal.close()},2e3)):(c="

"+e.data+"

",jQuery(c).insertAfter(jQuery(a)))}})}}),jQuery(document).on("click","#spbc_settings_templates_export_button",function(){jQuery("#spbc-ajax-result").remove();var t=jQuery("option:selected",jQuery("#spbc_settings_templates_export")),s=jQuery("#spbc_settings_templates_export_name");if(s.css("border-color","inherit"),void 0===t.data("id"))console.log('Attribute "data-id" not set for the option.');else{let e;if("new_template"===t.data("id")){var n=s.val();if(""===n)return void s.css("border-color","red");e={template_name:n}}else e={template_id:t.data("id")};let a=this;spbcSendAJAXRequest({action:"spbc_settings_templates_export",data:e},{timeout:25e3,button:a,spinner:jQuery("#spbc_settings_templates_export_button .spbc_preloader_button"),callback:function(e,t,s,n){let c;e.success?(c="

"+e.data+"

",jQuery(c).insertAfter(jQuery(a)),jQuery("#spbc_settings_templates_export_button .spbc_success").show(300),setTimeout(function(){jQuery("#spbc_settings_templates_export_button .spbc_success").hide(300)},2e3),document.addEventListener("spbcModalClosed",function(e){document.location.reload()}),setTimeout(function(){spbcModal.close()},2e3)):(c="

"+e.data+"

",jQuery(c).insertAfter(jQuery(a)))}})}}),jQuery(document).on("click","#spbc_settings_templates_reset_button",function(){let a=this;spbcSendAJAXRequest({action:"spbc_settings_templates_reset"},{timeout:25e3,button:a,spinner:jQuery("#spbc_settings_templates_reset_button .spbc_preloader_button"),callback:function(e,t,s,n){let c;e.success?(c="

"+e.data+"

",jQuery(c).insertAfter(jQuery(a)),jQuery("#spbc_settings_templates_reset_button .spbc_success").show(300),setTimeout(function(){jQuery("#spbc_settings_templates_reset_button .spbc_success").hide(300)},2e3),document.addEventListener("spbcModalClosed",function(e){document.location.reload()}),setTimeout(function(){spbcModal.close()},2e3)):(c="

"+e.data+"

",jQuery(c).insertAfter(jQuery(a)))}})}),jQuery(document).on("change","#spbc_settings_templates_export",function(){"new_template"===jQuery("option:selected",this).data("id")?jQuery(this).parent().parent().find("#spbc_settings_templates_export_name").show():jQuery(this).parent().parent().find("#spbc_settings_templates_export_name").hide()}),jQuery(document).on("click",".spbct_notice-dismiss",function(e){var t=this.parentElement.classList;let s=t[t.length-1];document.querySelectorAll("."+s).forEach(e=>{e.parentElement.setAttribute("style","display:none;"),spbcSetCookie(s,1,2592e3)})}),jQuery(document).off("click",".spbc_long_description__show"),jQuery(document).on("click",".spbc_long_description__show",function(){self=jQuery(this),spbcHandleLongDescription(self,self.attr("data-setting")||self.attr("setting"))}),jQuery(document).off("click",".spbc_long_recommendation__show"),jQuery(document).on("click",".spbc_long_recommendation__show",function(){spbcSettingsShowRecommendation(self=jQuery(this),self.attr("data-setting")||self.attr("setting"))}),document.referrer&&-1!==document.referrer.indexOf("page=spbc")||sessionStorage.removeItem("spbc_trial_renew_fullpage_dismissed"),sessionStorage.getItem("spbc_trial_renew_fullpage_dismissed")&&(jQuery(".spbc-trial-renew-fullpage").hide(),jQuery("#spbct-page--react").show()),jQuery("body").on("click",".spbc-trial-renew-fullpage .notice-dismiss",function(e){e.preventDefault(),sessionStorage.setItem("spbc_trial_renew_fullpage_dismissed","1"),jQuery(".spbc-trial-renew-fullpage").hide(),jQuery("#spbct-page--react").show()}),jQuery(document).off("click","#spbc_debug__run_pass_check_btn").on("click","#spbc_debug__run_pass_check_btn",function(){let t=jQuery(this),s=jQuery("#spbc_debug__pass_check_status");t.prop("disabled",!0),s.css("color","").text("Resetting records and running check, please wait..."),jQuery.ajax({type:"POST",url:ajaxurl,data:{action:"spbc_debug_users_pass_check_run",security:spbcSettings.ajax_nonce},success:function(e){t.prop("disabled",!1),e.success?s.css("color","green").html("✓ Done! Total: "+e.data.total+", Checked: "+e.data.checked+", Leaked: "+e.data.leaked+" — You can now test the login form in incognito."):s.css("color","red").text("Error: "+(e.data||"unknown error"))},error:function(e){t.prop("disabled",!1),s.css("color","red").text("Request failed: "+e.status+" "+e.statusText)}})})}); //# sourceMappingURL=spbc-settings.min.js.map diff --git a/js/spbc-settings.min.js.map b/js/spbc-settings.min.js.map index 018894135..1c0a4d027 100644 --- a/js/spbc-settings.min.js.map +++ b/js/spbc-settings.min.js.map @@ -1 +1 @@ -{"version":3,"file":"spbc-settings.min.js","sources":["spbc-settings.js"],"sourcesContent":["// Printf for JS\nString.prototype.printf = function() { // eslint-disable-line no-extend-native\n let formatted = this;\n /* eslint-disable guard-for-in */\n for ( let arg in arguments ) { // eslint-disable-line prefer-rest-params\n let beforeFormatted = formatted.substring(0, formatted.indexOf('%s', 0));\n let afterFormatted = formatted.substring(formatted.indexOf('%s', 0)+2, formatted.length);\n formatted = beforeFormatted + arguments[arg] + afterFormatted; // eslint-disable-line prefer-rest-params\n }\n return formatted;\n};\n\n/**\n * Switching tabs\n *\n * @param {object} tab\n * @param {object} params\n */\nfunction spbcSwitchTab(tab, params) {\n let tabName = tab.classList[1].replace('spbc_tab_nav-', '');\n\n // update url\n let searchParams = new URLSearchParams(window.location.search);\n searchParams.set('spbc_tab', tabName);\n let hashParam = window.location.hash;\n let newRelativePathQuery = window.location.pathname + '?' + searchParams.toString() + hashParam;\n\n history.replaceState(null, '', newRelativePathQuery);\n\n // Hiding a tab 'Backups' if exist\n if (searchParams.get('spbc_tab') !== 'backups') {\n jQuery('.spbc_tab_nav-backups').hide();\n }\n\n // update active tab\n jQuery('.spbc_tab_nav').removeClass('spbc_tab_nav--active');\n jQuery('.spbc_tab').removeClass('spbc_tab--active');\n jQuery(tab).addClass('spbc_tab_nav--active');\n jQuery('.spbc_tab-' + tabName).addClass('spbc_tab--active');\n\n if (!jQuery(tab).data('loaded')) {\n let data = {\n action: 'spbc_settings__draw_elements',\n tab_name: tabName,\n security: spbcSettings.ajax_nonce,\n };\n params = {\n callback: spbcDrawSettingsCallback,\n notJson: true,\n additional: params || null,\n };\n spbcSendAJAXRequest( data, params, tab );\n } else if (params && params.action) {\n switch (params.action) {\n case 'highlight':\n spbcHighlightElement(params.target, params.times);\n break;\n case 'click':\n setTimeout(function() {\n jQuery('#'+params.additional.target).click();\n }, 500);\n break;\n }\n }\n}\n\n/**\n * Draws settings\n *\n * @param {*} result\n * @param {*} data\n * @param {*} params\n * @param {*} obj\n */\nfunction spbcDrawSettingsCallback(result, data, params, obj) {\n jQuery(obj).data('loaded', true);\n jQuery('.spbc_tab-'+data.tab_name).replaceWith(result);\n let tab = jQuery('.spbc_tab-'+data.tab_name);\n tab.addClass('spbc_tab--active');\n\n jQuery(document).off( 'click', '.spbc_long_recommendation__show');\n jQuery(document).on('click', '.spbc_long_recommendation__show', function() {\n self = jQuery(this);\n spbcSettingsShowRecommendation(self, self.attr('data-setting') || self.attr('setting'));\n });\n\n if (params.additional) {\n switch (params.additional.action) {\n case 'highlight':\n spbcHighlightElement(params.additional.target, params.additional.times);\n break;\n case 'click':\n setTimeout(function() {\n jQuery('#'+params.additional.target).click();\n }, 500);\n break;\n }\n }\n\n jQuery(tab).on('click', '.spbc_hint-send_'+data.tab_name, function() {\n jQuery('.spbc_hint-send_'+data.tab_name).hide();\n spbcSendAJAXRequest(\n {action: 'spbc_send_'+data.tab_name, tab_name: data.tab_name},\n {callback: spbcSendLogsCallback},\n );\n });\n\n // scroll to anchor\n if (window.location.hash) {\n let scrollToTag = document.getElementById(window.location.hash.substring(1));\n if (scrollToTag !== null) {\n scrollToTag.scrollIntoView();\n }\n }\n\n if (typeof spbcHighlightCriticalFilesNotice === 'function') {\n spbcHighlightCriticalFilesNotice(false);\n }\n\n spbcSaveSettingsButtonMobileLayout();\n window.addEventListener('resize', (event) => {\n spbcSaveSettingsButtonMobileLayout();\n });\n}\n\n/**\n * A chunk to hide the custom element \"submit button\" on large screen or make sticky on mobile\n */\nfunction spbcSaveSettingsButtonMobileLayout() {\n const selector = document.querySelector('div[name=\"spbc_settings--save_button_custom\"]');\n if (selector) {\n if (window.innerWidth > 900) {\n selector.style.display = 'none';\n } else {\n selector.style.display = 'flex';\n }\n }\n}\n\n/**\n * Sends logs\n *\n * @param {*} result\n * @param {*} data\n * @param {*} params\n * @param {*} obj\n */\nfunction spbcSendLogsCallback(result, data, params, obj) {\n jQuery('.spbc_tab_nav-'+data.tab_name).data('loaded', false);\n spbcSwitchTab(document.getElementsByClassName('spbc_tab_nav-'+data.tab_name)[0]);\n}\n\n/**\n * Disables/enables settings by ID\n *\n * @param {array} settingsIDs\n * @param {number} enable\n */\nfunction spbcSettingsDependenciesbyId(settingsIDs, enable) { // eslint-disable-line no-unused-vars\n if (typeof settingsIDs === 'string') {\n tmp = [];\n tmp.push(settingsIDs);\n settingsIDs = tmp;\n }\n\n enable = typeof enable === 'undefined' ? null : +enable;\n\n settingsIDs.forEach(function(settingID, i, arr) {\n let elem = document.getElementById('spbc_setting_'+settingID);\n let doDisable = function() {\n elem.setAttribute('disabled', 'disabled');\n };\n let doEnable = function() {\n elem.removeAttribute('disabled');\n };\n\n if (enable !== null) {\n // Set\n enable === 1 ? doEnable() : doDisable();\n } else {\n // Switch\n elem.getAttribute('disabled') === null ? doDisable() : doEnable();\n }\n });\n}\n\n/**\n * Recursive in case children found!\n *\n * Settings dependencies\n *\n * @param {array} settingNames\n * @param {number} enable\n */\nfunction spbcSettingsDependencies(settingNames, enable) { // eslint-disable-line no-unused-vars\n // Cast settingNames to array\n const eventTarget = window.event && window.event.target ? window.event.target : null;\n const isRadio = eventTarget && eventTarget.type === 'radio';\n const radioEnabled = isRadio && eventTarget.value == '1';\n const targetEnableFlag = eventTarget ?\n ((isRadio && !radioEnabled) || +eventTarget.checked) :\n null;\n const doEnableFlag = typeof enable === 'undefined' ? targetEnableFlag : +enable;\n\n settingNames = typeof settingNames === 'string' ? settingNames.split(',') : settingNames;\n\n settingNames.forEach(function(settingName, i, arr) {\n let elements = document.getElementsByName('spbc_settings['+settingName+']');\n if (elements.length === 0) {\n // multiple selector case\n elements = document.getElementsByName('spbc_settings['+settingName+'][]');\n }\n\n elements\n .forEach(function( elem, i, arr ) {\n let doDisable = function() {\n elem.setAttribute('disabled', 'disabled');\n };\n let doEnable = function() {\n elem.removeAttribute('disabled');\n };\n if (doEnableFlag !== null) {\n // Set\n doEnableFlag === 1 ? doEnable() : doDisable();\n } else {\n // Switch\n elem.getAttribute('disabled') === null ? doDisable() : doEnable();\n }\n\n let children = elem.getAttribute('data-children');\n if ( children !== null ) {\n spbcSettingsDependencies(children, doEnableFlag && elem.checked);\n }\n });\n });\n}\n\n/**\n * Shows recommendation\n *\n * @param {object} label\n * @param {string} settingId\n */\nfunction spbcSettingsShowRecommendation(label, settingId) {\n let removeRecommendationFunc = function(e) {\n if (\n typeof e === 'undefined' ||\n (\n jQuery(e.target).parent('.spbc_long_desc').length == 0 ||\n jQuery(e.target).hasClass('spbc_long_desc__cancel')\n ) && !jQuery(e.target).hasClass('spbc_long_recommendation__show')\n ) {\n jQuery('.spbc_long_desc').remove();\n jQuery(document).off('click', removeRecommendationFunc);\n }\n };\n\n removeRecommendationFunc();\n\n label.after('
');\n let obj = jQuery('#spbc_long_desc__'+settingId);\n obj.append('')\n .append('
')\n .css({\n top: label.position().top + 25,\n left: label.position().left + 5,\n });\n\n\n spbcSendAJAXRequest(\n {action: 'spbc_settings__get_recommendation', setting_id: settingId},\n {\n spinner: obj.children('img'),\n callback: function(result, data, params, obj) {\n obj.empty()\n .append('
')\n .append('')\n .append('

'+result.title+'

')\n .append('

'+result.desc+'

');\n\n jQuery(document).on('click', removeRecommendationFunc);\n },\n },\n obj,\n );\n}\n\n/**\n * Shows/hides full text\n */\nfunction spbcStartShowHide() { // eslint-disable-line no-unused-vars\n jQuery('.spbcShortText')\n .off('mouseover' )\n .on('mouseover', function() {\n jQuery(this).next().show();\n })\n .off('mouseout' )\n .on('mouseout', function() {\n jQuery(this).next().hide();\n });\n jQuery('.spbcFullText')\n .off('mouseout' )\n .on('mouseout', function() {\n jQuery(this).hide();\n })\n .off('mouseover' )\n .on('mouseover', function() {\n jQuery(this).show();\n });\n}\n\n/**\n * Reloads last scan info\n */\nfunction spbcScannerReloadScanInfo() { // eslint-disable-line no-unused-vars\n jQuery('.spbc_hint__last_scan_title').text(spbcScaner.look_below_for_scan_res);\n const data = {\n action: 'spbc_scanner__last_scan_info',\n };\n const params = {\n callback: (result) => {\n document.getElementById('spbc_scanner__last_scan_info').innerText = result.data;\n },\n };\n spbcSendAJAXRequest(data, params);\n}\n\n/**\n * Checking current account status for renew notice\n */\nfunction spbcBannerCheck() {\n let bannerChecker = setInterval( function() {\n spbcSendAJAXRequest(\n {action: 'spbc_settings__check_renew_banner'},\n {\n callback: function(result, data, params, obj) {\n if (result.close_renew_banner) {\n if (jQuery('#spbc_renew_notice').length) {\n jQuery('#spbc_renew_notice').hide('slow');\n }\n if (jQuery('#spbc_trial_notice').length) {\n jQuery('#spbc_trial_notice').hide('slow');\n }\n clearInterval(bannerChecker);\n }\n },\n },\n );\n }, 60000);\n}\n\njQuery(document).ready(function() {\n if (jQuery('#spbc_renew_notice').length || jQuery('#spbc_trial_notice').length) {\n spbcBannerCheck();\n }\n\n // Sync button migrated to react\n\n if ( spbcSettings.key_changed ) {\n jQuery('#spbc_button__sync').click();\n }\n\n // Get Key Auto button migrated to Signup Wizard\n\n // Import settings\n jQuery( document ).on('click', '#spbc_settings_templates_import_button', function() {\n jQuery('#spbc-ajax-result').remove();\n let optionSelected = jQuery('option:selected', jQuery('#spbc_settings_templates_import'));\n let templateNameInput = jQuery('#spbc_settings_templates_import_name');\n templateNameInput.css('border-color', 'inherit');\n if ( typeof optionSelected.data('id') === 'undefined' ) {\n console.log( 'Attribute \"data-id\" not set for the option.' );\n return;\n }\n let data = {\n 'template_id': optionSelected.data('id'),\n 'template_name': optionSelected.data('name'),\n 'settings': optionSelected.data('settings'),\n };\n let button = this;\n spbcSendAJAXRequest(\n {action: 'spbc_settings_templates_import', data: data},\n {\n timeout: 25000,\n button: button,\n spinner: jQuery('#spbc_settings_templates_import_button .spbc_preloader_button' ),\n callback: function(result, data, params, obj) {\n let selector;\n if (result.success) {\n selector = '

' + result.data + '

';\n jQuery( selector ).insertAfter( jQuery(button) );\n jQuery('#spbc_settings_templates_import_button .spbc_success').show(300);\n setTimeout(function() {\n jQuery('#spbc_settings_templates_import_button .spbc_success').hide(300);\n }, 2000);\n document.addEventListener('spbcModalClosed', function( e ) {\n document.location.reload();\n });\n setTimeout(function() {\n spbcModal.close();\n }, 2000);\n } else {\n selector = '

' + result.data + '

';\n jQuery( selector ).insertAfter( jQuery(button) );\n }\n },\n },\n );\n });\n\n // Export settings\n jQuery( document ).on('click', '#spbc_settings_templates_export_button', function() {\n jQuery('#spbc-ajax-result').remove();\n let optionSelected = jQuery('option:selected', jQuery('#spbc_settings_templates_export'));\n let templateNameInput = jQuery('#spbc_settings_templates_export_name');\n templateNameInput.css('border-color', 'inherit');\n if ( typeof optionSelected.data('id') === 'undefined' ) {\n console.log( 'Attribute \"data-id\" not set for the option.' );\n return;\n }\n let data;\n if ( optionSelected.data('id') === 'new_template' ) {\n let templateName = templateNameInput.val();\n if ( templateName === '' ) {\n templateNameInput.css('border-color', 'red');\n return;\n }\n data = {\n 'template_name': templateName,\n };\n } else {\n data = {\n 'template_id': optionSelected.data('id'),\n };\n }\n let button = this;\n spbcSendAJAXRequest(\n {action: 'spbc_settings_templates_export', data: data},\n {\n timeout: 25000,\n button: button,\n spinner: jQuery('#spbc_settings_templates_export_button .spbc_preloader_button' ),\n callback: function(result, data, params, obj) {\n let selector;\n if (result.success) {\n selector = '

' + result.data + '

';\n jQuery( selector ).insertAfter( jQuery(button) );\n jQuery('#spbc_settings_templates_export_button .spbc_success').show(300);\n setTimeout(function() {\n jQuery('#spbc_settings_templates_export_button .spbc_success').hide(300);\n }, 2000);\n document.addEventListener('spbcModalClosed', function( e ) {\n document.location.reload();\n });\n setTimeout(function() {\n spbcModal.close();\n }, 2000);\n } else {\n selector = '

' + result.data + '

';\n jQuery( selector ).insertAfter( jQuery(button) );\n }\n },\n },\n );\n });\n\n // Reset settings\n jQuery( document ).on('click', '#spbc_settings_templates_reset_button', function() {\n let button = this;\n spbcSendAJAXRequest(\n {action: 'spbc_settings_templates_reset'},\n {\n timeout: 25000,\n button: button,\n spinner: jQuery('#spbc_settings_templates_reset_button .spbc_preloader_button' ),\n callback: function(result, data, params, obj) {\n let selector;\n if (result.success) {\n selector = '

' + result.data + '

';\n jQuery( selector ).insertAfter( jQuery(button) );\n jQuery('#spbc_settings_templates_reset_button .spbc_success').show(300);\n setTimeout(function() {\n jQuery('#spbc_settings_templates_reset_button .spbc_success').hide(300);\n }, 2000);\n document.addEventListener('spbcModalClosed', function( e ) {\n document.location.reload();\n });\n setTimeout(function() {\n spbcModal.close();\n }, 2000);\n } else {\n selector = '

' + result.data + '

';\n jQuery( selector ).insertAfter( jQuery(button) );\n }\n },\n },\n );\n });\n\n jQuery(document).on('change', '#spbc_settings_templates_export', function() {\n let optionSelected = jQuery('option:selected', this);\n if ( optionSelected.data('id') === 'new_template' ) {\n jQuery(this).parent().parent().find('#spbc_settings_templates_export_name').show();\n } else {\n jQuery(this).parent().parent().find('#spbc_settings_templates_export_name').hide();\n }\n });\n\n // Only handle legacy banners, not React Components\n // @ToDo remove the outdated way to close notice after transferring all tabs to components.\n jQuery(document).on('click', '.spbct_notice-dismiss', function(e) {\n let classListNotice = this.parentElement.classList;\n let classNoticeForClose = classListNotice[classListNotice.length - 1];\n\n document.querySelectorAll('.' + classNoticeForClose).forEach((element) => {\n element.parentElement.setAttribute('style', 'display:none;');\n spbcSetCookie(classNoticeForClose, 1, 86400 * 30);\n });\n });\n\n // @ToDo need to be refactored - this is the code dublication\n jQuery(document).off( 'click', '.spbc_long_description__show');\n jQuery(document).on('click', '.spbc_long_description__show', function() {\n self = jQuery(this);\n spbcHandleLongDescription(self, self.attr('data-setting') || self.attr('setting'));\n });\n\n // @ToDo need to be refactored - this is the code dublication\n jQuery(document).off( 'click', '.spbc_long_recommendation__show');\n jQuery(document).on('click', '.spbc_long_recommendation__show', function() {\n self = jQuery(this);\n spbcSettingsShowRecommendation(self, self.attr('data-setting') || self.attr('setting'));\n });\n\n // Reset dismiss flag if user came from outside plugin settings\n if (!document.referrer || document.referrer.indexOf('page=spbc') === -1) {\n sessionStorage.removeItem('spbc_trial_renew_fullpage_dismissed');\n }\n\n // Hide fullpage trial banner if it was already dismissed in this browser session\n if (sessionStorage.getItem('spbc_trial_renew_fullpage_dismissed')) {\n jQuery('.spbc-trial-renew-fullpage').hide();\n jQuery('#spbct-page--react').show();\n }\n\n jQuery('body').on('click', '.spbc-trial-renew-fullpage .notice-dismiss', function(e) {\n e.preventDefault();\n sessionStorage.setItem('spbc_trial_renew_fullpage_dismissed', '1');\n jQuery('.spbc-trial-renew-fullpage').hide();\n jQuery('#spbct-page--react').show();\n });\n\n jQuery(document)\n .off('click', '#spbc_debug__run_pass_check_btn')\n .on('click', '#spbc_debug__run_pass_check_btn', function() {\n let $btn = jQuery(this);\n let $st = jQuery('#spbc_debug__pass_check_status');\n $btn.prop('disabled', true);\n $st.css('color', '').text('Resetting records and running check, please wait...');\n jQuery.ajax({\n type: 'POST',\n url: ajaxurl,\n data: {\n action: 'spbc_debug_users_pass_check_run',\n security: spbcSettings.ajax_nonce,\n },\n success: function(response) {\n $btn.prop('disabled', false);\n if (response.success) {\n $st.css('color', 'green').html(\n '✓ Done! Total: ' + response.data.total +\n ', Checked: ' + response.data.checked +\n ', Leaked: ' + response.data.leaked +\n ' — You can now test the login form in incognito.',\n );\n } else {\n $st.css('color', 'red').text('Error: ' + (response.data || 'unknown error'));\n }\n },\n error: function(xhr) {\n $btn.prop('disabled', false);\n $st.css('color', 'red').text('Request failed: ' + xhr.status + ' ' + xhr.statusText);\n },\n });\n });\n});\n\n/**\n * Toggle class for DOM elements\n * @param {Array} arr\n * @param {string} className\n * @return {void}\n */\nfunction spbcToggleClass(arr, className) {\n for (let i=0; i {\n spbcSaveSettingsButtonMobileLayout();\n });\n}\n\n/**\n * A chunk to hide the custom element \"submit button\" on large screen or make sticky on mobile\n */\nfunction spbcSaveSettingsButtonMobileLayout() {\n const selector = document.querySelector('div[name=\"spbc_settings--save_button_custom\"]');\n if (selector) {\n if (window.innerWidth > 900) {\n selector.style.display = 'none';\n } else {\n selector.style.display = 'flex';\n }\n }\n}\n\n/**\n * Sends logs\n *\n * @param {*} result\n * @param {*} data\n * @param {*} params\n * @param {*} obj\n */\nfunction spbcSendLogsCallback(result, data, params, obj) {\n jQuery('.spbc_tab_nav-'+data.tab_name).data('loaded', false);\n spbcSwitchTab(document.getElementsByClassName('spbc_tab_nav-'+data.tab_name)[0]);\n}\n\n/**\n * Disables/enables settings by ID\n *\n * @param {array} settingsIDs\n * @param {number} enable\n */\nfunction spbcSettingsDependenciesbyId(settingsIDs, enable) { // eslint-disable-line no-unused-vars\n if (typeof settingsIDs === 'string') {\n tmp = [];\n tmp.push(settingsIDs);\n settingsIDs = tmp;\n }\n\n enable = typeof enable === 'undefined' ? null : +enable;\n\n settingsIDs.forEach(function(settingID, i, arr) {\n let elem = document.getElementById('spbc_setting_'+settingID);\n let doDisable = function() {\n elem.setAttribute('disabled', 'disabled');\n };\n let doEnable = function() {\n elem.removeAttribute('disabled');\n };\n\n if (enable !== null) {\n // Set\n enable === 1 ? doEnable() : doDisable();\n } else {\n // Switch\n elem.getAttribute('disabled') === null ? doDisable() : doEnable();\n }\n });\n}\n\n/**\n * Recursive in case children found!\n *\n * Settings dependencies\n *\n * @param {array} settingNames\n * @param {number} enable\n */\nfunction spbcSettingsDependencies(settingNames, enable) { // eslint-disable-line no-unused-vars\n // Cast settingNames to array\n const eventTarget = window.event && window.event.target ? window.event.target : null;\n const isRadio = eventTarget && eventTarget.type === 'radio';\n const radioEnabled = isRadio && eventTarget.value == '1';\n const targetEnableFlag = eventTarget ?\n ((isRadio && !radioEnabled) || +eventTarget.checked) :\n null;\n const doEnableFlag = typeof enable === 'undefined' ? targetEnableFlag : +enable;\n\n settingNames = typeof settingNames === 'string' ? settingNames.split(',') : settingNames;\n\n settingNames.forEach(function(settingName, i, arr) {\n let elements = document.getElementsByName('spbc_settings['+settingName+']');\n if (elements.length === 0) {\n // multiple selector case\n elements = document.getElementsByName('spbc_settings['+settingName+'][]');\n }\n\n elements\n .forEach(function( elem, i, arr ) {\n let doDisable = function() {\n elem.setAttribute('disabled', 'disabled');\n };\n let doEnable = function() {\n elem.removeAttribute('disabled');\n };\n if (doEnableFlag !== null) {\n // Set\n doEnableFlag === 1 ? doEnable() : doDisable();\n } else {\n // Switch\n elem.getAttribute('disabled') === null ? doDisable() : doEnable();\n }\n\n let children = elem.getAttribute('data-children');\n if ( children !== null ) {\n spbcSettingsDependencies(children, doEnableFlag && elem.checked);\n }\n });\n });\n}\n\n/**\n * Shows recommendation\n *\n * @param {object} label\n * @param {string} settingId\n */\nfunction spbcSettingsShowRecommendation(label, settingId) {\n let removeRecommendationFunc = function(e) {\n if (\n typeof e === 'undefined' ||\n (\n jQuery(e.target).parent('.spbc_long_desc').length == 0 ||\n jQuery(e.target).hasClass('spbc_long_desc__cancel')\n ) && !jQuery(e.target).hasClass('spbc_long_recommendation__show')\n ) {\n jQuery('.spbc_long_desc').remove();\n jQuery(document).off('click', removeRecommendationFunc);\n }\n };\n\n removeRecommendationFunc();\n\n label.after('
');\n let obj = jQuery('#spbc_long_desc__'+settingId);\n obj.append('')\n .append('
')\n .css({\n top: label.position().top + 25,\n left: label.position().left + 5,\n });\n\n\n spbcSendAJAXRequest(\n {action: 'spbc_settings__get_recommendation', setting_id: settingId},\n {\n spinner: obj.children('img'),\n callback: function(result, data, params, obj) {\n obj.empty()\n .append('
')\n .append('')\n .append('

'+result.title+'

')\n .append('

'+result.desc+'

');\n\n jQuery(document).on('click', removeRecommendationFunc);\n },\n },\n obj,\n );\n}\n\n/**\n * Shows/hides full text\n */\nfunction spbcStartShowHide() { // eslint-disable-line no-unused-vars\n jQuery('.spbcShortText')\n .off('mouseover' )\n .on('mouseover', function() {\n jQuery(this).next().show();\n })\n .off('mouseout' )\n .on('mouseout', function() {\n jQuery(this).next().hide();\n });\n jQuery('.spbcFullText')\n .off('mouseout' )\n .on('mouseout', function() {\n jQuery(this).hide();\n })\n .off('mouseover' )\n .on('mouseover', function() {\n jQuery(this).show();\n });\n}\n\n/**\n * Reloads last scan info\n */\nfunction spbcScannerReloadScanInfo() { // eslint-disable-line no-unused-vars\n jQuery('.spbc_hint__last_scan_title').text(spbcScaner.look_below_for_scan_res);\n const data = {\n action: 'spbc_scanner__last_scan_info',\n };\n const params = {\n callback: (result) => {\n document.getElementById('spbc_scanner__last_scan_info').innerText = result.data;\n },\n };\n spbcSendAJAXRequest(data, params);\n}\n\n/**\n * Checking current account status for renew notice\n */\nfunction spbcBannerCheck() {\n let bannerChecker = setInterval( function() {\n spbcSendAJAXRequest(\n {action: 'spbc_settings__check_renew_banner'},\n {\n callback: function(result, data, params, obj) {\n if (result.close_renew_banner) {\n if (jQuery('#spbc_renew_notice').length) {\n jQuery('#spbc_renew_notice').hide('slow');\n }\n if (jQuery('#spbc_trial_notice').length) {\n jQuery('#spbc_trial_notice').hide('slow');\n }\n clearInterval(bannerChecker);\n }\n },\n },\n );\n }, 60000);\n}\n\njQuery(document).ready(function() {\n if (jQuery('#spbc_renew_notice').length || jQuery('#spbc_trial_notice').length) {\n spbcBannerCheck();\n }\n\n // Sync button migrated to react\n\n if ( spbcSettings.key_changed ) {\n jQuery('#spbc_button__sync').click();\n }\n\n // Get Key Auto button migrated to Signup Wizard\n\n // Import settings\n jQuery( document ).on('click', '#spbc_settings_templates_import_button', function() {\n jQuery('#spbc-ajax-result').remove();\n let optionSelected = jQuery('option:selected', jQuery('#spbc_settings_templates_import'));\n let templateNameInput = jQuery('#spbc_settings_templates_import_name');\n templateNameInput.css('border-color', 'inherit');\n if ( typeof optionSelected.data('id') === 'undefined' ) {\n console.log( 'Attribute \"data-id\" not set for the option.' );\n return;\n }\n let data = {\n 'template_id': optionSelected.data('id'),\n 'template_name': optionSelected.data('name'),\n 'settings': optionSelected.data('settings'),\n };\n let button = this;\n spbcSendAJAXRequest(\n {action: 'spbc_settings_templates_import', data: data},\n {\n timeout: 25000,\n button: button,\n spinner: jQuery('#spbc_settings_templates_import_button .spbc_preloader_button' ),\n callback: function(result, data, params, obj) {\n let selector;\n if (result.success) {\n selector = '

' + result.data + '

';\n jQuery( selector ).insertAfter( jQuery(button) );\n jQuery('#spbc_settings_templates_import_button .spbc_success').show(300);\n setTimeout(function() {\n jQuery('#spbc_settings_templates_import_button .spbc_success').hide(300);\n }, 2000);\n document.addEventListener('spbcModalClosed', function( e ) {\n document.location.reload();\n });\n setTimeout(function() {\n spbcModal.close();\n }, 2000);\n } else {\n selector = '

' + result.data + '

';\n jQuery( selector ).insertAfter( jQuery(button) );\n }\n },\n },\n );\n });\n\n // Export settings\n jQuery( document ).on('click', '#spbc_settings_templates_export_button', function() {\n jQuery('#spbc-ajax-result').remove();\n let optionSelected = jQuery('option:selected', jQuery('#spbc_settings_templates_export'));\n let templateNameInput = jQuery('#spbc_settings_templates_export_name');\n templateNameInput.css('border-color', 'inherit');\n if ( typeof optionSelected.data('id') === 'undefined' ) {\n console.log( 'Attribute \"data-id\" not set for the option.' );\n return;\n }\n let data;\n if ( optionSelected.data('id') === 'new_template' ) {\n let templateName = templateNameInput.val();\n if ( templateName === '' ) {\n templateNameInput.css('border-color', 'red');\n return;\n }\n data = {\n 'template_name': templateName,\n };\n } else {\n data = {\n 'template_id': optionSelected.data('id'),\n };\n }\n let button = this;\n spbcSendAJAXRequest(\n {action: 'spbc_settings_templates_export', data: data},\n {\n timeout: 25000,\n button: button,\n spinner: jQuery('#spbc_settings_templates_export_button .spbc_preloader_button' ),\n callback: function(result, data, params, obj) {\n let selector;\n if (result.success) {\n selector = '

' + result.data + '

';\n jQuery( selector ).insertAfter( jQuery(button) );\n jQuery('#spbc_settings_templates_export_button .spbc_success').show(300);\n setTimeout(function() {\n jQuery('#spbc_settings_templates_export_button .spbc_success').hide(300);\n }, 2000);\n document.addEventListener('spbcModalClosed', function( e ) {\n document.location.reload();\n });\n setTimeout(function() {\n spbcModal.close();\n }, 2000);\n } else {\n selector = '

' + result.data + '

';\n jQuery( selector ).insertAfter( jQuery(button) );\n }\n },\n },\n );\n });\n\n // Reset settings\n jQuery( document ).on('click', '#spbc_settings_templates_reset_button', function() {\n let button = this;\n spbcSendAJAXRequest(\n {action: 'spbc_settings_templates_reset'},\n {\n timeout: 25000,\n button: button,\n spinner: jQuery('#spbc_settings_templates_reset_button .spbc_preloader_button' ),\n callback: function(result, data, params, obj) {\n let selector;\n if (result.success) {\n selector = '

' + result.data + '

';\n jQuery( selector ).insertAfter( jQuery(button) );\n jQuery('#spbc_settings_templates_reset_button .spbc_success').show(300);\n setTimeout(function() {\n jQuery('#spbc_settings_templates_reset_button .spbc_success').hide(300);\n }, 2000);\n document.addEventListener('spbcModalClosed', function( e ) {\n document.location.reload();\n });\n setTimeout(function() {\n spbcModal.close();\n }, 2000);\n } else {\n selector = '

' + result.data + '

';\n jQuery( selector ).insertAfter( jQuery(button) );\n }\n },\n },\n );\n });\n\n jQuery(document).on('change', '#spbc_settings_templates_export', function() {\n let optionSelected = jQuery('option:selected', this);\n if ( optionSelected.data('id') === 'new_template' ) {\n jQuery(this).parent().parent().find('#spbc_settings_templates_export_name').show();\n } else {\n jQuery(this).parent().parent().find('#spbc_settings_templates_export_name').hide();\n }\n });\n\n // Only handle legacy banners, not React Components\n // @ToDo remove the outdated way to close notice after transferring all tabs to components.\n jQuery(document).on('click', '.spbct_notice-dismiss', function(e) {\n let classListNotice = this.parentElement.classList;\n let classNoticeForClose = classListNotice[classListNotice.length - 1];\n\n document.querySelectorAll('.' + classNoticeForClose).forEach((element) => {\n element.parentElement.setAttribute('style', 'display:none;');\n spbcSetCookie(classNoticeForClose, 1, 86400 * 30);\n });\n });\n\n // @ToDo need to be refactored - this is the code dublication\n jQuery(document).off( 'click', '.spbc_long_description__show');\n jQuery(document).on('click', '.spbc_long_description__show', function() {\n self = jQuery(this);\n spbcHandleLongDescription(self, self.attr('data-setting') || self.attr('setting'));\n });\n\n // @ToDo need to be refactored - this is the code dublication\n jQuery(document).off( 'click', '.spbc_long_recommendation__show');\n jQuery(document).on('click', '.spbc_long_recommendation__show', function() {\n self = jQuery(this);\n spbcSettingsShowRecommendation(self, self.attr('data-setting') || self.attr('setting'));\n });\n\n // Reset dismiss flag if user came from outside plugin settings\n if (!document.referrer || document.referrer.indexOf('page=spbc') === -1) {\n sessionStorage.removeItem('spbc_trial_renew_fullpage_dismissed');\n }\n\n // Hide fullpage trial banner if it was already dismissed in this browser session\n if (sessionStorage.getItem('spbc_trial_renew_fullpage_dismissed')) {\n jQuery('.spbc-trial-renew-fullpage').hide();\n jQuery('#spbct-page--react').show();\n }\n\n jQuery('body').on('click', '.spbc-trial-renew-fullpage .notice-dismiss', function(e) {\n e.preventDefault();\n sessionStorage.setItem('spbc_trial_renew_fullpage_dismissed', '1');\n jQuery('.spbc-trial-renew-fullpage').hide();\n jQuery('#spbct-page--react').show();\n });\n\n jQuery(document)\n .off('click', '#spbc_debug__run_pass_check_btn')\n .on('click', '#spbc_debug__run_pass_check_btn', function() {\n let $btn = jQuery(this);\n let $st = jQuery('#spbc_debug__pass_check_status');\n $btn.prop('disabled', true);\n $st.css('color', '').text('Resetting records and running check, please wait...');\n jQuery.ajax({\n type: 'POST',\n url: ajaxurl,\n data: {\n action: 'spbc_debug_users_pass_check_run',\n security: spbcSettings.ajax_nonce,\n },\n success: function(response) {\n $btn.prop('disabled', false);\n if (response.success) {\n $st.css('color', 'green').html(\n '✓ Done! Total: ' + response.data.total +\n ', Checked: ' + response.data.checked +\n ', Leaked: ' + response.data.leaked +\n ' — You can now test the login form in incognito.',\n );\n } else {\n $st.css('color', 'red').text('Error: ' + (response.data || 'unknown error'));\n }\n },\n error: function(xhr) {\n $btn.prop('disabled', false);\n $st.css('color', 'red').text('Request failed: ' + xhr.status + ' ' + xhr.statusText);\n },\n });\n });\n});\n\n/**\n * Action to confirm creating support user.\n * @param {event} e\n * @return {void}\n */\nfunction spbcCreateSupportUser(e) { // eslint-disable-line no-unused-vars\n e.preventDefault();\n let localTextArray = spbcSettings.support_user_creation_msg_array;\n spbcModal.open();\n spbcModal.confirm(\n localTextArray.confirm_header,\n localTextArray.confirm_text,\n '',\n spbcCreateUserCallback,\n );\n}\n\n/**\n * Support user creation callback.\n * @return {void}\n */\nfunction spbcCreateUserCallback() {\n spbcSendAJAXRequest(\n {\n action: 'spbc_create_support_user',\n },\n {\n timeout: 5000,\n callback: function(result, data, params, obj) {\n let localTextArray = spbcSettings.support_user_creation_msg_array;\n let popupMsg = localTextArray.default_error;\n const responseValid = (\n typeof result === 'object' &&\n result.hasOwnProperty('success') &&\n result.hasOwnProperty('user_created') &&\n result.hasOwnProperty('mail_sent') &&\n result.hasOwnProperty('cron_updated') &&\n result.hasOwnProperty('user_data') &&\n result.hasOwnProperty('result_code') &&\n typeof result.user_data === 'object' &&\n result.user_data.hasOwnProperty('username') &&\n result.user_data.hasOwnProperty('email') &&\n result.user_data.hasOwnProperty('password')\n );\n if (responseValid && result.success) {\n if (result.user_created) {\n let mailSentMsg = '';\n let successCreationMsg = '';\n let cronUpdatedMsg = localTextArray.cron_updated;\n\n if (result.mail_sent) {\n mailSentMsg = localTextArray.mail_sent_success;\n } else {\n mailSentMsg = localTextArray.mail_sent_error;\n }\n\n if (result.result_code === 0) {\n successCreationMsg = localTextArray.user_updated;\n } else {\n successCreationMsg = localTextArray.user_created;\n }\n\n jQuery('#spbc_stats_user_creation_username').text(result.user_data.username);\n jQuery('#spbc_stats_user_creation_email').text(result.user_data.email);\n jQuery('#spbc_stats_user_creation_password').text(result.user_data.password);\n jQuery('#spbc_stats_user_creation_mail_sent').text(mailSentMsg);\n jQuery('#spbc_stats_user_creation_title').text(successCreationMsg);\n jQuery('#spbc_stats_user_creation_cron_updated').text(cronUpdatedMsg);\n jQuery('.spbc_stats_user_creation_result').css('display', 'block');\n return;\n } else {\n if (result.result_code === -2) {\n popupMsg = localTextArray.invalid_permission;\n } else if (result.result_code === -1) {\n popupMsg = localTextArray.unknown_creation_error;\n } else if (result.result_code === -4) {\n popupMsg = localTextArray.on_cooldown;\n } else if (result.result_code === -5) {\n popupMsg = localTextArray.email_is_busy;\n }\n }\n }\n spbcModal.loaded = popupMsg;\n spbcModal.open();\n },\n errorOutput: function(msg) {\n spbcModal.loaded = msg;\n spbcModal.open();\n },\n },\n );\n}\n"],"names":["spbcSwitchTab","tab","params","let","tabName","classList","replace","searchParams","URLSearchParams","window","location","search","hashParam","set","hash","newRelativePathQuery","pathname","toString","history","replaceState","get","jQuery","hide","removeClass","addClass","data","action","spbcHighlightElement","target","times","setTimeout","additional","click","tab_name","security","spbcSettings","ajax_nonce","callback","spbcDrawSettingsCallback","notJson","spbcSendAJAXRequest","result","obj","replaceWith","document","off","on","spbcSettingsShowRecommendation","self","this","attr","spbcSendLogsCallback","scrollToTag","getElementById","substring","scrollIntoView","spbcHighlightCriticalFilesNotice","spbcSaveSettingsButtonMobileLayout","addEventListener","selector","querySelector","innerWidth","style","display","getElementsByClassName","spbcSettingsDependenciesbyId","settingsIDs","enable","tmp","push","forEach","settingID","i","arr","elem","doDisable","setAttribute","doEnable","removeAttribute","getAttribute","spbcSettingsDependencies","settingNames","eventTarget","event","isRadio","type","radioEnabled","value","targetEnableFlag","checked","doEnableFlag","split","settingName","elements","getElementsByName","length","children","label","settingId","removeRecommendationFunc","e","parent","hasClass","remove","after","append","css","top","position","left","setting_id","spinner","empty","title","desc","spbcStartShowHide","next","show","spbcScannerReloadScanInfo","text","spbcScaner","look_below_for_scan_res","innerText","spbcBannerCheck","bannerChecker","setInterval","close_renew_banner","clearInterval","spbcCreateSupportUser","preventDefault","localTextArray","support_user_creation_msg_array","spbcModal","open","confirm","confirm_header","confirm_text","spbcCreateUserCallback","timeout","popupMsg","default_error","hasOwnProperty","user_data","success","user_created","mailSentMsg","successCreationMsg","cronUpdatedMsg","cron_updated","mail_sent","mail_sent_success","mail_sent_error","result_code","user_updated","username","email","password","invalid_permission","unknown_creation_error","on_cooldown","email_is_busy","loaded","errorOutput","msg","String","prototype","printf","formatted","arg","arguments","beforeFormatted","indexOf","afterFormatted","ready","key_changed","optionSelected","console","log","template_id","template_name","settings","button","insertAfter","reload","close","templateNameInput","templateName","val","find","classListNotice","parentElement","classNoticeForClose","querySelectorAll","element","spbcSetCookie","spbcHandleLongDescription","referrer","sessionStorage","removeItem","getItem","setItem","$btn","$st","prop","ajax","url","ajaxurl","response","html","total","leaked","error","xhr","status","statusText"],"mappings":"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"} \ No newline at end of file diff --git a/js/src/spbc-settings.js b/js/src/spbc-settings.js index accb89b0a..7a2c93e68 100644 --- a/js/src/spbc-settings.js +++ b/js/src/spbc-settings.js @@ -585,18 +585,6 @@ jQuery(document).ready(function() { }); }); -/** - * Toggle class for DOM elements - * @param {Array} arr - * @param {string} className - * @return {void} - */ -function spbcToggleClass(arr, className) { - for (let i=0; i Date: Wed, 19 Aug 2026 15:16:13 +0300 Subject: [PATCH 6/6] Fix. Code. Psalm notice fixed. --- inc/spbc-settings.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/inc/spbc-settings.php b/inc/spbc-settings.php index 2bd945c04..241cd2997 100644 --- a/inc/spbc-settings.php +++ b/inc/spbc-settings.php @@ -375,7 +375,7 @@ function spbc_settings__register_sections_and_fields($elems) break; } if (isset($elem['fields'])) { - $elem['fields'] = spbc_settings__register_sections_and_fields($elem['fields'], $elem_name); + $elem['fields'] = spbc_settings__register_sections_and_fields($elem['fields']); } break; case 'field':