PageSourceSearch

https://labclimaalpino.it/Assets/Js/Widgets/GraficoMultiserie.js?v=1

js labclimaalpino.it collected 2026-10-09 04:25:36 UTC 17,786 bytes, 594 lines download raw bytes

1/**
2 * Javascript per il frontend del widget Grafico Multiserie
3 */
4$(function () {
5	bootGraficoMultiserie();
6});
7
8function boootSingleGraficoMultiserie(id, widgetId) {
9	var url = "/GraficoMultiserie/BootWidgetData";
10	let from = "";
11	let to = "";
12	let tab_id = $($("#" + id))
13		.closest(".tab-pane")
14		.attr("id")
15		.replace("tab_", "");
16	if ($("#local-timing-" + id).val() != -2) {
17		from = $("#local_data_from_" + id).val();
18		to = $("#local_data_to_" + id).val();
19	} else {
20		from = $("#data_from_" + tab_id).val();
21		to = $("#data_to_" + tab_id).val();
22	}
23	let useConfig = false;
24	if (($("#local-timing-" + id).val() == -2 && $("#timing-" + tab_id).val() == -1) || $("#local-timing-" + id).val() == -1) {
25		useConfig = true;
26	} 
27	$("#chart-container_" + id ).hide();
28	$("#" + id + " .spinner-border").show();
29	$("#" + id + " .loader-container").show();
30	$("#" + id + " .loader").show();
31	$.post(
32		url,
33		{
34			config: widgetId,
35			from: from,
36			to: to,
37			useConfig: useConfig,
38		},
39		function (json) {
40			//unlockSection(output_id);
41			if (json.success) {
42				if (json.isNew) {
43					$("#" + output_id).html("<h2>widget non configurato</h2>");
44				} else {
45					initGraficoMultiserie(json, id);
46					// setPeriodByid(id);
47				}
48			} else {
49				showNotification("CALLBACK KO", "danger");
50			}
51		}
52	);
53}
54
55function bootGraficoMultiserie() {
56	var url = "/GraficoMultiserie/BootWidgetData";
57	$(".GraficoMultiserie_hms_tab_0").each(function (i, obj) {
58		let id = obj.id;
59		let id_widget = $("#" + id + " #config").val();
60		let output_id = id + "_content";
61
62		$(".local-filter").daterangepicker(
63			{
64				timePicker: false,
65				locale: {
66					format: "DD/MM/YYYY",
67				},
68			},
69			function (start, end, label) {
70				//console.log("A new date selection was made: " + start.format('YYYY-MM-DD') + ' to ' + end.format('YYYY-MM-DD'));
71				$("#local_data_from_" + id).val(start.format("DD/MM/YYYY"));
72				$("#local_data_to_" + id).val(end.format("DD/MM/YYYY"));
73			}
74		);
75		//Se è un tab con il controllo del timing, leggo il from e to
76		//Per leggere l'id del tab devo fare sta robaccia ma tant'è
77		let tab_id = $(obj).closest(".tab-pane").attr("id").replace("tab_", "");
78		$(".global-filter").daterangepicker(
79			{
80				timePicker: false,
81				locale: {
82					format: "DD/MM/YYYY HH:mm",
83				},
84			},
85			function (start, end, label) {
86				//console.log("A new date selection was made: " + start.format('YYYY-MM-DD') + ' to ' + end.format('YYYY-MM-DD'));
87				$("#data_from_" + tab_id).val(start.format("DD/MM/YYYY"));
88				$("#data_to_" + tab_id).val(end.format("DD/MM/YYYY"));
89			}
90		);
91		let useConfig = false;
92		if (($("#local-timing-" + id).val() == -2 && $("#timing-" + tab_id).val() == -1) || $("#timing-" + tab_id).val() == -1) {
93			useConfig = true;
94			const today = new Date();
95			$("#local_data_to_" + id).val(moment(today).format("YYYY-MM-DD HH:mm"));
96		}
97
98		let from,
99			to = null;
100		if ($("#data_from_" + tab_id).length && $("#data_to_" + tab_id).length) {
101			from = $("#data_from_" + tab_id).val();
102			to = $("#data_to_" + tab_id).val();
103		}
104		$("#chart-container_" + id).hide();
105		$("#" + id + " .spinner-border").show();
106		$("#" + id + " .loader-container").show();
107		$("#" + id + " .loader").show();
108		$.post(
109			url,
110			{
111				config: id_widget,
112				from: from,
113				to: to,
114				useConfig: useConfig,
115			},
116			function (json) {
117				//unlockSection(output_id);
118				if (json.success) {
119					if (json.isNew) {
120						$("#" + output_id).html("<h2>widget non configurato</h2>");
121					} else {
122						initGraficoMultiserie(json, id);
123						//setPeriodByid(id);
124						//setTabPeriodByid(tab_id);
125					}
126				} else {
127					showNotification("CALLBACK KO", "danger");
128				}
129			}
130		);
131	});
132}
133
134function bootGraficoMultiserieTab(tabid, modal = "") {
135
136	var url = "/GraficoMultiserie/BootWidgetData";
137	$(".GraficoMultiserie_" + modal + tabid).each(function (i, obj) {
138		let id = obj.id;
139		let id_widget = $("#" + id + " #config").val();
140		let output_id = id + "_content";
141
142		//Se è un tab con il controllo del timing, leggo il from e to
143		//Per leggere l'id del tab devo fare sta robaccia ma tant'è
144		let tab_id = $(obj).closest(".tab-pane").attr("id").replace("tab_", "");
145		let from = "";
146		let to = "";
147
148		if ($("#data_from_" + tab_id).length && $("#data_to_" + tab_id).length) {
149			from = $("#data_from_" + tab_id).val();
150			to = $("#data_to_" + tab_id).val();
151		}
152		$("#chart-container_" + id).hide();
153		$("#" + id + " .spinner-border").show();
154		$("#" + id + " .loader-container").show();
155		$("#" + id + " .loader").show();
156		$.post(
157			url,
158			{
159				config: id_widget,
160				from: from,
161				to: to,
162				useConfig: true,
163			},
164			function (json) {
165				//unlockSection(output_id);
166				if (json.success) {
167					if (json.isNew) {
168						$("#" + output_id).html("<h2>widget non configurato</h2>");
169					} else {
170						initGraficoMultiserie(json, id);
171					}
172				} else {
173					showNotification("CALLBACK KO", "danger");
174				}
175			}
176		);
177	});
178}
179
180tabBootFunctions.push(bootGraficoMultiserieTab);
181
182function initGraficoMultiserie(json, id, xAxisExtra = {}) {
183	$("#chart-container_" + id).show();
184	$("#" + id + " .spinner-border").hide();
185	$("#" + id + " .loader-container").hide();
186	$("#" + id + " .loader").hide();
187	let firstDataStr = json.return_data[0][0][0][0];
188
189	let firstData = new Date(firstDataStr);
190
191	$("#local_data_from_" + id).val(moment(firstData).format("YYYY-MM-DD HH:mm"));
192
193	var target = "chart-container_" + id;
194	var title = json.config.titolo;
195	var only_calculated = json.config.only_calculated;
196	if (json.config.local_filter == true) {
197		// hidden attribute il filtro locale
198		$("#filters-" + id).removeAttr("hidden");
199		//$("#filters-" + id).remove();
200		//const el = document.querySelector('#filters-' + id);
201		//el.classList.remove('d-none');
202	}
203
204	var zoom = json.config.zoom;
205
206	var timezoneTag = "UTC+1";
207
208	if (json.config.timezone != undefined && json.config.timezone!="") {
209		timezoneTag = json.config.timezone;
210	}
211
212	//Asse x di default temporale
213	var xAxis = {
214		title: json.config.x_axis.titolo,
215		type: "datetime",
216		//max: Date.now(),
217		...xAxisExtra,
218	};
219
220	//Definisco il/gli assi y
221	let yAxis = [];
222	let un_mis = "";
223
224	if (only_calculated !== true) {
225		json.config.y_axes.forEach((axe, i) => {
226			var min = null;
227			var max = null;
228			if (axe.max != -1) max = axe.max;
229			if (axe.min != -1) min = axe.min;
230			// un_mis = json.data[i].anagrafica.UnMis;
231
232			if (json.return_data[i] !== undefined && json.return_data[i].length !== 0) {
233				un_mis = json.return_data[i][3][0][5];
234			}
235			yAxis.push({
236				id: "" + axe.id, //è necessario sia una stringa per essere collegato ad una serie
237				title: axe.titolo,
238				min: min,
239				max: max,
240				opposite: axe.opposite,
241				reversed: axe.reversed,
242				minTickInterval: axe.min_tick_interval,
243				labels: {
244					format: "{value} " + un_mis,
245				},
246			});
247		});
248	} else {
249		un_mis = json.return_data[0][3][0][5];
250	}
251
252	let watermark = "";
253	if (typeof json.config.watermark !== "undefined") {
254		watermark = json.config.watermark;
255	}
256
257	//Se in una o più serie è richiesto il calcolo della cumulata, è necessario aggiungere una serie ed abbinarla ad un asse y dedicato (uno per tutte le cumulate)
258	let show_y_axe_cumulata = false;
259	let y_axe_cumulata = {
260		title: "Cumulata",
261		id: "asse_cum",
262		opposite: only_calculated === true ? false : true,
263		labels: {
264			format: "{value} " + un_mis,
265		},
266	};
267
268	//Se in una o più serie è richiesto il calcolo dell scostamento, è necessario aggiungere una serie ed abbinarla ad un asse y dedicato (uno per tutte le cumulate)
269	let show_y_axe_scostamento = false;
270	let y_axe_scostamento = {
271		title: "Scostamento",
272		id: "asse_sco",
273		opposite: only_calculated === true ? false : true,
274		labels: {
275			format: "{value} " + un_mis,
276		},
277	};
278
279	//Definisco la/le serie
280	let series = [];
281	// let data = json.data;
282	let data = json.return_data;
283
284	const seriesScostamento = {};
285	const seriesCumulata = {};
286	const tempSeries = [];
287
288	json.config.series.forEach((serie, i) => {
289		var type_text = "line";
290		var asse_y_id = serie.id_asse_y;
291		// var un_mis_serie = json.data[i].anagrafica.UnMis;
292		var un_mis_serie = json.return_data[i][3][0][5];
293		var decimalNumber = json.return_data[i][3][0][2];
294		
295		//Imposto il tipo serie
296		switch (serie.type) {
297			case 1:
298				type_text = "line";
299				break;
300			case 2:
301				type_text = "area";
302				break;
303			case 3:
304				type_text = "column";
305				break;
306			default:
307				type_text = "line";
308		}
309		
310		let data_serie = null;
311		let zoneAxis = "";
312		let zones = [];
313
314		if (typeof serie.tipologia !== "undefined" && serie.tipologia === 2) {
315			data_serie = createHighchartsBarSerieArrayVersion(data[i][0], null, data[i][3][0], serie.nome, 0, 3);
316			series.push(data_serie);
317		} else if (only_calculated !== true) {
318			data_serie = createHighchartsDataSerieArray(data[i][0], decimalNumber);
319			
320		
321
322			series.push({
323				name: serie.nome,
324				type: type_text,
325				yAxis: "" + asse_y_id, //è necessario sia una stringa, se è int lo interpreta come posizione dell'array.
326				color: serie.color,
327				data: data_serie,
328				tooltip: {
329					valueDecimals: 2,
330					valueSuffix: " " + un_mis_serie,
331				},
332				zoneAxis: zoneAxis,
333				zones: zones,
334				marker: {
335					enabled: false,
336				},
337			});
338		}
339
340		//Assegno l'unità di misura all'asse corrispondente
341		yAxis = yAxis.map((obj) => {
342			const tickInterval = Number(json.config.tick_interval);
343			if (tickInterval && !Number.isNaN(tickInterval) && tickInterval > 0) {
344				obj.tickInterval = tickInterval;
345			}
346
347			if (obj.id == asse_y_id) {
348				return {
349					...obj,
350					labels: {
351						format: "{value} " + un_mis_serie,
352					},
353				};
354			}
355
356			return obj;
357		});
358
359		//Verifico se è richiesto il calcolo della cumulata, se sì imposto lo show_y_axe_cumulata e creo la serie
360		if (serie.show_cumulata) {
361			show_y_axe_cumulata = true;
362
363			var data_serie_cumulata = createHighchartsDataSerieArray(data[i][1], decimalNumber);
364			seriesCumulata[serie.id] = data_serie_cumulata;
365			
366			//L'asse y a cui abbinarla sarà l'ultimo (in questa fase non ancora creato), cioè quello dedicato a tutte le cumulate.
367			// var asse_y_pos = yAxis.length;
368			series.push({
369				name: serie.nome + " Cumulata",
370				type: "line",
371				yAxis: "asse_cum",
372				color: serie.color,
373				data: data_serie_cumulata,
374				tooltip: {
375					valueDecimals: 1,
376					valueSuffix: " " + un_mis_serie,
377				},
378				marker: {
379					enabled: false,
380				},
381			});
382		}
383
384		//Verifico se è richiesto il calcolo dello scostamento, se sì imposto lo show_y_axe_scostamento e creo la serie
385		if (serie.show_scostamento) {
386			show_y_axe_scostamento = true;
387
388			var data_serie_scostamento = createHighchartsDataSerieArray(data[i][2], decimalNumber);
389			seriesScostamento[serie.id] = data_serie_scostamento;
390			
391			//L'asse y a cui abbinarla sarà l'ultimo (in questa fase non ancora creato), cioè quello dedicato a tutte le cumulate.
392			//var asse_y_pos = yAxis.length;
393			series.push({
394				name: serie.nome + " Scostamento",
395				type: "line",
396				yAxis: "asse_sco",
397				color: serie.color,
398				data: data_serie_scostamento,
399				tooltip: {
400					valueDecimals: 1,
401					valueSuffix: " " + un_mis_serie,
402				},
403				marker: {
404					enabled: false,
405				},
406			});
407		}
408	});
409
410	if (show_y_axe_cumulata) {
411		yAxis.push(y_axe_cumulata);
412	}
413	if (show_y_axe_scostamento) {
414		yAxis.push(y_axe_scostamento);
415	}
416
417	let plotBands = [];
418	if (json.config.plot_bands && json.config.plot_bands.length > 0 && yAxis.length > 0) {
419		plotBands = json.config.plot_bands;
420		yAxis[0].plotBands = plotBands;
421	}
422
423	Highcharts.chart(target, {
424		title: {
425			text: `
426            <h4 class="card-label pt-2">
427                <span class="d-flex text-dark fw-bolder" id="card_title_GraficoMultiserie_44_55">${title}<p class="h6 text-muted mt-1 mb-0 ms-2">${timezoneTag}</p></span>
428                
429            </h4>
430            `,
431			align: "left",
432			useHTML: true,
433			margin: 30,
434			// style: { color: "transparent", },
435		},
436		chart: {
437			zoomType: zoom,
438			plotBackgroundImage: watermark,
439		},
440		time: {
441			//timezone: "Europe/Rome",
442		},
443		xAxis: [xAxis],
444		yAxis: yAxis,
445		tooltip: {
446			shared: true,
447			// formatter: function () {
448
449			//     return this.points.reduce(function (s, point) {
450			// 		let suff = typeof (point.series.options.tooltip.valueSuffix) !== 'undefined' ? point.series.options.tooltip.valueSuffix : '';
451			//         let decimal = typeof (point.series.options.tooltip.valueDecimals) !== 'undefined'
452			// 			? point.series.options.tooltip.valueDecimals
453			//             : 1;
454			//         return s + '<br/><span style="color:' + point.series.color + '">\u25CF</span>' + point.series.name + ': <b>' +
455			//             Highcharts.numberFormat(point.y,decimal) + suff + '</b>';
456			//     }, '' + Highcharts.dateFormat('%d/%m/%Y %H:%M',
457			//         new Date(this.x)) + '');
458			// }
459			formatter: function () {
460				return this.points.reduce(function (s, point) {
461					let suff = "";
462					if (typeof point.series.options.tooltip !== "undefined") {
463						suff = typeof point.series.options.tooltip.valueSuffix !== "undefined" ? point.series.options.tooltip.valueSuffix : "";
464					}
465
466					/*if (point.series.name.includes("Cumulata")) {
467                        return s + '<br/><span style="color:' + point.series.color + '">\u25CF</span>' + point.series.name + ": <b>" + point.y.toFixed(1) + suff + "</b>";
468                    } else {
469                        return s + '<br/><span style="color:' + point.series.color + '">\u25CF</span>' + point.series.name + ": <b>" + point.y + suff + "</b>";
470                    }*/
471					return s + '<br/><span style="color:' + point.series.color + '">\u25CF</span>' + point.series.name + ": <b>" + point.y + suff + "</b>";
472				}, "" + new Date(this.x).toLocaleString("it-IT", { timeZone: "Europe/Rome" }));
473			},
474		},
475
476		series: series,
477		credits: {
478			enabled: false,
479		},
480		exporting: {
481			sourceWidth: 1920,
482			sourceHeight: 1080,
483			scale: 1,
484		},
485	});
486
487	// $("#card_title_" + id).html(title);
488	if (json.config.show_table) {
489        $("#multiseries-table-container-" + id).removeClass("d-none");
490		createTableMultiSeries(id, json, seriesScostamento, seriesCumulata);
491	}
492}
493
494const multiserieTables = {};
495function createTableMultiSeries(widgetId, json, seriesScostamento = {}, seriesCumulata = {}) {
496	const seriesInfo = json.config.series;
497	const seriesData = json.return_data.map((serie) => [serie[0], serie[3][0][5]]);
498	const columns = getColumnTableMultiseries();
499
500	var timezoneTag = "UTC+1";
501
502	if (json.config.timezone != undefined && json.config.timezone != "") {
503		timezoneTag = json.config.timezone;
504	}
505
506	const dataByTimestamp = {};
507	seriesData.forEach(([serie, _], i) => {
508		const id = `field-${seriesInfo[i].id}`;
509		serie.forEach((point, pointIndex) => {
510			const timestamp = point[0];
511			if (!dataByTimestamp[timestamp]) {
512				dataByTimestamp[timestamp] = {};
513			}
514
515			if (json.config.only_calculated !== true) {
516				dataByTimestamp[timestamp][id] = isNaN(point[1]) ? "" : point[1];
517			}
518
519
520			if (seriesScostamento[seriesInfo[i].id]) {
521				dataByTimestamp[timestamp][`${id}_scostamento`] = isNaN(seriesScostamento[seriesInfo[i].id][pointIndex][1]) 
522					? "" 
523					: seriesScostamento[seriesInfo[i].id][pointIndex][1];
524			}
525
526			if (seriesCumulata[seriesInfo[i].id]) {
527				dataByTimestamp[timestamp][`${id}_cumulata`] = isNaN(seriesCumulata[seriesInfo[i].id][pointIndex][1]) 
528					? "" 
529					: seriesCumulata[seriesInfo[i].id][pointIndex][1];
530			}
531
532		});
533	});
534	
535	const timestamps = Object.keys(dataByTimestamp).sort((a, b) => new Date(b) - new Date(a));
536	const data = timestamps.map((timestamp) => {
537		const row = dataByTimestamp[timestamp];
538		row.timestamp = timestamp;
539		return row;
540	});
541	
542	if (multiserieTables[widgetId]) {
543		// Check if element is in the DOM
544		const [element, grid] = multiserieTables[widgetId];
545		if (document.body.contains(element)) {
546			grid.setGridOption("rowData", data);
547			return;
548		}
549	}
550
551	const gridOptions = {
552		columnDefs: [{ field: "timestamp", headerName: "Data " + timezoneTag, maxWidth: 200 }, ...columns],
553		defaultColDef: {
554			// flex: 1,
555			// minWidth: 150,\
556
557
558			sortable: true,
559			resizable: true,
560		},
561		getContextMenuItems: (params) => {
562			const defaultItems = ['copy', 'copyWithHeaders'];
563			return defaultItems; // Nessuna voce export
564		},
565		rowData: data,
566		pagination: true,
567		paginationPageSize: 10,
568		paginationPageSizeSelector: [10, 25, 50, 100],
569	};
570
571	const gridDiv = document.querySelector(`#multiseries-table-${widgetId}`);
572	multiserieTables[widgetId] = [gridDiv, agGrid.createGrid(gridDiv, gridOptions)];
573
574	// === FUNCTIONS ===
575	function getColumnTableMultiseries() {
576		let columns = [];
577
578		if (json.config.only_calculated !== true) {
579			columns = seriesInfo.map((serie, index) => {
580				return { field: `field-${serie.id}`, headerName: `${serie.nome} [${seriesData[index][1]}]` };
581			});
582		}
583
584		seriesInfo.forEach((serie, index) => {
585			if (serie.show_cumulata) {
586				columns.push({ field: `field-${serie.id}_cumulata`, headerName: `${serie.nome} Cumulata [${seriesData[index][1]}]` });
587			}
588			if (serie.show_scostamento) {
589				columns.push({ field: `field-${serie.id}_scostamento`, headerName: `${serie.nome} Scostamento [${seriesData[index][1]}]` });
590			}
591		});
592		return columns;
593	}
594}

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.