diff --git a/public/js/appChart.js b/public/js/appChart.js
index f78f42eef122adf0a07953a053f6cb58a2eced65..5fffe09ad7a2840eb87351772b3f3f2ebe505373 100644
--- a/public/js/appChart.js
+++ b/public/js/appChart.js
@@ -24,6 +24,8 @@ import { drawColumnChartHighcharts } from "./src_modules/chartColumn.mjs";
import {
showLoadingSpinner,
hideLoadingSpinner,
+ disableDrawChartButton,
+ enableDrawChartButton,
} from "./src_modules/loadingIndicator.mjs";
import { vanillaSelectBox } from "./thirdparty/vanillaSelectBox.mjs";
@@ -138,7 +140,7 @@ const drawChartUsingSelectedOptions = async function () {
const selectedOptionsAllDropDownLists =
getSelectedOptionsFromAllDropDownLists();
- // Note: The aggregation type + duration and chart type are the first and
+ // Note: The chart type amd aggregation type + duration are the first and
// third elements respectively, we have ignored the second and fourth elements
const [selectedChartTypeArr, , selectedAggregationTypeDurationArr] =
selectedOptionsAllDropDownLists;
@@ -178,71 +180,74 @@ const drawChartUsingSelectedOptions = async function () {
);
// Create copies of the arrays of building(s) + data point(s) + sampling rate
- const selectedBuildingsDataPointsSamplingRateAbbrevNonComputedCopyArr = [
+ const selectedBuildingsDataPointsSamplingRateAbbrevRawObsCopyArr = [
...selectedBuildingsDataPointsSamplingRateAbbrevNestedArr,
];
- const selectedBuildingsDataPointsSamplingRateAbbrevComputedCopyArr = [
+ const selectedBuildingsDataPointsSamplingRateAbbrevTempDiffCopyArr = [
...selectedBuildingsDataPointsSamplingRateAbbrevNestedArr,
];
- // Check if we have non-computed
- const selectedBuildingsDataPointsSamplingRateAbbrevNonComputedArr =
+ // Check if we have raw observations
+ const selectedBuildingsDataPointsSamplingRateAbbrevRawObsArr =
checkIfSelectedOptionsContainTemperatureDifference(
- selectedBuildingsDataPointsSamplingRateAbbrevNonComputedCopyArr
+ selectedBuildingsDataPointsSamplingRateAbbrevRawObsCopyArr
)
? deleteTemperatureDifferenceOptions(
- selectedBuildingsDataPointsSamplingRateAbbrevNonComputedCopyArr
+ selectedBuildingsDataPointsSamplingRateAbbrevRawObsCopyArr
)
- : selectedBuildingsDataPointsSamplingRateAbbrevNonComputedCopyArr;
+ : selectedBuildingsDataPointsSamplingRateAbbrevRawObsCopyArr;
- // Check if we have computed / dT
- const selectedBuildingsDataPointsSamplingRateAbbrevComputedArr =
+ // Check if we have dT (temperature difference)
+ const selectedBuildingsDataPointsSamplingRateAbbrevTempDiffArr =
checkIfSelectedOptionsContainTemperatureDifference(
- selectedBuildingsDataPointsSamplingRateAbbrevComputedCopyArr
+ selectedBuildingsDataPointsSamplingRateAbbrevTempDiffCopyArr
)
? extractTemperatureDifferenceOptions(
- selectedBuildingsDataPointsSamplingRateAbbrevComputedCopyArr
+ selectedBuildingsDataPointsSamplingRateAbbrevTempDiffCopyArr
)
: [];
// Display the loading indicator
showLoadingSpinner();
- // Fetch the observations + metadata / non-computed
- const observationsPlusMetadataNonComputedArr =
- selectedBuildingsDataPointsSamplingRateAbbrevNonComputedArr.length === 0
+ // Disable the 'draw chart' button
+ disableDrawChartButton();
+
+ // Fetch the observations + metadata / raw observations
+ const observationsRawPlusMetadataArr =
+ selectedBuildingsDataPointsSamplingRateAbbrevRawObsArr.length === 0
? [[], []]
: await getMetadataPlusObservationsFromSingleOrMultipleDatastreams(
BASE_URL,
QUERY_PARAMS_COMBINED,
- selectedBuildingsDataPointsSamplingRateAbbrevNonComputedArr
+ selectedBuildingsDataPointsSamplingRateAbbrevRawObsArr
);
- // Fetch the observations + metadata / computed (dT)
- const observationsPlusMetadataComputedArr =
- selectedBuildingsDataPointsSamplingRateAbbrevComputedArr.length === 0
+ // Fetch the observations + metadata / temperature difference (dT)
+ const observationsTempDiffPlusMetadataArr =
+ selectedBuildingsDataPointsSamplingRateAbbrevTempDiffArr.length === 0
? [[], []]
: await calculateVorlaufMinusRuecklaufTemperature(
BASE_URL,
QUERY_PARAMS_COMBINED,
extractBuildingPlusSamplingRate(
- selectedBuildingsDataPointsSamplingRateAbbrevComputedArr
+ selectedBuildingsDataPointsSamplingRateAbbrevTempDiffArr
)
);
- // Extract the combined arrays for observations and metadata / non-computed
- const [observationsNestedNonComputedArr, metadataNestedNonComputedArr] =
- observationsPlusMetadataNonComputedArr;
+ // Extract the combined arrays for observations and metadata / raw observations
+ const [observationsRawNestedArr, metadataTempDiffNestedArr] =
+ observationsRawPlusMetadataArr;
- // Extract the combined arrays for observations and metadata / computed (dT)
+ // Extract the combined arrays for observations and metadata / temperature difference (dT)
const [observationsNestedComputedArr, metadataNestedComputedArr] =
- observationsPlusMetadataComputedArr;
+ observationsTempDiffPlusMetadataArr;
// Create a combined array of observations and metadata
const observationsPlusMetadataCombined = [
- [...observationsNestedNonComputedArr, ...observationsNestedComputedArr],
- [...metadataNestedNonComputedArr, ...metadataNestedComputedArr],
+ [...observationsRawNestedArr, ...observationsNestedComputedArr],
+ [...metadataTempDiffNestedArr, ...metadataNestedComputedArr],
];
const [observationsComboNestedArr, metadataComboNestedArr] =
@@ -253,8 +258,8 @@ const drawChartUsingSelectedOptions = async function () {
(metadataObj) => formatDatastreamMetadataForChart(metadataObj)
);
- // Extract the formatted metadata properties - used by ALL chart types
- const extractedFormattedDatastreamProperties =
+ // Extract the formatted metadata properties for the raw observations - used by ALL chart types
+ const rawObservationsExtractedFormattedDatastreamProperties =
extractPropertiesFromFormattedDatastreamMetadata(
formattedMetadataNestedArr,
false
@@ -272,111 +277,125 @@ const drawChartUsingSelectedOptions = async function () {
const aggregationStartDate = "2020-01-01";
const aggregationEndDate = "2020-12-31";
- // Extract observations within the user-specified start and end date - used by MULTIPLE chart types
- const observationsAggregationNestedArr = observationsComboNestedArr.map(
- (obsArr) =>
- extractObservationsWithinDatesInterval(
- obsArr,
- "60min",
- aggregationStartDate,
- aggregationEndDate
- )
- );
+ // Create final array of observations- used by MULTIPLE chart types
+ // If we are performing aggregation, it was envisioned that the user would
+ // select observations that fall within a start and end date
+ const observationsComboNestedFinalArr =
+ selectedAggregationType === "None (raw data)"
+ ? observationsComboNestedArr
+ : observationsComboNestedArr.map((observationsArr) =>
+ extractObservationsWithinDatesInterval(
+ observationsArr,
+ selectedSamplingRateAbbrev,
+ aggregationStartDate,
+ aggregationEndDate
+ )
+ );
// Unique calendar dates - used by MULTIPLE chart types
- const uniqueCalendarDatesNestedArr = observationsAggregationNestedArr.map(
+ const uniqueCalendarDatesNestedArr = observationsComboNestedFinalArr.map(
(observationsArr) =>
extractUniqueCalendarDatesFromTimestamp(observationsArr)
);
for (const selectedChartType of selectedChartTypeArr) {
- if (selectedChartType === "Heatmap") {
- // We are interested in raw observations
- if (
- selectedAggregationOptionsAreValid &&
- selectedBuildingDataPointsOptionsAreValid
- ) {
- drawHeatmapBasedOnSelectedOptions(
- observationsComboNestedArr,
- extractedFormattedDatastreamProperties
- );
- }
- }
-
- if (selectedChartType === "Scatter Plot") {
- // We are interested in raw observations
- if (
- selectedAggregationOptionsAreValid &&
- selectedBuildingDataPointsOptionsAreValid
- ) {
- drawScatterPlotFromChartSelection(
- observationsComboNestedArr,
- extractedFormattedDatastreamProperties
- );
- }
- }
-
- if (selectedChartType === "Line") {
- // We are interested in raw observations or aggregated observations
-
- // Raw observations
- if (selectedAggregationType === "None (raw data)") {
- // Create formatted array(s) for observations
- const formattedRawObservationsLineChartNestedArr =
- observationsComboNestedArr.map((observationsArr) =>
- formatSensorThingsApiResponseForLineOrColumnChart(observationsArr)
+ switch (selectedChartType) {
+ // We are interested in raw observations ONLY
+ case "Heatmap":
+ if (
+ selectedAggregationOptionsAreValid &&
+ selectedBuildingDataPointsOptionsAreValid
+ ) {
+ drawHeatmapBasedOnSelectedOptions(
+ observationsComboNestedFinalArr,
+ rawObservationsExtractedFormattedDatastreamProperties
);
-
- drawLineChartHighcharts(
- formattedRawObservationsLineChartNestedArr,
- extractedFormattedDatastreamProperties
- );
- }
- // Aggregated observations
- else {
- drawLineChartBasedOnSelectedAggregationOptions(
- selectedAggregationType,
- selectedAggregationDuration,
- observationsAggregationNestedArr,
- selectedSamplingRateAbbrev,
- uniqueCalendarDatesNestedArr,
- formattedMetadataNestedArr
- );
- }
- }
-
- if (selectedChartType === "Column") {
- // We are interested in raw observations or aggregated observations
-
- // Raw observations
- if (selectedAggregationType === "None (raw data)") {
- // Create formatted array(s) for observations
- const formattedRawObservationsColumnChartNestedArr =
- observationsComboNestedArr.map((observationsArr) =>
- formatSensorThingsApiResponseForLineOrColumnChart(observationsArr)
+ }
+ break;
+
+ // We are interested in raw observations ONLY
+ case "Scatter Plot":
+ if (
+ selectedAggregationOptionsAreValid &&
+ selectedBuildingDataPointsOptionsAreValid
+ ) {
+ drawScatterPlotFromChartSelection(
+ observationsComboNestedFinalArr,
+ rawObservationsExtractedFormattedDatastreamProperties
+ );
+ }
+ break;
+
+ // We are interested in BOTH raw observations and aggregated observations
+ case "Line":
+ // Raw observations
+ if (selectedAggregationType === "None (raw data)") {
+ // Create formatted array(s) for observations
+ const formattedRawObservationsLineChartNestedArr =
+ observationsComboNestedFinalArr.map((observationsArr) =>
+ formatSensorThingsApiResponseForLineOrColumnChart(
+ observationsArr
+ )
+ );
+
+ drawLineChartHighcharts(
+ formattedRawObservationsLineChartNestedArr,
+ rawObservationsExtractedFormattedDatastreamProperties
);
+ }
+ // Aggregated observations
+ else {
+ drawLineChartBasedOnSelectedAggregationOptions(
+ selectedAggregationType,
+ selectedAggregationDuration,
+ observationsComboNestedFinalArr,
+ selectedSamplingRateAbbrev,
+ uniqueCalendarDatesNestedArr,
+ formattedMetadataNestedArr
+ );
+ }
+ break;
+
+ // We are interested in BOTH raw observations and aggregated observations
+ case "Column":
+ // Raw observations
+ if (selectedAggregationType === "None (raw data)") {
+ // Create formatted array(s) for observations
+ const formattedRawObservationsColumnChartNestedArr =
+ observationsComboNestedFinalArr.map((observationsArr) =>
+ formatSensorThingsApiResponseForLineOrColumnChart(
+ observationsArr
+ )
+ );
+
+ drawColumnChartHighcharts(
+ formattedRawObservationsColumnChartNestedArr,
+ rawObservationsExtractedFormattedDatastreamProperties
+ );
+ }
+ // Aggregated observations
+ else {
+ drawColumnChartBasedOnSelectedAggregationOptions(
+ selectedAggregationType,
+ selectedAggregationDuration,
+ observationsComboNestedFinalArr,
+ selectedSamplingRateAbbrev,
+ uniqueCalendarDatesNestedArr,
+ formattedMetadataNestedArr
+ );
+ }
+ break;
- drawColumnChartHighcharts(
- formattedRawObservationsColumnChartNestedArr,
- extractedFormattedDatastreamProperties
- );
- }
- // Aggregated observations
- else {
- drawColumnChartBasedOnSelectedAggregationOptions(
- selectedAggregationType,
- selectedAggregationDuration,
- observationsAggregationNestedArr,
- selectedSamplingRateAbbrev,
- uniqueCalendarDatesNestedArr,
- formattedMetadataNestedArr
- );
- }
+ default:
+ throw new Error("None of the chart type options were selected");
}
}
} catch (err) {
console.error(err);
} finally {
+ // Enable the 'draw chart' button
+ enableDrawChartButton();
+
// Hide the loading indicator
hideLoadingSpinner();
}
diff --git a/public/js/src_modules/aggregate.mjs b/public/js/src_modules/aggregate.mjs
index 324716daf646328d6ac8a25ef40ec38a9a799b5a..02f5998067cca546ae6434e59558a19fe5b7f6be 100644
--- a/public/js/src_modules/aggregate.mjs
+++ b/public/js/src_modules/aggregate.mjs
@@ -5,6 +5,16 @@ import {
extractObservationValuesWithinMonthInterval,
} from "./aggregateHelpers.mjs";
+/**
+ * Remove `null` observation values from an array of observation values
+ *
+ * @param {Array} obsValuesArr An array of observation values
+ * @returns {Array} An array with `null` observation values removed
+ */
+const filterOutNullObservationValues = function (obsValuesArr) {
+ return obsValuesArr.filter((obs) => obs !== null);
+};
+
/**
* Calculate the minimum observation values that fall within a time interval delimited by a start date and end date
* @param {Array} obsValuesForDaysIntervalArr An array of observation values that fall within our time interval
@@ -13,7 +23,10 @@ import {
const calculateMinimumObservationValuesWithinDatesInterval = function (
obsValuesForDaysIntervalArr
) {
- return Math.min(...obsValuesForDaysIntervalArr);
+ // If the observation value is `null`, skip the calculation
+ return obsValuesForDaysIntervalArr.reduce((previousValue, obsValue) =>
+ obsValue === null ? null : Math.min(previousValue, obsValue)
+ );
};
/**
@@ -24,7 +37,10 @@ const calculateMinimumObservationValuesWithinDatesInterval = function (
const calculateMaximumObservationValuesWithinDatesInterval = function (
obsValuesForDaysIntervalArr
) {
- return Math.max(...obsValuesForDaysIntervalArr);
+ // If the observation value is `null`, skip the calculation
+ return obsValuesForDaysIntervalArr.reduce((previousValue, obsValue) =>
+ obsValue === null ? null : Math.max(previousValue, obsValue)
+ );
};
/**
@@ -35,8 +51,10 @@ const calculateMaximumObservationValuesWithinDatesInterval = function (
const calculateSumOfObservationValuesWithinDatesInterval = function (
obsValuesForDaysIntervalArr
) {
- return obsValuesForDaysIntervalArr.reduce(
- (accumulator, obsValue) => accumulator + obsValue
+ // Remove the `null` observation values before calculating the sum
+ return filterOutNullObservationValues(obsValuesForDaysIntervalArr).reduce(
+ (previousValue, obsValue) => previousValue + obsValue,
+ 0
);
};
@@ -48,10 +66,11 @@ const calculateSumOfObservationValuesWithinDatesInterval = function (
const calculateAverageOfObservationValuesWithinDatesInterval = function (
obsValuesForDaysIntervalArr
) {
+ // The observation values array should only include non-`null` values
return (
calculateSumOfObservationValuesWithinDatesInterval(
obsValuesForDaysIntervalArr
- ) / obsValuesForDaysIntervalArr.length
+ ) / filterOutNullObservationValues(obsValuesForDaysIntervalArr).length
);
};
@@ -63,7 +82,10 @@ const calculateAverageOfObservationValuesWithinDatesInterval = function (
const calculateMinimumObservationValuesWithinMonthInterval = function (
obsValuesForMonthIntervalArr
) {
- return Math.min(...obsValuesForMonthIntervalArr);
+ // If the observation value is `null`, skip the calculation
+ return obsValuesForMonthIntervalArr.reduce((previousValue, obsValue) =>
+ obsValue === null ? null : Math.min(previousValue, obsValue)
+ );
};
/**
@@ -74,7 +96,10 @@ const calculateMinimumObservationValuesWithinMonthInterval = function (
const calculateMaximumObservationValuesWithinMonthInterval = function (
obsValuesForMonthIntervalArr
) {
- return Math.max(...obsValuesForMonthIntervalArr);
+ // If the observation value is `null`, skip the calculation
+ return obsValuesForMonthIntervalArr.reduce((previousValue, obsValue) =>
+ obsValue === null ? null : Math.max(previousValue, obsValue)
+ );
};
/**
@@ -85,8 +110,10 @@ const calculateMaximumObservationValuesWithinMonthInterval = function (
const calculateSumOfObservationValuesWithinMonthInterval = function (
obsValuesForMonthIntervalArr
) {
- return obsValuesForMonthIntervalArr.reduce(
- (accumulator, obsValue) => accumulator + obsValue
+ // Remove the `null` observation values before calculating the sum
+ return filterOutNullObservationValues(obsValuesForMonthIntervalArr).reduce(
+ (previousValue, obsValue) => previousValue + obsValue,
+ 0
);
};
@@ -98,10 +125,11 @@ const calculateSumOfObservationValuesWithinMonthInterval = function (
const calculateAverageOfObservationValuesWithinMonthInterval = function (
obsValuesForMonthIntervalArr
) {
+ // The observation values array should only include non-`null` values
return (
calculateSumOfObservationValuesWithinMonthInterval(
obsValuesForMonthIntervalArr
- ) / obsValuesForMonthIntervalArr.length
+ ) / filterOutNullObservationValues(obsValuesForMonthIntervalArr).length
);
};
diff --git a/public/js/src_modules/chartColumn.mjs b/public/js/src_modules/chartColumn.mjs
index 8c6de145fc19df5f711ea814670f90fc88e72428..35fc3d5203968d2f5f2e1338f99fad622f3771e7 100644
--- a/public/js/src_modules/chartColumn.mjs
+++ b/public/js/src_modules/chartColumn.mjs
@@ -131,10 +131,12 @@ const drawColumnChartHighcharts = function (
title: {
text: textChartTitle,
+ "align": "center",
},
subtitle: {
text: textChartSubtitle,
+ "align": "center",
},
xAxis: {
@@ -143,7 +145,6 @@ const drawColumnChartHighcharts = function (
},
yAxis: {
- min: 0,
title: {
text: `${phenomenonName} [${unitOfMeasurementSymbol}]`,
},
@@ -155,10 +156,10 @@ const drawColumnChartHighcharts = function (
// this.x -- common for all points
// this.points -- an array containing properties for each series
// Note that our `reduce` method is in this format:
- // ((accumulator, currentValue, currentIndex) => {...}, initialValue)
+ // ((previousValue, currentValue, currentIndex) => {...}, initialValue)
return this.points.reduce(
- (accumulator, point, i) =>
- `${accumulator}
${
+ (previousValue, point, i) =>
+ `${previousValue}
${
point.series.name
}: ${point.y.toFixed(2)} ${
unitOfMeasurementSymbolsArr[i]
diff --git a/public/js/src_modules/chartHeatmap.mjs b/public/js/src_modules/chartHeatmap.mjs
index 69d77f55038a4342d778596988377d3efebc5553..87884e0af04ab2f6e1124e33ac437c6a9ff942ab 100644
--- a/public/js/src_modules/chartHeatmap.mjs
+++ b/public/js/src_modules/chartHeatmap.mjs
@@ -48,7 +48,8 @@ const calculateMinMaxValuesForHeatmapColorAxis = function (
const maxValue = Math.trunc(Math.max(...obsValueArr));
// Calculate the closest multiple of 5
- const minObsValue = minValue - (minValue % 5);
+ const minObsValue =
+ minValue > 0 ? minValue - (minValue % 5) : minValue + (minValue % 5);
const maxObsValue = maxValue + (5 - (maxValue % 5));
return { minObsValue, maxObsValue };
@@ -98,13 +99,13 @@ const drawHeatMapHighcharts = function (
title: {
text: TEXT_CHART_TITLE,
- align: "left",
+ align: "center",
x: 40,
},
subtitle: {
text: TEXT_CHART_SUBTITLE,
- align: "left",
+ align: "center",
x: 40,
},
diff --git a/public/js/src_modules/chartLine.mjs b/public/js/src_modules/chartLine.mjs
index ba9a2e157d65af2f5aee6da8379e4d027f57cd6f..2538662fd06e9f6e517d4cc5a89b52f57564e000 100644
--- a/public/js/src_modules/chartLine.mjs
+++ b/public/js/src_modules/chartLine.mjs
@@ -134,12 +134,12 @@ const drawLineChartHighcharts = function (
title: {
text: textChartTitle,
- "align": "left",
+ "align": "center",
},
subtitle: {
text: textChartSubtitle,
- align: "left",
+ align: "center",
},
tooltip: {
diff --git a/public/js/src_modules/chartScatterPlot.mjs b/public/js/src_modules/chartScatterPlot.mjs
index 0cc2bdb3ceaffe731cdc0922e08f7cd038c02efc..e84e6118f365ecbb91b62defe8d9838c10dea958 100644
--- a/public/js/src_modules/chartScatterPlot.mjs
+++ b/public/js/src_modules/chartScatterPlot.mjs
@@ -231,12 +231,12 @@ const drawScatterPlotHighcharts = function (
title: {
text: CHART_TITLE,
- "align": "left",
+ "align": "center",
},
subtitle: {
text: CHART_SUBTITLE,
- "align": "left",
+ "align": "center",
},
xAxis: {
diff --git a/public/js/src_modules/dropDownListHelpers.mjs b/public/js/src_modules/dropDownListHelpers.mjs
index f087cdd06569cbedcc16a9940e250bf018f70678..b505d7e198d17c8eacb630f6ac737ef89e442204 100644
--- a/public/js/src_modules/dropDownListHelpers.mjs
+++ b/public/js/src_modules/dropDownListHelpers.mjs
@@ -1,3 +1,5 @@
+"use strict";
+
/**
* Get the selected option(s) from a dropdown list
*
diff --git a/public/js/src_modules/dropDownListProcessing.mjs b/public/js/src_modules/dropDownListProcessing.mjs
index 47715ca69cbd55c46012d6b032b02742e309cd28..123db15692211ef5bd3ae1f03bc595156d0232e5 100644
--- a/public/js/src_modules/dropDownListProcessing.mjs
+++ b/public/js/src_modules/dropDownListProcessing.mjs
@@ -1,3 +1,5 @@
+"use strict";
+
import { drawLineChartHighcharts } from "./chartLine.mjs";
import {
diff --git a/public/js/src_modules/loadingIndicator.mjs b/public/js/src_modules/loadingIndicator.mjs
index c8465f1339fe71c88fc3c7936b3174ebfe1cee5f..c1964b2c02c2d2e713c26f5d1b35b87bcda86fb6 100644
--- a/public/js/src_modules/loadingIndicator.mjs
+++ b/public/js/src_modules/loadingIndicator.mjs
@@ -2,7 +2,7 @@
/**
* Show a loading indicator at the start of an async task. The indicator consists of a spinner and a transluscent mask placed on top of page elements
- * @returns {undefined}
+ * @returns {undefined} undefined
*/
const showLoadingSpinner = function () {
const loadingIndicatorMask = document.querySelector("#loadingIndicator");
@@ -14,7 +14,7 @@ const showLoadingSpinner = function () {
/**
* Hide the loading indicator after completion of the async tasks
- * @returns {undefined}
+ * @returns {undefined} undefined
*/
const hideLoadingSpinner = function () {
const loadingIndicatorMask = document.querySelector("#loadingIndicator");
@@ -24,4 +24,27 @@ const hideLoadingSpinner = function () {
loadingIconSpinner.style.display = "none";
};
-export { showLoadingSpinner, hideLoadingSpinner };
+/**
+ * Disable the button used to trigger the drawing of charts
+ *
+ * @returns {undefined} undefined
+ */
+const disableDrawChartButton = function () {
+ document.querySelector("#btn-draw-chart").disabled = true;
+};
+
+/**
+ * Enable the button used to trigger the drawing of charts
+ *
+ * @returns {undefined} undefined
+ */
+const enableDrawChartButton = function () {
+ document.querySelector("#btn-draw-chart").disabled = false;
+};
+
+export {
+ showLoadingSpinner,
+ hideLoadingSpinner,
+ disableDrawChartButton,
+ enableDrawChartButton,
+};
diff --git a/public/js/thirdparty/scripts.js b/public/js/thirdparty/scripts.js
index 0b2fd5c5ed6032910542854db6b28d81dfa80be8..82e16ea4385b253a0567468f7e29f478c7080bf2 100644
--- a/public/js/thirdparty/scripts.js
+++ b/public/js/thirdparty/scripts.js
@@ -1,22 +1,27 @@
/*!
- * Start Bootstrap - SB Admin v6.0.2 (https://startbootstrap.com/template/sb-admin)
- * Copyright 2013-2020 Start Bootstrap
- * Licensed under MIT (https://github.com/StartBootstrap/startbootstrap-sb-admin/blob/master/LICENSE)
- */
- (function($) {
- "use strict";
+ * Start Bootstrap - SB Admin v7.0.3 (https://startbootstrap.com/template/sb-admin)
+ * Copyright 2013-2021 Start Bootstrap
+ * Licensed under MIT (https://github.com/StartBootstrap/startbootstrap-sb-admin/blob/master/LICENSE)
+ */
+//
+// Scripts
+//
- // Add active state to sidbar nav links
- var path = window.location.href; // because the 'href' property of the DOM element is the absolute path
- $("#layoutSidenav_nav .sb-sidenav a.nav-link").each(function() {
- if (this.href === path) {
- $(this).addClass("active");
- }
- });
-
- // Toggle the side navigation
- $("#sidebarToggle").on("click", function(e) {
- e.preventDefault();
- $("body").toggleClass("sb-sidenav-toggled");
+window.addEventListener("DOMContentLoaded", (event) => {
+ // Toggle the side navigation
+ const sidebarToggle = document.body.querySelector("#sidebarToggle");
+ if (sidebarToggle) {
+ // Uncomment Below to persist sidebar toggle between refreshes
+ // if (localStorage.getItem('sb|sidebar-toggle') === 'true') {
+ // document.body.classList.toggle('sb-sidenav-toggled');
+ // }
+ sidebarToggle.addEventListener("click", (event) => {
+ event.preventDefault();
+ document.body.classList.toggle("sb-sidenav-toggled");
+ localStorage.setItem(
+ "sb|sidebar-toggle",
+ document.body.classList.contains("sb-sidenav-toggled")
+ );
});
-})(jQuery);
+ }
+});