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.