PluginProbe
WebTotem Security / 3.0.0
WebTotem Security v3.0.0
3.0.1 3.0.0 trunk 1.0 1.1 1.2 1.3 1.3.1 1.3.2 1.3.3 2.0 2.1 2.1.1 2.1.2 2.1.3 2.1.4 2.1.5 2.1.6 2.1.7 2.1.8 2.1.9 2.2.1 2.2.2 2.2.3 2.2.4 All 109 releases
wt-security / includes / js / chart.js

chart.js in WebTotem Security 3.0.0, at includes/js/chart.js

1,057 lines 33.6 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 const isDarkThemeSet = ()=> {
2 return !!document.querySelector(".wtotem_theme—dark")}
3 ;
4
5 // async function drawLineChart(dataset) {
6 // var diagram = document.getElementById('wtotem_chart_diagram');
7 // var days = diagram.dataset.days;
8 //
9 // const attacksAccessor = (d) => d.attacks;
10 // const blockedAccessor = (d) => d.blocked;
11 // const biggerAccessor = (d) =>
12 // d.attacks > d.blocked ? d.attacks : d.blocked;
13 // const dateParser = (days <= 1) ? d3.timeParse("%Y-%m-%d %H:%m:%M"): d3.timeParse("%Y-%m-%d");
14 // const xAccessor = (d) => dateParser(d.date);
15 //
16 //
17 // const chartWrapper = document.getElementById('line-chart')
18 // let dimensions = {
19 // width: chartWrapper.offsetWidth,
20 // height: 251,
21 // margin: {
22 // top: 15,
23 // right: 15,
24 // bottom: 40,
25 // left: 60,
26 // },
27 // };
28 // dimensions.boundedWidth =
29 // dimensions.width - dimensions.margin.left - dimensions.margin.right;
30 // dimensions.boundedHeight =
31 // dimensions.height - dimensions.margin.top - dimensions.margin.bottom;
32 //
33 // const wrapper = d3
34 // .select("#line-chart")
35 // .append("svg")
36 // .attr("width", dimensions.width)
37 // .attr("height", dimensions.height);
38 //
39 // const bounds = wrapper
40 // .append("g")
41 // .style(
42 // "transform",
43 // `translate(${dimensions.margin.left - 10}px, ${
44 // dimensions.margin.top
45 // }px)`
46 // );
47 //
48 //
49 // const yValues = dataset.reduce(
50 // (acc, curr) => [...acc, curr.attacks, curr.blocked],
51 // [0, 100]
52 // );
53 //
54 // const yScale = d3
55 // .scaleLinear()
56 // .domain(d3.extent(yValues))
57 // .range([dimensions.boundedHeight, 0]);
58 // const xScale = d3
59 // .scaleTime()
60 // .domain(d3.extent(dataset, xAccessor))
61 // .range([0, dimensions.boundedWidth]);
62 //
63 // function make_y_gridlines() {
64 // return d3.axisLeft(yScale).ticks(5);
65 // }
66 //
67 // bounds
68 // .append("g")
69 // .attr("class", "grid")
70 // .call(
71 // make_y_gridlines().tickSize(-dimensions.boundedWidth).tickFormat("")
72 // );
73 //
74 // const area1 = d3
75 // .area()
76 // .x((d) => xScale(xAccessor(d)))
77 // .y0(yScale(0))
78 // .y1((d) => yScale(attacksAccessor(d)));
79 //
80 // const area2 = d3
81 // .area()
82 // .x((d) => xScale(xAccessor(d)))
83 // .y0(yScale(0))
84 // .y1((d) => yScale(blockedAccessor(d)));
85 //
86 // const isDarkTheme = isDarkThemeSet();
87 // const minOpacity = isDarkTheme ? 0.2 : 0.1;
88 // const maxOpacity = isDarkTheme ? 0.8 : 0.6;
89 //
90 // bounds
91 // .append("linearGradient")
92 // .attr("id", "area-gradient1")
93 // .attr("gradientUnits", "userSpaceOnUse")
94 // .attr("x1", 0)
95 // .attr("y1", yScale(0))
96 // .attr("x2", 0)
97 // .attr("y2", yScale(100))
98 // .selectAll("stop")
99 // .data([
100 // { offset: "0%", color: "#d46c6a", opacity: minOpacity},
101 // { offset: "100%", color: "#d46c6a", opacity: maxOpacity},
102 // ])
103 // .enter()
104 // .append("stop")
105 // .attr("offset", function (d) {
106 // return d.offset;
107 // })
108 // .attr("stop-color", function (d) {
109 // return d.color;
110 // })
111 // .attr("stop-opacity", function (d) {
112 // return d.opacity;
113 // });
114 //
115 // bounds
116 // .append("linearGradient")
117 // .attr("id", "area-gradient2")
118 // .attr("gradientUnits", "userSpaceOnUse")
119 // .attr("x1", 0)
120 // .attr("y1", yScale(0))
121 // .attr("x2", 0)
122 // .attr("y2", yScale(100))
123 // .selectAll("stop")
124 // .data([
125 // { offset: "0%", color: "#bace3d", opacity: minOpacity },
126 // { offset: "100%", color: "#bace3d", opacity: maxOpacity},
127 // ])
128 // .enter()
129 // .append("stop")
130 // .attr("offset", function (d) {
131 // return d.offset;
132 // })
133 // .attr("stop-color", function (d) {
134 // return d.color;
135 // })
136 // .attr("stop-opacity", function (d) {
137 // return d.opacity;
138 // });
139 //
140 // const lineGenerator1 = d3
141 // .line()
142 // .x((d) => xScale(xAccessor(d)))
143 // .y((d) => yScale(attacksAccessor(d)));
144 // const lineGenerator2 = d3
145 // .line()
146 // .x((d) => xScale(xAccessor(d)))
147 // .y((d) => yScale(attacksAccessor(d)));
148 //
149 // const line1 = bounds
150 // .append("path")
151 // .datum(dataset)
152 // .attr("d", lineGenerator1(dataset))
153 // .attr("class", "area")
154 // .attr("d", area1)
155 // .style("fill", "url(#area-gradient1)");
156 //
157 // const line2 = bounds
158 // .append("path")
159 // .datum(dataset)
160 // .attr("d", lineGenerator2(dataset))
161 // .attr("class", "area")
162 // .attr("d", area2)
163 // .style("fill", "url(#area-gradient2)");
164 //
165 // const yAxisGenerator = d3.axisLeft().scale(yScale);
166 // // const xAxisGenerator = d3.axisBottom().scale(xScale);
167 //
168 // let dateFormat = (days <= 1) ? d3.timeFormat("%H:%M") : (days > 31) ? d3.timeFormat("%b %Y") : d3.timeFormat("%b %d");
169 // const xAxisGenerator = (days <= 1) ? d3.axisBottom().scale(xScale).ticks(d3.timeHour.every(2)).tickFormat(dateFormat) :
170 // (days > 31) ? d3.axisBottom().scale(xScale).ticks(d3.timeMonth.every(1)).tickFormat(dateFormat) :
171 // (days <= 7) ? d3.axisBottom().scale(xScale).ticks(d3.timeDay.every(1)).tickFormat(dateFormat) :
172 // d3.axisBottom().scale(xScale).ticks(9).tickFormat(dateFormat);
173 // //const xAxisGenerator = d3.axisBottom().scale(xScale).ticks(ticks).tickFormat(dateFormat);
174 //
175 // const yAxis = bounds.append("g").attr("class", "axis").call(yAxisGenerator);
176 // const xAxis = bounds
177 // .append("g")
178 // .call(xAxisGenerator)
179 // .attr("class", "axis chart-date")
180 // .style("transform", `translateY(${dimensions.boundedHeight}px)`);
181 //
182 // const listeningRect = bounds
183 // .append("rect")
184 // .attr("class", "listening-rect")
185 // .attr("width", dimensions.boundedWidth)
186 // .attr("height", dimensions.boundedHeight)
187 // .on("mousemove", onMouseMove)
188 // .on("mouseleave", onMouseLeave);
189 //
190 // const tooltipAttacks = d3.select("#tooltipAttacks");
191 // const tooltipBlocked = d3.select("#tooltipBlocked");
192 // const tooltipCircle1 = bounds
193 // .append("circle")
194 // .attr("class", "tooltip-circle")
195 // .attr("r", 4)
196 // .attr("stroke", "#F3F5F6")
197 // .attr("fill", "#1D293F")
198 // .attr("stroke-width", 2)
199 // .style("opacity", 0);
200 // const tooltipCircle2 = bounds
201 // .append("circle")
202 // .attr("class", "tooltip-circle")
203 // .attr("r", 4)
204 // .attr("stroke", "#F3F5F6")
205 // .attr("fill", "#1D293F")
206 // .attr("stroke-width", 2)
207 // .style("opacity", 0);
208 // function onMouseMove() {
209 // const mousePosition = d3.mouse(this);
210 // const hoveredDate = xScale.invert(mousePosition[0]);
211 //
212 // const getDistanceFromHoveredDate = (d) =>
213 // Math.abs(xAccessor(d) - hoveredDate);
214 // const closestIndex = d3.scan(
215 // dataset,
216 // (a, b) =>
217 // getDistanceFromHoveredDate(a) - getDistanceFromHoveredDate(b)
218 // );
219 // const closestDataPoint = dataset[closestIndex];
220 //
221 // const closestXValue = xAccessor(closestDataPoint);
222 // const closestAttacksValue = attacksAccessor(closestDataPoint);
223 // const closestBlockedValue = blockedAccessor(closestDataPoint);
224 //
225 // if(closestAttacksValue || closestBlockedValue){
226 // AmplitudeAnalytics.worldMap(closestAttacksValue, closestBlockedValue);
227 // }
228 //
229 // const x = xScale(closestXValue) + dimensions.margin.left + 8;
230 // const yAttacks =
231 // yScale(closestAttacksValue) + dimensions.margin.top + 10;
232 // const yBlocked =
233 // yScale(closestBlockedValue) + dimensions.margin.top + 10;
234 //
235 // tooltipAttacks.style(
236 // "transform",
237 // `translate(` +
238 // `calc( -50% + ${x}px),` +
239 // `calc(-100% + ${yAttacks}px)` +
240 // `)`
241 // );
242 // tooltipBlocked.style(
243 // "transform",
244 // `translate(` +
245 // `calc( -50% + ${x}px),` +
246 // `calc(-100% + ${yBlocked}px)` +
247 // `)`
248 // );
249 //
250 // tooltipAttacks.style("opacity", 1);
251 // tooltipBlocked.style("opacity", 1);
252 //
253 // tooltipAttacks.select("#countAttacks").html(closestAttacksValue);
254 // tooltipBlocked.select("#countBlocked").html(closestBlockedValue);
255 // tooltipCircle1
256 // .attr("cx", xScale(closestXValue))
257 // .attr("cy", yScale(closestAttacksValue))
258 // .style("opacity", 1);
259 // tooltipCircle2
260 // .attr("cx", xScale(closestXValue))
261 // .attr("cy", yScale(closestBlockedValue))
262 // .style("opacity", 1);
263 // }
264 //
265 // function onMouseLeave() {
266 // tooltipAttacks.style("opacity", 0);
267 // tooltipBlocked.style("opacity", 0);
268 //
269 // tooltipCircle1.style("opacity", 0);
270 // tooltipCircle2.style("opacity", 0);
271 // }
272 // }
273
274 async function drawLineChart(dataset) {
275 var diagram = document.getElementById('wtotem_chart_diagram');
276 var days = diagram.dataset.days;
277
278 // используем встроенный Date
279 const xAccessor = (d) => new Date(d.date);
280
281 const chartWrapper = document.getElementById('line-chart');
282
283 const allDates = Array.from(new Set(
284 Object.values(dataset).flat().map(d => d.date)
285 )).sort();
286
287 const preparedData = {};
288 Object.keys(dataset).forEach(key => {
289 const data = dataset[key];
290 preparedData[key] = allDates.map(date => {
291 const found = data.find(d => d.date === date);
292 return found ? found : { date, total_attack: 0 };
293 });
294 });
295
296 let dimensions = {
297 width: chartWrapper.offsetWidth,
298 height: 300,
299 margin: { top: 20, right: 20, bottom: 40, left: 60 },
300 };
301 dimensions.boundedWidth =
302 dimensions.width - dimensions.margin.left - dimensions.margin.right;
303 dimensions.boundedHeight =
304 dimensions.height - dimensions.margin.top - dimensions.margin.bottom;
305
306 // очищаем контейнер
307 d3.select("#line-chart").selectAll("svg").remove();
308
309 const svg = d3.select("#line-chart")
310 .append("svg")
311 .attr("width", dimensions.width)
312 .attr("height", dimensions.height);
313
314 const bounds = svg.append("g")
315 .attr("transform", `translate(${dimensions.margin.left},${dimensions.margin.top})`);
316
317 // --- Категории атак (ключи объекта) ---
318 const keys = Object.keys(preparedData);
319
320 // --- Собираем все значения для масштаба ---
321 const allData = keys.flatMap(k => preparedData[k]);
322 const yValues = allData.map(d => +d.total_attack);
323
324 const yScale = d3.scaleLinear()
325 .domain([0, d3.max(yValues)])
326 .nice()
327 .range([dimensions.boundedHeight, 0]);
328
329 const xScale = d3.scaleTime()
330 .domain(d3.extent(allData, xAccessor))
331 .range([0, dimensions.boundedWidth]);
332
333 // --- Цвета ---
334 const color = d3.scaleOrdinal(d3.schemeCategory10).domain(keys);
335
336 // --- Линии для каждой категории ---
337 keys.forEach((key) => {
338 const line = d3.line()
339 .x(d => xScale(xAccessor(d)))
340 .y(d => yScale(+d.total_attack))
341 .curve(d3.curveMonotoneX);
342
343 bounds.append("path")
344 .datum(preparedData[key])
345 .attr("d", line)
346 .attr("stroke", color(key))
347 .attr("fill", "none")
348 .attr("stroke-width", 2);
349 });
350
351 // --- Оси ---
352 let dateFormat = (days <= 1) ? d3.timeFormat("%H:%M") :
353 (days > 31) ? d3.timeFormat("%b %Y") :
354 (days <= 7) ? d3.timeFormat("%b %d") :
355 d3.timeFormat("%b %d");
356
357 const xAxisGenerator = (days <= 1)
358 ? d3.axisBottom(xScale).ticks(d3.timeHour.every(2)).tickFormat(dateFormat)
359 : (days > 31)
360 ? d3.axisBottom(xScale).ticks(d3.timeMonth.every(1)).tickFormat(dateFormat)
361 : (days <= 7)
362 ? d3.axisBottom(xScale).ticks(d3.timeDay.every(1)).tickFormat(dateFormat)
363 : d3.axisBottom(xScale).ticks(9).tickFormat(dateFormat);
364
365 bounds.append("g")
366 .call(d3.axisLeft(yScale))
367 .attr("class", "axis");
368
369 bounds.append("g")
370 .call(xAxisGenerator)
371 .attr("class", "axis chart-date")
372 .attr("transform", `translate(0,${dimensions.boundedHeight})`);
373
374 // --- Легенда ---
375 const legend = svg.append("g")
376 .attr("transform", `translate(${dimensions.margin.left}, 10)`);
377
378 keys.forEach((key, i) => {
379 const g = legend.append("g")
380 .attr("transform", `translate(${i * 100}, 0)`);
381
382 g.append("rect")
383 .attr("width", 12)
384 .attr("height", 12)
385 .attr("fill", color(key));
386
387 g.append("text")
388 .attr("x", 18)
389 .attr("y", 10)
390 .attr("class", "chart-legend-text")
391 .text(key);
392 });
393 }
394
395 const drawWafChart = (data) => {
396 var firewallChart = d3.select("#line-chart").selectAll("svg")
397 firewallChart = firewallChart.remove();
398 drawLineChart(data);
399 };
400
401 if(typeof waf_chart == "object"){
402 drawWafChart(waf_chart);
403 }
404
405 var resizeTimerFirewallChart;
406 window.onresize = function (event) {
407 clearTimeout(resizeTimerFirewallChart);
408 resizeTimerFirewallChart = setTimeout(function () {
409 drawLineChart(waf_chart);
410 }, 10);
411 };
412
413 async function drawBarChart(dataset) {
414
415 // --- Подготовка данны�
416 ---
417 const parseDate = d3.timeParse("%Y-%m-%dT%H:%M:%SZ");
418 const formatDate = d3.timeFormat("%a");
419
420 const data = dataset
421 .map(d => ({
422 day: parseDate(d.day),
423 availability: +d.availability_percent
424 }))
425 .sort((a, b) => a.day - b.day);
426
427 // --- Размеры ---
428 const margin = { top: 50, right: 20, bottom: 40, left: 50 };
429 const width = 300 - margin.left - margin.right;
430 const height = 150 - margin.top - margin.bottom;
431
432 // --- SVG контейнер ---
433 const svg = d3.select("#bar-chart")
434 .append("svg")
435 .attr("width", width + margin.left + margin.right)
436 .attr("height", height + margin.top + margin.bottom)
437 .append("g")
438 .attr("transform", "translate(" + margin.left + "," + margin.top + ")");
439
440 // --- Оси ---
441 const x = d3.scaleBand()
442 .domain(data.map(d => formatDate(d.day)))
443 .rangeRound([0, width])
444 .padding(0.2);
445
446 const y = d3.scaleLinear()
447 .domain([0, 100])
448 .rangeRound([height, 0]);
449
450 // --- Прямоугольники (бары) ---
451 svg.selectAll(".bar")
452 .data(data)
453 .enter()
454 .append("rect")
455 .attr("class", "bar")
456 .attr("x", d => x(formatDate(d.day)))
457 .attr("y", d => y(d.availability))
458 .attr("width", x.bandwidth())
459 .attr("height", d => height - y(d.availability))
460 .attr("fill", "#4e79a7");
461
462 // --- Ось X ---
463 svg.append("g")
464 .attr("transform", "translate(0," + height + ")")
465 .call(d3.axisBottom(x))
466 .selectAll("text")
467 .attr("transform", "rotate(-30)")
468 .style("text-anchor", "end");
469
470 // --- Ось Y ---
471 svg.append("g")
472 .call(d3.axisLeft(y).ticks(5));
473
474 }
475
476 const drawAvailabilityChart = (data) => {
477
478 var AvailabilityChart = d3.select("#bar-chart").selectAll("svg")
479 AvailabilityChart = AvailabilityChart.remove();
480 drawBarChart(data);
481 };
482 if(typeof availability_chart == "object"){
483 drawAvailabilityChart(availability_chart);
484 }
485
486
487 // server-status
488 async function drawServerStatusChart(id, elementSelector, tooltipSelector, tooltipValueSelector, color, dataset) {
489 const dataAccessor = (d) => d.value;
490
491 let diagram = document.querySelector(elementSelector);
492 let days = diagram.dataset.days;
493
494 const dateParser = (days <= 1) ? d3.timeParse("%Y-%m-%d %H:%m:%M"): d3.timeParse("%Y-%m-%d");
495
496 const xAccessor = (d) => dateParser(d.date);
497
498 const chartWrapper = document.querySelector(elementSelector);
499
500 let dimensions = {
501 width: chartWrapper.offsetWidth,
502 height: 251,
503 margin: {
504 top: 15,
505 right: 15,
506 bottom: 40,
507 left: 60,
508 },
509 };
510 dimensions.boundedWidth =
511 dimensions.width - dimensions.margin.left - dimensions.margin.right;
512 dimensions.boundedHeight =
513 dimensions.height - dimensions.margin.top - dimensions.margin.bottom;
514
515 const wrapper = d3
516 .select(elementSelector)
517 .append("svg")
518 .attr("width", dimensions.width)
519 .attr("height", dimensions.height);
520
521 const bounds = wrapper
522 .append("g")
523 .style(
524 "transform",
525 `translate(${dimensions.margin.left - 10}px, ${
526 dimensions.margin.top
527 }px)`
528 );
529
530 const yValues = dataset.reduce(
531 (acc, curr) => [...acc, curr.value],
532 [0, 100]
533 );
534
535 const yScale = d3
536 .scaleLinear()
537 .domain(d3.extent(yValues))
538 .range([dimensions.boundedHeight, 0]);
539 const xScale = d3
540 .scaleTime()
541 .domain(d3.extent(dataset, xAccessor))
542 .range([0, dimensions.boundedWidth]);
543
544 function make_y_gridlines() {
545 return d3.axisLeft(yScale).ticks(5);
546 }
547
548 bounds
549 .append("g")
550 .attr("class", "grid")
551 .call(
552 make_y_gridlines().tickSize(-dimensions.boundedWidth).tickFormat("")
553 );
554
555 const area1 = d3
556 .area()
557 .x((d) => xScale(xAccessor(d)))
558 .y0(yScale(0))
559 .y1((d) => yScale(dataAccessor(d)));
560
561 const gradientAreaId = "area-gradient-" + id;
562
563 const isDarkTheme = isDarkThemeSet();
564 const minOpacity = isDarkTheme ? 0.2 : 0.1;
565 const maxOpacity = isDarkTheme ? 0.8 : 0.6;
566
567 bounds
568 .append("linearGradient")
569 .attr("id", gradientAreaId)
570 .attr("gradientUnits", "userSpaceOnUse")
571 .attr("x1", 0)
572 .attr("y1", yScale(0))
573 .attr("x2", 0)
574 .attr("y2", yScale(100))
575 .selectAll("stop")
576 .data([
577 { offset: "0%", color: color, opacity: minOpacity },
578 { offset: "100%", color: color, opacity: maxOpacity },
579 ])
580 .enter()
581 .append("stop")
582 .attr("offset", function (d) {
583 return d.offset;
584 })
585 .attr("stop-color", function (d) {
586 return d.color;
587 })
588 .attr("stop-opacity", function (d) {
589 return d.opacity;
590 });
591
592 const lineGenerator1 = d3
593 .line()
594 .x((d) => xScale(xAccessor(d)))
595 .y((d) => yScale(dataAccessor(d)));
596
597 const line1 = bounds
598 .append("path")
599 .datum(dataset)
600 .attr("d", lineGenerator1(dataset))
601 .attr("class", "area")
602 .attr("d", area1)
603 .style("fill", "url(#"+gradientAreaId+")");
604
605 const yAxisGenerator = d3.axisLeft().scale(yScale);
606 // const xAxisGenerator = d3.axisBottom().scale(xScale);
607 let dateFormat = (days <= 1) ? d3.timeFormat("%H:%M") : (days > 31) ? d3.timeFormat("%b %Y") : d3.timeFormat("%b %d");
608 const xAxisGenerator = (days <= 1) ? d3.axisBottom().scale(xScale).ticks(d3.timeHour.every(2)).tickFormat(dateFormat) :
609 (days > 31) ? d3.axisBottom().scale(xScale).ticks(d3.timeMonth.every(1)).tickFormat(dateFormat) :
610 (days <= 7) ? d3.axisBottom().scale(xScale).ticks(d3.timeDay.every(1)).tickFormat(dateFormat) :
611 d3.axisBottom().scale(xScale).ticks(9).tickFormat(dateFormat);
612
613 const yAxis = bounds.append("g").attr("class", "axis").call(yAxisGenerator);
614 const xAxis = bounds
615 .append("g")
616 .call(xAxisGenerator)
617 .attr("class", "axis")
618 .style("transform", `translateY(${dimensions.boundedHeight}px)`);
619
620 const listeningRect = bounds
621 .append("rect")
622 .attr("class", "listening-rect")
623 .attr("width", dimensions.boundedWidth)
624 .attr("height", dimensions.boundedHeight)
625 .on("mousemove", onMouseMove)
626 .on("mouseleave", onMouseLeave);
627
628 const tooltipData = d3.select(tooltipSelector);
629 const tooltipCircle1 = bounds
630 .append("circle")
631 .attr("class", "tooltip-circle")
632 .attr("r", 4)
633 .attr("stroke", "#F3F5F6")
634 .attr("fill", "#1D293F")
635 .attr("stroke-width", 2)
636 .style("opacity", 0);
637
638 function onMouseMove() {
639 const mousePosition = d3.mouse(this);
640 const hoveredDate = xScale.invert(mousePosition[0]);
641
642 const getDistanceFromHoveredDate = (d) =>
643 Math.abs(xAccessor(d) - hoveredDate);
644 const closestIndex = d3.scan(
645 dataset,
646 (a, b) =>
647 getDistanceFromHoveredDate(a) - getDistanceFromHoveredDate(b)
648 );
649 const closestDataPoint = dataset[closestIndex];
650
651 const closestXValue = xAccessor(closestDataPoint);
652 const closestValue = dataAccessor(closestDataPoint);
653
654 const x = xScale(closestXValue) + dimensions.margin.left + 8;
655 const yAttacks =
656 yScale(closestValue) + dimensions.margin.top + 10;
657
658 tooltipData.style(
659 "transform",
660 `translate(` +
661 `calc( -50% + ${x}px),` +
662 `calc(-100% + ${yAttacks}px)` +
663 `)`
664 );
665
666 tooltipData.style("opacity", 1);
667
668 tooltipData.select(tooltipValueSelector).html(closestValue);
669 tooltipCircle1
670 .attr("cx", xScale(closestXValue))
671 .attr("cy", yScale(closestValue))
672 .style("opacity", 1);
673 }
674
675 function onMouseLeave() {
676 tooltipData.style("opacity", 0);
677 tooltipCircle1.style("opacity", 0);
678 }
679 }
680
681 const drawRamChart = (data) => {
682 const ramChartSelector = "#ram-chart";
683 const ramTooltipSelector = "#tooltipRam";
684 const ramTooltipValueSelector = "#countRam";
685 const ramChartColor = "#3d50df";
686
687
688 let chartRam = d3.select(ramChartSelector).selectAll("svg")
689 chartRam = chartRam.remove();
690
691 drawServerStatusChart("ram", ramChartSelector, ramTooltipSelector, ramTooltipValueSelector, ramChartColor, data);
692 }
693
694
695 if(typeof ram_chart == "object"){
696 drawRamChart(ram_chart);
697 }
698
699
700 const drawCpuChart = (data) => {
701 const cpuChartSelector = "#cpu-chart";
702 const cpuTooltipSelector = "#tooltipCpu";
703 const cpuTooltipValueSelector = "#countCpu";
704 const cpuChartColor = "#6d3594";
705
706 let chartCpu = d3.select(cpuChartSelector).selectAll("svg");
707 chartCpu = chartCpu.remove();
708
709 drawServerStatusChart("cpu", cpuChartSelector, cpuTooltipSelector, cpuTooltipValueSelector, cpuChartColor, data);
710 }
711
712 if(typeof cpu_chart == "object") {
713 drawCpuChart(cpu_chart);
714 }
715
716
717 var resizeTimerRamChart;
718 var resizeTimerCpuChart;
719
720 window.onresize = function (event) {
721
722 if (document.querySelector("#ram-chart") !== null) {
723 clearTimeout(resizeTimerRamChart);
724 resizeTimerRamChart = setTimeout(function () {
725 if(typeof ram_chart == "object") {
726 drawRamChart(ram_chart);
727 }
728 }, 10);
729 }
730
731 if (document.querySelector("#cpu-chart") !== null) {
732 clearTimeout(resizeTimerCpuChart);
733 resizeTimerCpuChart = setTimeout(function () {
734 if(typeof cpu_chart == "object") {
735 drawCpuChart(cpu_chart);
736 }
737 }, 10);
738 }
739 };
740
741
742 const colorModeToggle = document.querySelector("#color_scheme_toggle");
743 if(colorModeToggle){
744 const addThemeChangeEventListener = (callback)=>{
745 colorModeToggle.addEventListener("change", callback)
746 }
747
748 addThemeChangeEventListener(()=>{
749 if(typeof cpu_chart == "object") {
750 drawCpuChart(cpu_chart);
751 }
752 if(typeof ram_chart == "object") {
753 drawRamChart(ram_chart);
754 }
755 if(typeof attacks_map == "object"){
756 attacksMap();
757 }
758 });
759 }
760
761 // disk-chart
762
763 async function drawDiskChart_(elementSelector, data) {
764 // set the dimensions and margins of the graph
765 var width = 150
766 height = 150
767 margin = 1
768
769 // The radius of the pieplot is half the width or half the height (smallest one). I subtract a bit of margin.
770 var radius = Math.min(width, height) / 2 - margin
771
772 // append the svg object to the div called 'my_dataviz'
773 var svg = d3.select(elementSelector)
774 .append("svg")
775 .attr("width", width)
776 .attr("height", height)
777 .append("g")
778 .attr("transform", "translate(" + width / 2 + "," + height / 2 + ")");
779
780 // Create dummy data
781 // var data = {a: 9, b: 20, c:30, d:8, e:12}
782
783 // set the color scale
784 var color = d3.scaleOrdinal()
785 .domain(data)
786 .range(["#5E6977", "#3D50DF"])
787
788 // Compute the position of each group on the pie:
789 var pie = d3.pie()
790 .value(function(d) {return d.value; })
791 var data_ready = pie(d3.entries(data))
792
793 // Build the pie chart: Basically, each part of the pie is a path that we build using the arc function.
794 svg
795 .selectAll('whatever')
796 .data(data_ready)
797 .enter()
798 .append('path')
799 .attr('d', d3.arc()
800 .innerRadius(30) // This is the size of the donut hole
801 .outerRadius(radius)
802 )
803 .attr('fill', function(d){ return(color(d.data.key)) })
804 .attr("stroke", "white")
805 .style("stroke-width", "15px")
806 .style("opacity", 1)
807
808 }
809
810
811 const drawDiskChart = (data) => {
812 const diskData = {use: data.used, free: data.free};
813 const discChartSelector = "#disk-chart";
814 drawDiskChart_(discChartSelector, diskData);
815 };
816
817 if(typeof disc_chart == "object") {
818 drawDiskChart(disc_chart);
819 }
820
821 /**
822 * Create chart "Attacks on world map"
823 * @returns {Promise<void>}
824 */
825 async function attacksMap() {
826
827 var countries = attacks_map['countries'];
828 var labels = attacks_map['labels'];
829 new Chart(document.getElementById("firewall"), {
830 type: "horizontalBar",
831
832 data: {
833 labels: labels,
834 datasets: [
835 {
836 backgroundColor: "#3D50DF",
837 data: attacks_map['attacks'],
838 },
839 ],
840 },
841 options: {
842 cornerRadius: 3,
843 maintainAspectRatio: false,
844 scales: {
845 yAxes: [
846 {
847 barPercentage: 0.6,
848 stacked: true,
849 gridLines: {
850 display: true,
851 },
852 },
853 ],
854 xAxes: [
855 {
856 gridLines: {
857 display: false,
858 },
859 ticks: {
860 beginAtZero: true, // �
861 начинаем ось X от нуля
862 },
863 },
864 ],
865 },
866 legend: {
867 display: false,
868 },
869 },
870 });
871
872 const fill = (country) => {
873 svg._groups[0][0].childNodes[0].childNodes.forEach((e) => {
874 if (e.__data__.properties.name === country) {
875 d3.select(e).style("fill", "#3D50DF");
876 }
877 });
878 };
879 // The svg
880 var svg = d3.select("#firewallMap"),
881 width = +svg.attr("width"),
882 height = +svg.attr("height");
883
884 // Map and projection
885 var path = d3.geoPath();
886 var projection = d3
887 .geoMercator()
888 .scale(65)
889 .center([0, 0])
890 .translate([230, 220]);
891
892 // Load external data and boot
893 d3.queue().defer(d3.json, world_map_json).await(ready);
894
895 function ready(error, topo) {
896
897 // Draw the map
898 svg.append("g")
899 .selectAll("path")
900 .data(topo.features)
901 .enter()
902 .append("path")
903 // draw each country
904 .attr("d", d3.geoPath().projection(projection))
905 // set the color of each country
906 .attr("fill", function () {
907 return "#5E6977";
908 })
909 .style("stroke", "transparent")
910 .attr("class", function () {
911 return "Country";
912 });
913 countries.map((e) => {
914 fill(e);
915 });
916 }
917
918
919 Chart.elements.Rectangle.prototype.draw = function () {
920 function t(t) {
921 return s[(f + t) % 4];
922 }
923 var r,
924 e,
925 i,
926 o,
927 _,
928 h,
929 l,
930 a,
931 b = this._chart.ctx,
932 d = this._view,
933 n = d.borderWidth,
934 u = this._chart.config.options.cornerRadius;
935 if (
936 (u < 0 && (u = 0),
937 void 0 === u && (u = 0),
938 d.horizontal
939 ? ((r = d.base),
940 (e = d.x),
941 (i = d.y - d.height / 2),
942 (o = d.y + d.height / 2),
943 (_ = e > r ? 1 : -1),
944 (h = 1),
945 (l = d.borderSkipped || "left"))
946 : ((r = d.x - d.width / 2),
947 (e = d.x + d.width / 2),
948 (i = d.y),
949 (_ = 1),
950 (h = (o = d.base) > i ? 1 : -1),
951 (l = d.borderSkipped || "bottom")),
952 n)
953 ) {
954 var T = Math.min(Math.abs(r - e), Math.abs(i - o)),
955 v = (n = n > T ? T : n) / 2,
956 g = r + ("left" !== l ? v * _ : 0),
957 c = e + ("right" !== l ? -v * _ : 0),
958 C = i + ("top" !== l ? v * h : 0),
959 w = o + ("bottom" !== l ? -v * h : 0);
960 g !== c && ((i = C), (o = w)),
961 C !== w && ((r = g), (e = c));
962 }
963 b.beginPath(),
964 (b.fillStyle = d.backgroundColor),
965 (b.strokeStyle = d.borderColor),
966 (b.lineWidth = n);
967 var s = [
968 [r, o],
969 [r, i],
970 [e, i],
971 [e, o],
972 ],
973 f = ["bottom", "left", "top", "right"].indexOf(l, 0);
974 -1 === f && (f = 0);
975 var q = t(0);
976 b.moveTo(q[0], q[1]);
977 for (var m = 1; m < 4; m++)
978 (q = t(m)),
979 (nextCornerId = m + 1),
980 4 == nextCornerId && (nextCornerId = 0),
981 (nextCorner = t(nextCornerId)),
982 (width = s[2][0] - s[1][0]),
983 (height = s[0][1] - s[1][1]),
984 (x = s[1][0]),
985 (y = s[1][1]),
986 (a = u) > Math.abs(height) / 2 &&
987 (a = Math.floor(Math.abs(height) / 2)),
988 a > Math.abs(width) / 2 &&
989 (a = Math.floor(Math.abs(width) / 2)),
990 height < 0
991 ? ((x_tl = x),
992 (x_tr = x + width),
993 (y_tl = y + height),
994 (y_tr = y + height),
995 (x_bl = x),
996 (x_br = x + width),
997 (y_bl = y),
998 (y_br = y),
999 b.moveTo(x_bl + a, y_bl),
1000 b.lineTo(x_br - a, y_br),
1001 b.quadraticCurveTo(x_br, y_br, x_br, y_br - a),
1002 b.lineTo(x_tr, y_tr + a),
1003 b.quadraticCurveTo(x_tr, y_tr, x_tr - a, y_tr),
1004 b.lineTo(x_tl + a, y_tl),
1005 b.quadraticCurveTo(x_tl, y_tl, x_tl, y_tl + a),
1006 b.lineTo(x_bl, y_bl - a),
1007 b.quadraticCurveTo(x_bl, y_bl, x_bl + a, y_bl))
1008 : width < 0
1009 ? ((x_tl = x + width),
1010 (x_tr = x),
1011 (y_tl = y),
1012 (y_tr = y),
1013 (x_bl = x + width),
1014 (x_br = x),
1015 (y_bl = y + height),
1016 (y_br = y + height),
1017 b.moveTo(x_bl + a, y_bl),
1018 b.lineTo(x_br - a, y_br),
1019 b.quadraticCurveTo(x_br, y_br, x_br, y_br - a),
1020 b.lineTo(x_tr, y_tr + a),
1021 b.quadraticCurveTo(x_tr, y_tr, x_tr - a, y_tr),
1022 b.lineTo(x_tl + a, y_tl),
1023 b.quadraticCurveTo(x_tl, y_tl, x_tl, y_tl + a),
1024 b.lineTo(x_bl, y_bl - a),
1025 b.quadraticCurveTo(x_bl, y_bl, x_bl + a, y_bl))
1026 : (b.moveTo(x + a, y),
1027 b.lineTo(x + width - a, y),
1028 b.quadraticCurveTo(
1029 x + width,
1030 y,
1031 x + width,
1032 y + a
1033 ),
1034 b.lineTo(x + width, y + height - a),
1035 b.quadraticCurveTo(
1036 x + width,
1037 y + height,
1038 x + width - a,
1039 y + height
1040 ),
1041 b.lineTo(x + a, y + height),
1042 b.quadraticCurveTo(
1043 x,
1044 y + height,
1045 x,
1046 y + height - a
1047 ),
1048 b.lineTo(x, y + a),
1049 b.quadraticCurveTo(x, y, x + a, y));
1050 b.fill(), n && b.stroke();
1051 };
1052 }
1053
1054 if(typeof attacks_map == "object"){
1055 attacksMap();
1056 }
1057