From 05ac1b569ea5145661d74ab2c72ebffb34ecd4e9 Mon Sep 17 00:00:00 2001
From: Pithon Kabiro <pithon.kabiro@hft-stuttgart.de>
Date: Thu, 30 Sep 2021 14:05:00 +0200
Subject: [PATCH] Make further edits to markup for drop-down lists

---
 index.html                |  33 +++++--
 public/js/dropDownList.js | 195 +++++++++++++++++++++++++++++++++++---
 2 files changed, 206 insertions(+), 22 deletions(-)

diff --git a/index.html b/index.html
index 5997c90..d51f644 100644
--- a/index.html
+++ b/index.html
@@ -186,7 +186,10 @@
                           ><strong>Aggregation (Type, Duration)</strong></span
                         >
                         <div class="nowrap">
-                          <select id="drop-down--aggregation-type">
+                          <!-- We need the `multiple` attribute for the dropdowns even if 
+                          we do not need to support multiple selections. This seems to 
+                          be a quirk of the `vanillaSelectBox` library -->
+                          <select id="drop-down--aggregation-type" multiple>
                             <option>None (raw data)</option>
                             <option>Sum/Daily</option>
                             <option>Sum/Monthly</option>
@@ -203,7 +206,10 @@
                       <div id="drop-down--sampling-rate-parent">
                         <span><strong>Sampling rate</strong></span>
                         <div class="nowrap">
-                          <select id="drop-down--sampling-rate">
+                          <!-- We need the `multiple` attribute for the dropdowns even if 
+                          we do not need to support multiple selections. This seems to 
+                          be a quirk of the `vanillaSelectBox` library -->
+                          <select id="drop-down--sampling-rate" multiple>
                             <option>15 min</option>
                             <option>60 min</option>
                           </select>
@@ -213,7 +219,10 @@
                       <div id="drop-down--chart-type-parent">
                         <span><strong>Chart type</strong></span>
                         <div class="nowrap">
-                          <select id="drop-down--chart-type">
+                          <!-- We need the `multiple` attribute for the dropdowns even if 
+                          we do not need to support multiple selections. This seems to 
+                          be a quirk of the `vanillaSelectBox` library -->
+                          <select id="drop-down--chart-type" multiple>
                             <option>Column</option>
                             <option>Line</option>
                             <option>Heatmap</option>
@@ -249,7 +258,7 @@
                 <div class="card mb-4">
                   <div class="card-header">
                     <i class="fas fa-chart-line mr-1"></i>
-                    Line Chart Example
+                    Line Chart
                   </div>
                   <div class="card-body">
                     <div id="chart-line" width="100%" height="40"></div>
@@ -260,7 +269,7 @@
                 <div class="card mb-4">
                   <div class="card-header">
                     <i class="fas fa-chart-area mr-1"></i>
-                    Area Chart Example
+                    Heatmap
                   </div>
                   <div class="card-body">
                     <div id="chart-heatmap" width="100%" height="40"></div>
@@ -274,7 +283,7 @@
                   <div class="card-header">
                     <!-- No free scatter plot icon available; use this instead -->
                     <i class="fas fa-braille mr-1"></i>
-                    Scatter Plot Example
+                    Scatter Plot
                   </div>
                   <div class="card-body">
                     <div id="chart-scatter-plot" width="100%" height="40"></div>
@@ -285,7 +294,7 @@
                 <div class="card mb-4">
                   <div class="card-header">
                     <i class="far fa-chart-bar mr-1"></i>
-                    Bar Chart Example
+                    Column Chart
                   </div>
                   <div class="card-body">
                     <div id="chart-column" width="100%" height="40"></div>
@@ -302,9 +311,15 @@
             >
               <div class="text-muted">Copyright &copy; HfT Stuttgart 2021</div>
               <div>
-                <a href="#">Privacy Policy</a>
+                <a href="https://www.hft-stuttgart.de/impressum" target="_blank"
+                  >Impressum</a
+                >
                 &middot;
-                <a href="#">Terms &amp; Conditions</a>
+                <a
+                  href="https://www.hft-stuttgart.de/datenschutz"
+                  target="_blank"
+                  >Datenschutz</a
+                >
               </div>
             </div>
           </div>
diff --git a/public/js/dropDownList.js b/public/js/dropDownList.js
index 14cde18..b3526de 100644
--- a/public/js/dropDownList.js
+++ b/public/js/dropDownList.js
@@ -52,7 +52,10 @@ const styleLevelOneDropDown = function () {
 const styleLevelTwoDropDown = function () {
   // Create our dropdown list using `vanillaSelectBox`
   new vanillaSelectBox("#drop-down--aggregation-type", {
+    "disableSelectAll": true,
+    "maxSelect": 1,
     "placeHolder": "--Select--",
+    "search": false,
   });
 };
 
@@ -64,7 +67,10 @@ const styleLevelTwoDropDown = function () {
 const styleLevelThreeDropDown = function () {
   // Create our dropdown list using `vanillaSelectBox`
   new vanillaSelectBox("#drop-down--sampling-rate", {
+    "disableSelectAll": true,
+    "maxSelect": 1,
     "placeHolder": "--Select--",
+    "search": false,
   });
 };
 
@@ -76,7 +82,10 @@ const styleLevelThreeDropDown = function () {
 const styleLevelFourDropDown = function () {
   // Create our dropdown list using `vanillaSelectBox`
   new vanillaSelectBox("#drop-down--chart-type", {
+    "disableSelectAll": true,
+    "maxSelect": 1,
     "placeHolder": "--Select--",
+    "search": false,
   });
 };
 
@@ -94,24 +103,118 @@ const afterDocumentLoads = function () {
 };
 
 /**
- * Get the values from the currently selected options in the linked drop dpwn lists
- * @returns {Array} An array containing the values of the selected options
+ * Get the selected option(s) from a dropdown list
+ *
+ * @param {String} selectorStr A CSS selector string representing the dropdown list
+ * @returns {Array} An array of string(s) representing the value(s) of the selected `<option>` elements
+ */
+const getSelectedOptionsFromDropDownList = function (selectorStr) {
+  // Array to store our final result
+  const selectedOptionsArr = [];
+
+  // Select all the matching <option> elements as a NodeList
+  const optionElements = document.querySelectorAll(`${selectorStr} option`);
+
+  optionElements.forEach((optionEl) => {
+    if (optionEl.selected) {
+      selectedOptionsArr.push(optionEl.value);
+    }
+  });
+
+  return selectedOptionsArr;
+};
+
+/**
+ * Process the selected option(s) from a buildings & data points dropdown list.
+ * This is currently the first dropdown list in the UI.
+ *
+ * @param {Array} selectedOptionsArr An array of string(s) representing the value(s) of the selected `<option>` elements
+ * @returns {Array} An array of string(s) representing the processed value(s) of the selected buildings & data points option(s)
+ */
+const processSelectionsFromBuildingDataPointOptions = function (
+  selectedOptionsArr
+) {
+  // Array to store our final result
+  const selectedOptionsBuildingDataPointArr = [];
+
+  selectedOptionsArr.forEach((optionStr) => {
+    if (optionStr.includes("/")) {
+      // Case 1: <option> element's value CONTAINS a "/" character
+      // We wish to create a string like this `Bau 101/VL`
+
+      // Split the <option> element's value into two substrings
+      const optionsStrPartOne = optionStr.slice(0, 3);
+      const optionsStrPartTwo = optionStr.slice(3);
+
+      // Create a new string for the first substring
+      const optionsStrPartOneNew = `Bau ${optionsStrPartOne}`;
+
+      // Create a new combined string
+      const optionsStrNew = optionsStrPartOneNew + optionsStrPartTwo;
+
+      selectedOptionsBuildingDataPointArr.push(optionsStrNew);
+    } else {
+      // Case 2: <option> element's value DOES NOT CONTAIN a "/" character
+      // We wish to create a string like this `Other/Außentemp`
+      selectedOptionsBuildingDataPointArr.push(`Other/${optionStr}`);
+    }
+  });
+
+  return selectedOptionsBuildingDataPointArr;
+};
+
+/**
+ * Split an option element's value (a string) using a forward slash character ("/") as the delimiter
+ *
+ * @param {Array} selectedOptionsArr An array of string(s) representing the value(s) of the selected `<option>` elements
+ * @returns {Array} An array made up of resulting strings after splitting
+ */
+const splitOptionsTextDelimitedBySlash = function (selectedOptionsArr) {
+  return selectedOptionsArr.map((selectedOption) => selectedOption.split("/"));
+};
+
+/**
+ * Get the values from the currently selected options in ALL the drop down lists
+ * @returns {Array} An array containing four arrays, where each array contains the values of the selected options
  */
-const getSelectedOptionsFromDropDownLists = function () {
-  const selectedBuilding = document.querySelector("#drop-down--bldg").value;
-  const selectedSensor = document.querySelector("#drop-down--sensor").value;
-  const selectedSamplingRate = document.querySelector(
+const getSelectedOptionsFromAllDropDownLists = function () {
+  const selectedBuildingDataPointOptionsArr =
+    processSelectionsFromBuildingDataPointOptions(
+      getSelectedOptionsFromDropDownList("#drop-down--bldg-data-point")
+    );
+
+  // Separate the building ID from the data point
+  const selectedBuildingDataPointOptionsSplitArr =
+    splitOptionsTextDelimitedBySlash(selectedBuildingDataPointOptionsArr);
+
+  const selectedAggregationOptionsArr = getSelectedOptionsFromDropDownList(
+    "#drop-down--aggregation-type"
+  );
+
+  const selectedSamplingRateArr = getSelectedOptionsFromDropDownList(
     "#drop-down--sampling-rate"
-  ).value;
+  );
+
+  const selectedChartTypeArr = getSelectedOptionsFromDropDownList(
+    "#drop-down--chart-type"
+  );
+
+  // Ensure that all the options have at least one selection
 
   if (
-    selectedBuilding === "--Select--" ||
-    selectedSensor === "" ||
-    selectedSamplingRate === ""
+    selectedBuildingDataPointOptionsSplitArr.length === 0 ||
+    selectedAggregationOptionsArr.length === 0 ||
+    selectedSamplingRateArr.length === 0 ||
+    selectedChartTypeArr.length === 0
   )
     return;
 
-  return [selectedBuilding, selectedSensor, selectedSamplingRate];
+  return [
+    selectedBuildingDataPointOptionsSplitArr,
+    selectedAggregationOptionsArr,
+    selectedSamplingRateArr,
+    selectedChartTypeArr,
+  ];
 };
 
 /**
@@ -134,14 +237,18 @@ const getBuildingSensorSamplingRateAbbreviation = function (
       "Bau 112": "112, 118",
       "Bau 125": "125",
       "Bau 225": "225",
+      "Other": "weather_station_521",
     },
     phenomenon: {
-      Vorlauftemperatur: "vl",
-      Rücklauftemperatur: "rl",
+      VL: "vl",
+      RL: "rl",
+      dT: "dT",
+
       Durchfluss: "flow",
       Leistung: "power",
       Energie: "energy",
       Energie_VERBR: "energy_verb",
+      Außentemp: "outside_temp",
     },
     samplingRate: {
       "15 min": "15min",
@@ -149,6 +256,29 @@ const getBuildingSensorSamplingRateAbbreviation = function (
     },
   };
 
+  if (
+    fullFormToAbbreviationMapping["buildings"]?.[buildingFullForm] === undefined
+  )
+    throw new Error(
+      "The provided building ID is not valid or is not supported by this function"
+    );
+
+  if (
+    fullFormToAbbreviationMapping["phenomenon"]?.[phenomenonFullForm] ===
+    undefined
+  )
+    throw new Error(
+      "The provided data point is not valid or is not supported by this function"
+    );
+
+  if (
+    fullFormToAbbreviationMapping["samplingRate"]?.[samplingRateFullForm] ===
+    undefined
+  )
+    throw new Error(
+      "The provided sampling rate is not valid or is not supported by this function"
+    );
+
   const buildingAbbrev =
     fullFormToAbbreviationMapping["buildings"]?.[buildingFullForm];
 
@@ -161,6 +291,45 @@ const getBuildingSensorSamplingRateAbbreviation = function (
   return [buildingAbbrev, phenomenonAbbrev, samplingRateAbbrev];
 };
 
+/**
+ * Get the abbreviated form for the currently selected options in ALL the drop down lists
+ *
+ * @param {Array} allSelectedOptionsArr An array containing four arrays, where each array contains the values of the selected options
+ * @returns {Array} An array which contains one or more nested arrays of abbreviations of building(s), data point(s) and sampling rate(s)
+ */
+const getAbbreviationsForSelectedOptionsFromAllDropDownLists = function (
+  allSelectedOptionsArr
+) {
+  // Note: The sampling rate array is the third array, therefore we skip one element
+  const [selectedBuildingDataPointOptionsSplitArr, , selectedSamplingRateArr] =
+    allSelectedOptionsArr;
+
+  // The building is the first element
+  const selectedBuildingsArr = selectedBuildingDataPointOptionsSplitArr.map(
+    (selectedBuildingDataPoint) => selectedBuildingDataPoint[0]
+  );
+
+  // The data point is the second element
+  const selectedDataPointsArr = selectedBuildingDataPointOptionsSplitArr.map(
+    (selectedBuildingDataPoint) => selectedBuildingDataPoint[1]
+  );
+
+  // Assume that the buildings and data points arrays have equal length
+  // use one of the arrays for looping
+  if (selectedBuildingsArr.length !== selectedDataPointsArr.length)
+    throw new Error(
+      "The buildings array and data points array have different lengths"
+    );
+
+  return selectedBuildingsArr.map((selectedBuilding, i) =>
+    getBuildingSensorSamplingRateAbbreviation(
+      selectedBuilding,
+      selectedDataPointsArr[i],
+      ...selectedSamplingRateArr
+    )
+  );
+};
+
 /**
  * Callback function for chart selection using drop down list
  * @returns {undefined}
-- 
GitLab