e.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/src/spbc-timeline-widget.js b/js/src/spbc-timeline-widget.js
index a8914066a..4bfad6162 100644
--- a/js/src/spbc-timeline-widget.js
+++ b/js/src/spbc-timeline-widget.js
@@ -32,6 +32,7 @@ class SpbcTimelineWidget {
this.summaryEventsCount = 0;
this.userLegendExpanded = false;
this.selectedUserValue = 'most_active__action';
+ this.timelineInited = false;
}
/**
@@ -51,7 +52,37 @@ class SpbcTimelineWidget {
this.timelineKnownEventsData = spbcTimelineWidgetData.known_events_data;
this.timelineMarkersData = this.generateMarkersData(spbcTimelineWidgetData.found_types);
- // Initialize timeline when tab is loaded
+ const tryInit = () => {
+ const accordion = document.getElementById('spbc-timeline-accordion');
+ if (!accordion) {
+ return false;
+ }
+ // Draw first, while the panel is still visible. jQuery accordion
+ // with active:false sets display:none and clientWidth becomes 0.
+ this.initTimeline();
+ const $acc = jQuery(accordion);
+ if ($acc.length && ! $acc.hasClass('ui-accordion')) {
+ $acc.accordion({
+ heightStyle: 'content',
+ collapsible: true,
+ active: false,
+ activate: () => {
+ this.redrawCurrentTimeline();
+ },
+ });
+ }
+ return true;
+ };
+
+ if (!tryInit()) {
+ const observer = new MutationObserver(() => {
+ if (tryInit()) {
+ observer.disconnect();
+ }
+ });
+ observer.observe(document.body, {childList: true, subtree: true});
+ }
+
window.addEventListener('spbctTabLoaded', (event) => {
if (event.detail.tab === 'security_log') {
this.initTimeline();
@@ -70,18 +101,28 @@ class SpbcTimelineWidget {
// Handle window resize
window.addEventListener('resize', () => {
- if (!this.selectedUserValue) {
- return;
- }
this.userLegendExpanded = false;
- if (this.selectedUserValue === 'most_active__action') {
- this.drawUserTimelineMostActive();
- } else {
- this.drawUserTimeline(this.selectedUserValue);
- }
+ this.redrawCurrentTimeline();
});
}
+ /**
+ * Redraw the currently selected timeline using the live container width.
+ */
+ redrawCurrentTimeline() {
+ if (!this.timelineInited || !this.selectedUserValue) {
+ return;
+ }
+ if (!document.getElementById('spbc-timeline-svg')) {
+ return;
+ }
+ if (this.selectedUserValue === 'most_active__action') {
+ this.drawUserTimelineMostActive();
+ } else {
+ this.drawUserTimeline(this.selectedUserValue);
+ }
+ }
+
/**
* Initialize timeline with user selection
*/
@@ -90,6 +131,10 @@ class SpbcTimelineWidget {
const trigger = document.getElementById('spbc-timeline-user-select-trigger');
const dropdown = document.getElementById('spbc-timeline-user-select-dropdown');
const valueDisplay = document.getElementById('spbc-timeline-user-select-value');
+ if (this.timelineInited || !wrapper || !trigger || !dropdown || !valueDisplay) {
+ return;
+ }
+ this.timelineInited = true;
const usersWithRecentEvents = [];
// Calculate summary statistics for the last 24 hours only