ExcelToMap.vue

This is where users can convert their files such as CSV, KML, and Google Sheet to map. They can also edit, delete, share, bookmark, export, and publish the map.

import

// Importing Vue components for the NavigationBar and Footer
import NavigationBar from "../reusable-layout/NavigationBar.vue";
import Footer from "../reusable-layout/Footer.vue";
// Importing Axios for making HTTP requests and MarkerClusterer for clustering markers on Google Maps
import axios from "axios";
import { MarkerClusterer } from "@googlemaps/markerclusterer";

Above, we are importing the NavigationBar and Footer component which is a reusable layout because navigation bars and footers are usually same for each page so instead of writing it for each page, it's better to write it in one Vue page and import it in each page as components

Next, is the "axios" package which is used to make API call to the backend (Laravel). It is important because this is like the key that allows us to communicate with the backend.

Finally, is the "MarkerClusterer" package. This package is used to make clusters in the map when there are too many markers in the map. This makes the map less messy and reduces lag.

export default

// Exporting the Vue component
export default {}

Inside this is where we define data, methods, computed properties, and lifecycle hooks.

components

// Register the imported components
components: {
  NavigationBar,
  Footer,
}

The components option is used to register and make Vue external components available for use within this component

data

// Data properties of the component
data() {
  return {
    domainBackend: "",
    domainFrontend: "",
    tableTitle: "",
    allData: [],
    currentData: [],
    selectedMethod: "",
    title: "",
    selectedFile: "",
    googleSheetLink: "",
    showWarningMessage: false,
    map: null,
  };
}

The data method is used to initialize and define the data properties for this component. See the source code to know what each variable is used for.

mounted

// Lifecycle hook: Executed when the component is mounted to the DOM
mounted() {
  // Scroll to the top of the window when the component is mounted
  window.scrollTo(0, 0);
  // Setting backend domain from the Vuex store
  this.domainBackend = this.$store.state.domainBackend;
  // Setting frontend domain from the Vuex store
  this.domainFrontend = this.$store.state.domainFrontend;
  // Check if the user ID cookie is not available
  if (!this.$cookie.isCookieAvailable("L_userId")) {
    // If specific cookies for all data and current data are not available,
    // initialize them with empty arrays
    if (
      !this.$cookie.isCookieAvailable("ETM_allData") ||
      !this.$cookie.isCookieAvailable("ETM_currentData")
    ) {
      this.$cookie.setCookie("ETM_allData", JSON.stringify([]));
      this.$cookie.setCookie("ETM_currentData", JSON.stringify([]));
    } else {
      // If cookies are available, parse them and assign to component data
      this.allData = JSON.parse(this.$cookie.getCookie("ETM_allData"));
      this.currentData = this.$cookie.getCookie("ETM_currentData");
    }
  } else {
    // If user ID cookie is available, fetch map data
    this.getMap();
  }
  // Initialize map and related UI components based on available data
  if (this.allData.length == 0) {
    this.initMap();
    this.updateMapButtons();
    this.tableTitle = "Table";
  } else {
    this.addMarkers(this.allData[0].DATA);
    this.updateTable(this.allData[0].DATA);
    this.updateMapButtons();
    this.tableTitle = this.allData[0].TITLE;
  }
}

This is called mounted lifecycle hook. The mounted hook is called after the component has been added to the DOM, making it a good place to perform initial setup and actions.

To explain what this mounted hook does:

  1. It scrolls the window to the top so the user sees the content at the beginning of the page.

  2. It gets the backend and frontend domain from what is defined in the Vuex.

  3. If the user is logged in, then it will get the JSON representation of the map data array from the backend that is saved by the user. If the user is not logged in, then it will first check if the cookie of the map data array or currently selected map data is saved or not. If saved, then it will get the cookie of the map data array and currently selected map data and initialize it. If not saved, then it will define an empty map data array and map data and save it as the cookie.

  4. If the map data array is empty, then it will just initialize an empty map to display, update the map entries in the selection section, and name the table with its default name.

  5. If the map data array is not empty, then it will choose the first map data in the array to add the markers in the map, populate the table with rows, update the map entries in the selection section, and name the table with name of the map.

computed

computed: {
  generateFileMethod() {
    // Determine the method to generate a file based on the selected method
    if (this.selectedMethod == "csv") {
      // If CSV is selected, check if title and file are set
      // If both are set, return the method to convert CSV to JSON
      if (this.title != "" && this.selectedFile != null) {
        return this.convertCsvToJson;
      }
    } else if (this.selectedMethod == "googleSheet") {
      // If Google Sheet is selected, check if title and sheet link are set
      // If both are set, return the method to convert Google Sheet to JSON
      if (this.title != "" && this.googleSheetLink != "") {
        return this.convertGoogleSheetToJson;
      }
    } else if (this.selectedMethod == "kml") {
      // If KML is selected, check if title and file are set
      // If both are set, return the method to convert KML to JSON
      if (this.title != "" && this.selectedFile != null) {
        return this.convertKmlToJson;
      }
    }
  },
}

This computed: property is used to dynamically determine and return a method based on the values of several component data properties and the selected method.

It check the value of the selectedMethod data property. The selectedMethod property represents the user's choice of a method for generating a file (e.g., "csv," "googleSheet," or "kml").

If selectedMethod is "csv," it checks if the title and selectedFile properties are not empty. If they are not empty, it returns the convertCsvToJson method. Same goes for "googleSheet" and "kml".

The purpose of this computed property is to dynamically select and return a method that should be executed based on the user's selection.

methods

// Methods of the component
methods: {}

The methods option is used to define methods that can be called or triggered within a Vue component. These methods are typically used to define the behavior and functionality of the component

getMap

// Method to fetch map data for the user from the backend API
getMap() {
  // Prepare a request object with the user ID obtained from cookies.
  const request = {
    userId: this.$cookie.getCookie("L_userId"),
  };
  // Send a POST request to the backend API to get map data for the user.
  axios
    .post(this.domainBackend + "/api/getMap", request)
    .then((response) => {
      // Check if the response indicates success.
      if (response.data.SUCCESS == 1) {
        // Deep copy the data received in the response.
        const data = JSON.parse(JSON.stringify(response.data.DATA));
        // Iterate over each item in the data.
        data.forEach((item) => {
          // If an item has the 'WKT' property, delete it.
          if (item.hasOwnProperty("WKT")) {
            delete item.WKT;
          }
        });
        // Create a proxy for the data to handle get and set operations.
        const proxy_data = new Proxy(data, {
          get: function (target, prop) {
            return target[prop];
          },
          set: function (target, prop, value) {
            target[prop] = value;
            return true;
          },
        });
        // Update the allData property of the component with the proxy data.
        this.allData = proxy_data;
        // If there's no data, reset currentData and UI components related to the map.
        if (this.allData.length == 0) {
          this.currentData = [];
          this.addMarkers([]);
          this.updateTable([]);
          this.updateMapButtons();
          this.tableTitle = "TABLE";
        } else {
          // If there is data, set the first dataset as current and update the UI accordingly.
          this.currentData = this.allData[0];
          this.addMarkers(this.allData[0].DATA);
          this.updateTable(this.allData[0].DATA);
          this.updateMapButtons();
          this.tableTitle = this.allData[0].TITLE;
        }
      }
    });
}

To explain what this getMap method does:

  1. It send the user ID of the currently logged in user to the backend. Then, the backend will find the map file that matches the user ID and respond with the JSON content of the file.

  2. It initializes the map data array.

  3. If the map data array is empty, then it will just initialize an empty map to display, update the table rows, update the map entries in the selection section, and name the table with its default name.

  4. If the map data array is not empty, then it will choose the first map data in the array to set it as the map data to be used initially when the page loads, add the markers in the map, populate the table with rows, update the map entries in the selection section, and name the table with name of the map.

storeMap

// Method to store or update the current map state in the backend database
storeMap() {
  // Construct a request payload. This includes:
  // - USER_ID: Retrieved from a cookie, likely representing the current user's identifier.
  // - DATA: The current state of allData, which presumably holds map-related data.
  // The data is converted into a JSON string before sending.
  const request = {
    jsonData: JSON.stringify({
      USER_ID: this.$cookie.getCookie("L_userId"),
      DATA: this.allData,
    }),
  };
  // Make an HTTP POST request using axios to the backend.
  // The endpoint '/api/storeMap' suggests that this is for storing (or updating) map data.
  axios
    .post(this.domainBackend + "/api/storeMap", request)
    .then((response) => {
      // Check the response for a success flag.
      // If the operation was successful (SUCCESS == 1), no further action is taken.
      // This could mean that the front-end does not need to update its state or notify the user upon successful storage.
      // The lack of else clause for handling errors or unsuccessful attempts might be intentional or an oversight.
      if (response.data.SUCCESS == 1) {
        //Do nothing
      }
    });
}

To explain what this storeMap method does:

  1. It sends the JSON representation of the map data array is sent to the backend so that the backend can write the JSON data to the map file of the user.

makeHash

// Method to generate a random hash string of the specified length
makeHash(length) {
  // Initialize an empty string to store the result.
  let result = "";
  // Define a string of characters that the hash can contain.
  // This includes uppercase and lowercase alphabets, and numbers from 0 to 9.
  const characters =
    "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
  // Calculate the length of the characters string for later use.
  const charactersLength = characters.length;
  // Initialize a counter for the while loop.
  let counter = 0;
  // Run a loop until the counter reaches the desired length of the hash.
  while (counter < length) {
    // Append a random character from 'characters' to 'result'.
    // Math.random() generates a random decimal number between 0 and 1.
    // Multiplying this by charactersLength gives a range from 0 to charactersLength.
    // Math.floor() rounds down to the nearest whole number, ensuring it's a valid index for 'characters'.
    // characters.charAt() gets the character at the chosen index.
    result += characters.charAt(
      Math.floor(Math.random() * charactersLength)
    );
    // Increment the counter after adding each character.
    counter += 1;
  }
  // Return the final result, which is a randomly generated string of the specified length.
  return result;
}

To explain what this makeHash method does:

  1. It generates a 32-characters hash. The hash is used to define the ID of the published map file so that each published map file can be identified uniquely.

publishedMap

// Method to publish the current map data for public access if the user is logged in
publishMap() {
  // Check if the user ID cookie is available, which implies that the user is logged in.
  if (this.$cookie.isCookieAvailable("L_userId")) {
    // Create a request object for publishing the map, which includes:
    // - ID: A unique identifier for the map, generated using the makeHash function.
    // - TITLE: The title of the current map data.
    // - USER_ID: The user's ID, retrieved from a cookie.
    // - USERNAME: The user's username, also retrieved from a cookie.
    // - DATA: The current map data to be published.
    const request = {
      jsonData: JSON.stringify({
        ID: this.makeHash(32),
        TITLE: this.currentData.TITLE,
        USER_ID: this.$cookie.getCookie("L_userId"),
        USERNAME: this.$cookie.getCookie("L_username"),
        DATA: this.currentData.DATA,
      }),
    };
    // Send a POST request to the backend to store the published map.
    axios
      .post(this.domainBackend + "/api/storePublishedMap", request)
      .then((response) => {
        // If the response indicates success (SUCCESS == 1), display an alert stating the map has been published.
        if (response.data.SUCCESS == 1) {
          alert("MAP PUBLISHED.");
        }
      });
  } else {
    // If the user ID cookie is not available (user is not logged in), display an alert asking them to log in.
    alert("LOGIN TO PUBLISH THIS MAP TO PUBLIC.");
  }
}

To explain what this publishedMap method does:

  1. It checks whether the user is logged in or not.

  2. If logged in, then it will the send the currently selected map data to the backend so that the backend can make and store the new published map file.

  3. If not logged in, then it will alert the user to log in using an account.

openShareToSocial

// Method to prepare and open a sharing interface for the current map data
openShareToSocial() {
  // Prepare a request object with the title and data of the current map.
  // This data is converted into a JSON string.
  const request = {
    jsonData: JSON.stringify({
      TITLE: this.currentData.TITLE,
      DATA: this.currentData.DATA,
    }),
  };
  // Send a POST request to the backend API to store the map data for sharing.
  // The endpoint '/api/storeSharedMap' suggests that this API is specifically for sharing maps.
  axios
    .post(this.domainBackend + "/api/storeSharedMap", request)
    .then((response) => {
      // Check if the operation was successful based on the response data.
      if (response.data.SUCCESS == 1) {
        // Find the HTML element with the ID 'urlLink'.
        const urlLink = document.getElementById("urlLink");
        // Set the value of this element to a URL constructed using the frontend domain
        // and the filename received in the response. This URL likely points to the shared map.
        urlLink.value =
          this.domainFrontend + "/shared-map/" + response.data.FILENAME;
        // Find the HTML element responsible for showing the share interface/container.
        const containerShareToSocial = document.getElementById(
          "containerShareToSocial"
        );
        // Make the share interface/container visible.
        containerShareToSocial.style.display = "block";
      }
    });
}

To explain what this openShareToSocial method does:

  1. It sends the JSON representation of the currently selected map data to the backend so that the backend can make and store the new shared map file. Then, the backend will return the name of the file which is also the route (URL) to access the shared map file.

  2. After that, a pop-up box will be shown where the user can copy the URL of the shared map.

closeShareToSocial

// Method to close the sharing interface
closeShareToSocial() {
  // Retrieve the HTML element with the ID 'containerShareToSocial'.
  // This element is assumed to be the container for the share interface.
  const containerShareToSocial = document.getElementById(
    "containerShareToSocial"
  );
  // Set the display style of this element to 'none', effectively hiding it from view.
  // This is a common way to toggle visibility in web development.
  // When this method is called, it will close or hide the share interface.
  containerShareToSocial.style.display = "none";
}

To explain what this closeShareToSocial method does:

  1. It will the close the pop-up box.

downloadJson

// Method to download the current map data as a JSON file
downloadJson() {
  // Create a deep copy of the current map data.
  // This prevents modifying the original data structure when changes are made.
  const data = JSON.parse(JSON.stringify(this.currentData.DATA));
  // Iterate over each item in the copied data.
  data.forEach((item) => {
    // Remove the LATITUDE and LONGITUDE properties from each item.
    // This suggests that these properties are not needed in the downloaded JSON.
    delete item.LATITUDE;
    delete item.LONGITUDE;
  });
  // Convert the modified data array back into a JSON string with pretty-printing (indentation of 2 spaces).
  const json = JSON.stringify(data, null, 2);
  // Create a new Blob object, representing data as a JSON file.
  // Blobs are used to handle raw data such as files in web applications.
  const blob = new Blob([json], { type: "application/json" });
  // Generate a URL for the Blob object.
  // This URL will be used to trigger the file download.
  const url = window.URL.createObjectURL(blob);
  // Create an anchor (<a>) element programmatically.
  const a = document.createElement("a");
  // Set the href of the anchor to the blob URL and define the filename for the download.
  // The filename uses the current map's title with a .json extension.
  a.href = url;
  a.download = this.currentData.TITLE + ".json";
  // Add the anchor to the document body.
  // This is necessary to trigger the download action.
  document.body.appendChild(a);
  // Programmatically click the anchor to trigger the file download.
  a.click();
  // Release the created blob URL to free up resources.
  // This is good practice to avoid memory leaks.
  window.URL.revokeObjectURL(url);
  // Remove the anchor from the document body since it's no longer needed.
  document.body.removeChild(a);
}

To explain what this downloadJson method does:

  1. It will get the currently selected map data and convert it to a BLOB object that encapsulates the JSON data.

  2. Then, it programmatically initiates the download of the file.

convertJsonToGoogleSheet

// Method to convert JSON format map data to a Google Sheet
convertJsonToGoogleSheet() {
  // Store the backend domain in a variable for later use.
  const domainBackend = this.domainBackend;
  // Get the title of the current data.
  const title = this.currentData.TITLE;
  // Create a deep copy of the current data and remove latitude and longitude from each item.
  const data = JSON.parse(JSON.stringify(this.currentData.DATA));
  data.forEach((item) => {
    delete item.LATITUDE;
    delete item.LONGITUDE;
  });
  // Convert the modified data to a JSON string.
  const jsonData = JSON.stringify(data);
  // Define properties for a new popup window (used for Google authentication).
  const popupProperties = "width=500,height=800,scrollbars=yes";
  // Open a new popup window. Initially, it's empty.
  let windowPopUp = window.open("", "Google", popupProperties);
  // Make a GET request to authenticate the user's Google account for accessing Google Sheets.
  axios
    .get(
      this.domainBackend + "/api/authenticateGoogleAccountForGoogleSheet"
    )
    .then((response) => {
      // Check if the authentication process was successful.
      if (response.data.SUCCESS == 1) {
        // Redirect the popup window to the Google authentication URL provided by the backend.
        const url = response.data.AUTH_URL;
        windowPopUp.location.href = url;

        // Monitor the URL of the popup for changes, indicating successful authentication.
        monitorUrlChanges();
      }
    });
  // Function to monitor changes in the URL of the popup window.
  function monitorUrlChanges() {
    // Set an interval to check the URL every 1000 milliseconds (1 second).
    const interval = setInterval(() => {
      // Check if the popup window is open and not closed.
      if (windowPopUp && !windowPopUp.closed) {
        // Get the current URL of the popup window.
        const urlPopUp = windowPopUp.location.href;
        // If the URL contains a specific query parameter (code), proceed with data sending.
        if (urlPopUp.includes("?code=")) {
          // Extract the 'code' parameter from the URL.
          const params = new URLSearchParams(urlPopUp.split("?")[1]);
          const code = params.get("code");
          // Call the function to send the JSON data along with the extracted code.
          sendJson(code);
          // Clear the interval as it's no longer needed.
          clearInterval(interval);
        }
      } else {
        // If the popup window is closed, clear the interval.
        clearInterval(interval);
      }
    }, 1000);
  }
  // Function to send the JSON data to the backend for conversion to Google Sheets.
  function sendJson(code) {
    // Prepare the request payload with the title, JSON data, and the code for Google Sheets access.
    const request = {
      title: title,
      jsonData: jsonData,
      code: code,
    };
    // Make a POST request to send the data for conversion.
    axios
      .post(domainBackend + "/api/convertJsonToGoogleSheet", request)
      .then((response) => {
        // Check if the conversion process was successful.
        if (response.data.SUCCESS == 1) {
          // Close the popup window as it's no longer needed.
          windowPopUp.close();
          // Open a new tab/window with the link to the created Google Sheet.
          window.open(response.data.LINK, "_blank").focus();
        }
      });
  }
}

To explain what this convertJsonToGoogleSheet method does:

  1. It retrieves an authentication URL which is used to authenticate the user's Google account. It is needed because the converted Google Sheet file will be saved in the user's Google Drive.

  2. The authentication URL will be opened in a pop-up window.

  3. Then, the changes that happens in the URL bar of the pop-up window is monitored because after the user has authenticated their account, Google will send a code which is then used to do the Google Sheets conversion.

  4. After the code has been given by Google, the JSON representation of the currently selected map data, the map title, and the code given by Google is sent to the backend.

  5. After converting the JSON data to Google Sheets, the backend will respond with a link to the Google Sheets file.

  6. The link is automatically opened in a new tab.

convertJsonToCsv

// Method to convert JSON format map data to a CSV file
convertJsonToCsv() {
  // Create a deep copy of the current map data.
  // This prevents modifying the original data structure directly.
  const data = JSON.parse(JSON.stringify(this.currentData.DATA));
  // Iterate over each item in the copied data and remove the latitude and longitude properties.
  // This suggests that these properties are not needed in the CSV file.
  data.forEach((item) => {
    delete item.LATITUDE;
    delete item.LONGITUDE;
  });
  // Prepare the request payload for the API call.
  // This includes the modified data in JSON format.
  const request = {
    jsonData: JSON.stringify(data),
  };
  // Make an HTTP POST request using axios to the backend for converting the JSON data to CSV.
  axios
    .post(this.domainBackend + "/api/convertJsonToCsv", request)
    .then((response) => {
      // Check if the response indicates a successful conversion.
      if (response.data.SUCCESS == 1) {
        // Create a new Blob object representing the CSV data.
        // Blobs are used for handling raw data such as files in web applications.
        const blob = new Blob([response.data.DATA], { type: "text/csv" });
        // Generate a URL for the Blob object.
        // This URL will be used to trigger the file download.
        const url = window.URL.createObjectURL(blob);
        // Create an anchor (<a>) element programmatically.
        const a = document.createElement("a");
        // Set the href of the anchor to the blob URL and define the filename for the download.
        // The filename uses the current map's title with a .csv extension.
        a.href = url;
        a.download = this.currentData.TITLE + ".csv";
        // Add the anchor to the document body.
        // This is necessary to trigger the download action.
        document.body.appendChild(a);
        // Programmatically click the anchor to trigger the file download.
        a.click();
        // Release the created blob URL to free up resources.
        // This is a good practice to avoid memory leaks.
        window.URL.revokeObjectURL(url);
        // Remove the anchor from the document body since it's no longer needed.
        document.body.removeChild(a);
      }
    });
}

To explain what this convertJsonToCsv method does:

  1. It sends the JSON representation of the currently selected map data to the backend.

  2. The backend will respond with a CSV representation of the currently selected map data then it converts it to a BLOB object that encapsulates the CSV data.

  3. Then, it programmatically initiates the download of the file.

openExportMap

// Method to open an interface for exporting the current map data
openExportMap() {
  // Retrieve the HTML element with the ID 'containerExportMap'.
  // This element is assumed to be the container for the export map interface or functionality.
  const containerExportMap = document.getElementById("containerExportMap");
  // Set the display style of this element to 'block', making it visible on the webpage.
  // This is a common way to toggle visibility in web development.
  // When this method is called, it will show the export map interface or container on the page.
  containerExportMap.style.display = "block";
}

To explain what this openExportMap method does:

  1. It shows a pop-up box where the user can choose whether to export the currently selected map data to CSV file or Google Sheets file.

closeExportMap

// Method to close the export map interface
closeExportMap() {
  // Retrieve the HTML element with the ID 'containerExportMap'.
  // This element is assumed to be the container for the export map interface or functionality.
  const containerExportMap = document.getElementById("containerExportMap");
  // Set the display style of this element to 'none', effectively hiding it from view.
  // This is a common method to toggle visibility in web development.
  // When this method is called, it will hide the export map interface or container on the page.
  containerExportMap.style.display = "none";
}

To explain what this closeExportMap method does:

  1. It will the close the pop-up box.

deleteMap

// Method to delete a map from the user's collection based on the provided index
deleteMap(index) {
  // Iterate over the allData array, which contains all the map data.
  for (let i = 0; i < this.allData.length; i++) {
    // Check if the current index matches the specified index to delete.
    if (i == index) {
      // Remove the map at the specified index from allData.
      this.allData.splice(i, 1);
      // Update currentData to the first map in the updated allData array.
      // This acts as a reset to the current view after deletion.
      this.currentData = this.allData[0];
    }
  }
  // Check if allData is now empty after deletion.
  if (this.allData.length == 0) {
    // If allData is empty, reinitialize the map to its default state.
    this.initMap();
    // Update the table, map buttons, and the table title to reflect the absence of map data.
    this.updateTable([]);
    this.updateMapButtons();
    this.tableTitle = "Table";
  } else {
    // If allData is not empty, update the map markers, table, and buttons to reflect the new current data.
    this.addMarkers(this.currentData.DATA);
    this.updateTable(this.currentData.DATA);
    this.updateMapButtons();
    this.tableTitle = this.currentData.TITLE;
  }
  // Check if the user is logged in (indicated by the availability of a user ID cookie).
  if (this.$cookie.isCookieAvailable("L_userId")) {
    // If logged in, store the updated map data to the backend or server.
    this.storeMap();
  } else {
    // If not logged in, update the allData cookie with the new allData array.
    // This suggests that for non-logged-in users, map data is stored locally in cookies.
    this.$cookie.setCookie("ETM_allData", JSON.stringify(this.allData));
  }
}

To explain what this deleteMap method does:

  1. When the user clicks on the delete icon in the map selection section to delete that particular map data, the index of the map data that resides in the map data array goes to this function.

  2. Then, the map data array is looped until the index matches.

  3. Once the index matches, the map data will be removed from the map data array.

  4. If the map data array is empty, then it will just initialize an empty map to display, populate the table with rows, update the map entries in the selection section, and name the table with its default name.

  5. If the map data array is not empty, then it will choose the first map data in the array to set it as the map data to be used initially when the page loads, add the markers in the map, populate the table with rows, update the map entries in the selection section, and name the table with name of the map.

  6. if the user is logged in, the JSON representation of the map data array is sent to the backend so that the backend can write the JSON data to the map file of the user.

  7. If the user is not logged in, then the map data array is saved to the cookie.

updateMapButtons

// Method to update the UI with buttons for each map in the user's collection
updateMapButtons() {
  // Retrieve the HTML element that will contain the map buttons.
  const mapButtonsContainer = document.getElementById(
    "map-buttons-container"
  );
  // Clear any existing content in the map buttons container.
  mapButtonsContainer.innerHTML = "";
  // Iterate over the allData array, which contains data for each map.
  for (let i = 0; i < this.allData.length; i++) {
    // Create a new div element for each map. This will contain the map button and delete icon.
    const firstDiv = document.createElement("div");
    firstDiv.classList.add("d-flex", "justify-content-between");
    firstDiv.style.display = "flex";
    firstDiv.style.borderLeft = "3px solid white";
    firstDiv.style.borderBottom = "3px solid white";
    // Create a clickable anchor tag for each map, setting its text to the map's title.
    const aTagInsideSecondDiv = document.createElement("a");
    aTagInsideSecondDiv.classList.add("nav-item", "nav-link", "text-white");
    aTagInsideSecondDiv.innerText = this.allData[i].TITLE;
    aTagInsideSecondDiv.style.cursor = "pointer";
    // Add an event listener to the anchor tag.
    // When clicked, it will update various elements in the UI with data from the clicked map.
    aTagInsideSecondDiv.addEventListener("click", () => {
      this.tableTitle = this.allData[i].TITLE;
      this.addMarkers(this.allData[i].DATA);
      this.updateTable(this.allData[i].DATA);
      this.currentData = this.allData[i];
      this.$cookie.setCookie(
        "ETM_currentData",
        JSON.stringify(this.currentData)
      );
    });
    // Create a container div for the anchor tag and append the anchor tag to it.
    const secondDiv = document.createElement("div");
    secondDiv.style.width = "70%";
    secondDiv.appendChild(aTagInsideSecondDiv);
    // Append this container to the main div.
    firstDiv.appendChild(secondDiv);
    // Create a container div for the delete icon.
    const thirdDiv = document.createElement("div");
    // Create the delete icon as an <i> element.
    const iconElement = document.createElement("i");
    iconElement.classList.add("fa", "fa-trash", "text-white");
    iconElement.style.cursor = "pointer";
    iconElement.style.marginRight = "10px";
    iconElement.style.marginTop = "10px";
    // Add an event listener to the delete icon.
    // When clicked, it will call the deleteMap method with the current index.
    iconElement.addEventListener("click", () => {
      this.deleteMap(i);
    });
    // Append the delete icon to its container.
    thirdDiv.appendChild(iconElement);
    // Append the delete icon container to the main div.
    firstDiv.appendChild(thirdDiv);
    // Finally, append the main div to the map buttons container.
    // This adds the new button and delete icon for each map in allData to the UI.
    mapButtonsContainer.appendChild(firstDiv);
  }
}

To explain what this updateMapButtons method does:

  1. It updates the map selection section with a list of buttons. Each button represents a map data from the map data array. Each button is created dynamically and includes a title, a link that triggers various actions when clicked (which is populating the table with rows, adding markers to the map, and setting cookies), and a delete icon that allows removing the associated map data from the array.

toggleSection

// Method to toggle visibility of a UI section based on the selected method
toggleSection() {
  // Check if the selected method is 'googleSheet'.
  if (this.selectedMethod === "googleSheet") {
    // If the selected method is 'googleSheet', set 'showWarningMessage' to true.
    // This likely triggers the display of a warning message in the UI.
    // The warning message might be specific to the use of Google Sheets functionality.
    this.showWarningMessage = true;
  } else {
    // If the selected method is not 'googleSheet', set 'showWarningMessage' to false.
    // This likely hides the warning message in the UI.
    this.showWarningMessage = false;
  }
}

To explain what this toggleSection method does:

  1. It shows a warning message if the selected method for conversion is "googleSheet" and hides it if otherwise.

handleFileChange

// Method to handle file input change events for file uploads
handleFileChange(event) {
  // Update the 'selectedFile' property of the component.
  // 'event.target.files' is an array-like object of all the files selected by the user.
  // '[0]' accesses the first file in this collection.
  // This assumes that only the first file selected by the user is relevant for the application's purposes.
  this.selectedFile = event.target.files[0];
}

To explain what this handleFileChange method does:

  1. It sets the selectedFile property to the file that a user selects in an input element on the page.

initMap

// Method to initialize the Google Map with default settings
initMap() {
  // Create a new Google Map instance.
  // 'document.getElementById("map")' refers to the HTML element where the map will be displayed.
  // This requires an HTML element with the ID 'map' to be present in the DOM.
  this.map = new google.maps.Map(document.getElementById("map"), {
    // Set the initial center of the map. Here, it's set to latitude 0 and longitude 0 (the geographical center of the Earth).
    center: { lat: 0, lng: 0 },
    // Set the initial zoom level of the map. '2' is quite zoomed out, showing a large portion of the globe.
    zoom: 2,
    // Initialize an empty array for markers.
    // This suggests that the application might add markers to the map later on.
    markers: [],
  });
}

To explain what this initMap method does:

  1. It initializes a Google Map on the page.

convertGoogleSheetToJson

// Method to convert data from a Google Sheet link to JSON format
convertGoogleSheetToJson() {
  // Prepare the request payload with the Google Sheet link and a title.
  const request = {
    googleSheetLink: this.googleSheetLink, // URL of the Google Sheet.
    title: this.title, // Title for the operation or the data.
  };
  // Send a POST request to the backend API endpoint to convert the Google Sheet data to JSON.
  axios
    .post(this.domainBackend + "/api/convertGoogleSheetToJson", request)
    .then((response) => {
      // Check if the conversion was successful.
      if (response.data.SUCCESS == 1) {
        // Create an object representing the converted data.
        const x = {
          TITLE: response.data.TITLE, // Title of the data, possibly extracted from the Google Sheet.
          DATA: response.data.DATA, // The actual converted data in JSON format.
        };
        // Add the converted data object to the 'allData' array, which stores all map data.
        this.allData.push(x);
        // Set 'currentData' to the newly added data, updating the current view or state.
        this.currentData = x;
        // Update various components of the UI with the new data.
        this.addMarkers(x.DATA); // Add markers to the map based on the new data.
        this.updateTable(x.DATA); // Update the data table.
        this.updateMapButtons(); // Update the map buttons.
        this.tableTitle = x.TITLE; // Update the title of the table.
        // Check if the user is logged in (indicated by the availability of a user ID cookie).
        if (this.$cookie.isCookieAvailable("L_userId")) {
          // If logged in, store the updated map data to the backend or server.
          this.storeMap();
        } else {
          // If not logged in, update the local cookies with the new data.
          // This suggests that for non-logged-in users, map data is stored locally in cookies.
          this.$cookie.setCookie(
            "ETM_allData",
            JSON.stringify(this.allData)
          );
          this.$cookie.setCookie(
            "ETM_currentData",
            JSON.stringify(this.currentData)
          );
        }
      } else if (response.data.SUCCESS == 0) {
        // If the conversion failed (e.g., due to an invalid URL), alert the user.
        alert("INVALID URL.");
      }
    });
}

To explain what this convertGoogleSheetToJson method does:

  1. It sends the map title and the Google Sheets file link to the backend.

  2. The backend will respond with the JSON representation of the Google Sheets.

  3. Then, the map data array and the currently selected map data is initialized with the JSON representation.

  4. After that, the JSON representation is used to add the markers in the map, populate the table with rows, update the map entries in the selection section, and name the table with name of the map.

  5. Then, the map data array and the currently selected map data are saved to the cookie.

  6. If the user is logged in, then the JSON representation of the map data array is sent to the backend so that the backend can write the JSON data to the map file of the user.

// Method to convert data from a CSV file to JSON format
convertCsvToJson() {
  // Create a new FormData object, which is used to send form data as key-value pairs in a request.
  const formData = new FormData();
  // Append the selected file (a CSV file) to the form data.
  // 'this.selectedFile' is expected to be a file object selected by the user.
  formData.append("file", this.selectedFile);
  // Append the title associated with the file/data to the form data.
  formData.append("title", this.title);
  // Make a POST request to the backend API endpoint to convert the CSV file to JSON.
  // The formData containing the file and title is sent as the request body.
  axios
    .post(this.domainBackend + "/api/convertCsvToJson", formData)
    .then((response) => {
      // Check if the conversion was successful.
      if (response.data.SUCCESS == 1) {
        // Update the map markers with the converted data.
        // This implies that the converted data includes information for placing markers on a map.
        this.addMarkers(response.data.DATA);
        // Update the data table with the converted data.
        this.updateTable(response.data.DATA);
        // Create an object representing the converted data.
        const x = {
          TITLE: response.data.TITLE, // Title of the data, possibly from the CSV file or the response.
          DATA: response.data.DATA, // The actual converted data in JSON format.
        };
        // Add the converted data object to the 'allData' array, which stores all map-related data.
        this.allData.push(x);
        // Set 'currentData' to the newly added data, updating the current view or state.
        this.currentData = x;
        // Update various components of the UI with the new data.
        this.updateMapButtons(); // Update the map buttons.
        this.tableTitle = x.TITLE; // Update the title of the table.
        // Update the local cookies with the new data, which is used for persistence.
        this.$cookie.setCookie("ETM_allData", JSON.stringify(this.allData));
        this.$cookie.setCookie(
          "ETM_currentData",
          JSON.stringify(this.currentData)
        );
        // If the user is logged in, store the updated map data on the backend or server.
        if (this.$cookie.isCookieAvailable("L_userId")) {
          this.storeMap();
        }
      }
    });
}

To explain what this convertCsvToJson method does:

  1. It sends the map title and the CSV file to the backend.

  2. The backend will respond with the JSON representation of the CSV.

  3. After that, the JSON representation is used to add the markers in the map, populate the table with rows, update the map entries in the selection section, and name the table with name of the map.

  4. Then, the map data array and the currently selected map data is initialized with the JSON representation.

  5. Next, the map data array and the currently selected map data are saved to the cookie.

  6. If the user is logged in, then the JSON representation of the map data array is sent to the backend so that the backend can write the JSON data to the map file of the user.

convertKmlToJson

// Method to convert data from a KML file to JSON format
convertKmlToJson() {
  // Initialize a FormData object to prepare data for the HTTP request.
  // FormData is used for constructing key-value pairs representing form fields and their values.
  const formData = new FormData();
  // Append the selected KML file to the form data.
  // 'this.selectedFile' is expected to be a KML file chosen by the user.
  formData.append("file", this.selectedFile);
  // Append the title associated with the file/data to the form data.
  formData.append("title", this.title);
  // Make a POST request to the backend API endpoint to convert the KML file to JSON.
  // The formData containing the file and title is sent as the request body.
  // Additional headers are set to indicate the content type of the request.
  axios
    .post(this.domainBackend + "/api/convertKmlToJson", formData, {
      headers: {
        "Content-Type": "multipart/form-data",
      },
    })
    .then((response) => {
      // Check if the conversion was successful.
      if (response.data.SUCCESS == 1) {
        // Update the map markers with the converted data.
        // This suggests that the converted data includes information for placing markers on a map.
        this.addMarkers(response.data.DATA);
        // Update the data table with the converted data.
        this.updateTable(response.data.DATA);
        // Create an object representing the converted data.
        const x = {
          TITLE: response.data.TITLE, // Title of the data, possibly from the KML file or the response.
          DATA: response.data.DATA, // The actual converted data in JSON format.
        };
        // Add the converted data object to the 'allData' array, which stores all map-related data.
        this.allData.push(x);
        // Set 'currentData' to the newly added data, updating the current view or state.
        this.currentData = x;
        // Update various components of the UI with the new data.
        this.updateMapButtons(); // Update the map buttons.
        this.tableTitle = x.TITLE; // Update the title of the table.
        // Update the local cookies with the new data, which is used for persistence.
        this.$cookie.setCookie("ETM_allData", JSON.stringify(this.allData));
        this.$cookie.setCookie(
          "ETM_currentData",
          JSON.stringify(this.currentData)
        );
        // If the user is logged in, store the updated map data on the backend or server.
        if (this.$cookie.isCookieAvailable("L_userId")) {
          this.storeMap();
        }
      }
    });
}

To explain what this convertKmlToJson method does:

  1. It sends the map title and the KML file to the backend.

  2. The backend will respond with the JSON representation of the KML.

  3. After that, the JSON representation is used to add the markers in the map, populate the table with rows, update the map entries in the selection section, and name the table with name of the map.

  4. Then, the map data array and the currently selected map data is initialized with the JSON representation.

  5. Next, the map data array and the currently selected map data are saved to the cookie.

  6. If the user is logged in, then the JSON representation of the map data array is sent to the backend so that the backend can write the JSON data to the map file of the user.

addMarkers

// Method to add markers to the map based on the provided data
addMarkers(data) {
  // Check if the data is not empty or null. If so, proceed with adding markers.
  if (data.length != 0 || data == null) {
    // Reset the map to null and reinitialize it.
    this.map = null;
    this.initMap();
    // Initialize an array to hold the marker objects.
    const markers = [];
    let currentInfoWindow = null; // Variable to keep track of the currently open info window.
    // Loop through each place in the data array.
    data.forEach((place) => {
      // Create a new marker for each place.
      const marker = new google.maps.Marker({
        position: {
          lat: parseFloat(place.LATITUDE), // Parse the latitude.
          lng: parseFloat(place.LONGITUDE), // Parse the longitude.
        },
        map: this.map, // Set the map where the marker should be added.
        title: place.NAME, // Set the title for the marker.
      });
      // Add a click listener to each marker.
      marker.addListener("click", () => {
        // Close the currently open info window, if there is one.
        if (currentInfoWindow) {
          currentInfoWindow.close();
        }
        // Create the content for the info window.
        // This includes creating HTML elements dynamically and setting their properties.
        // Elements include a title, coordinates display, and interactive buttons like share, locate, edit, and bookmark.
        const mainDiv = document.createElement("div");
        const titleHolderDiv = document.createElement("div");
        titleHolderDiv.setAttribute("class", "titleHolder");
        titleHolderDiv.innerHTML =
          '<strong style="color:black;">' + place.NAME + "</strong>";
        const coordinateHolderDiv = document.createElement("div");
        coordinateHolderDiv.setAttribute("class", "coordinateHolder");
        coordinateHolderDiv.innerHTML =
          "<p><b>LATITUDE:</b>" +
          place.LATITUDE +
          "</p><p><b>LONGITUDE:</b>" +
          place.LONGITUDE +
          "</p>";
        const iconsHolderDiv = document.createElement("div");
        iconsHolderDiv.setAttribute("class", "iconsHolder");
        const shareButtonDiv = document.createElement("div");
        shareButtonDiv.setAttribute("class", "icon");
        shareButtonDiv.style.backgroundColor = "white";
        shareButtonDiv.style.borderColor = "white";
        shareButtonDiv.addEventListener("click", () =>
          this.shareRow(place)
        );
        shareButtonDiv.innerHTML =
          '<i class="fas fa-share-nodes" aria-hidden="true"></i><span class="tooltiptext">Share</span>';
        const locationButtonDiv = document.createElement("div");
        locationButtonDiv.setAttribute("class", "icon");
        locationButtonDiv.style.backgroundColor = "white";
        locationButtonDiv.style.borderColor = "white";
        locationButtonDiv.addEventListener("click", () => {
          const url =
            "https://www.google.com/maps/search/?api=1&query=" +
            place.LATITUDE +
            "%2C" +
            place.LONGITUDE;
          window.open(url, "_blank");
        });
        locationButtonDiv.innerHTML =
          '<i class="fas fa-location-dot" aria-hidden="true"></i><span class="tooltiptext">Locate</span>';
        const editButtonDiv = document.createElement("div");
        editButtonDiv.setAttribute("class", "icon");
        editButtonDiv.style.backgroundColor = "white";
        editButtonDiv.style.borderColor = "white";
        editButtonDiv.addEventListener("click", () => this.editRow(place));
        editButtonDiv.innerHTML =
          '<i class="fas fa-edit" aria-hidden="true"></i><span class="tooltiptext">Edit</span>';
        const bookmarkButtonDiv = document.createElement("div");
        bookmarkButtonDiv.setAttribute("class", "icon");
        bookmarkButtonDiv.style.backgroundColor = "white";
        bookmarkButtonDiv.style.borderColor = "white";
        bookmarkButtonDiv.addEventListener("click", () =>
          this.bookmarkRow(place)
        );
        bookmarkButtonDiv.innerHTML =
          '<i id="myBookmark" class="fa fas fa-bookmark" aria-hidden="true"></i><span class="tooltiptext">Bookmark</span>';
        iconsHolderDiv.appendChild(shareButtonDiv);
        iconsHolderDiv.appendChild(locationButtonDiv);
        iconsHolderDiv.appendChild(editButtonDiv);
        iconsHolderDiv.appendChild(bookmarkButtonDiv);
        mainDiv.appendChild(titleHolderDiv);
        mainDiv.appendChild(coordinateHolderDiv);
        mainDiv.appendChild(iconsHolderDiv);
        // Create a new info window with the created content.
        const infowindow = new google.maps.InfoWindow({
          content: mainDiv,
        });
        // Open the info window on the map at the marker's position.
        infowindow.open(this.map, marker);
        currentInfoWindow = infowindow;
      });
      const map = this.map;
      // Add the created marker to the markers array.
      markers.push(marker);
      this.map = map;
    });
    // Center the map on the first marker's location and set a zoom level.
    const map = this.map;
    map.setCenter({
      lat: parseFloat(data[0].LATITUDE),
      lng: parseFloat(data[0].LONGITUDE),
    });
    map.setZoom(10);
    // Create a MarkerClusterer instance to manage the markers.
    const markerCluster = new MarkerClusterer({
      map: map,
    });
    // Add all the markers to the marker cluster.
    markers.forEach((marker) => {
      markerCluster.addMarker(marker);
    });
    // Save the markers to the map object for future reference.
    map.markers = markers;
    this.map = map;
  } else {
    // If data is empty or null, reset the map and reinitialize it without markers.
    this.map = null;
    this.initMap();
  }
}

To explain what this addMarkers method does:

  1. if the map data is not empty, it proceeds with the following steps; otherwise, it initializes the map and sets it to null.

  2. It initializes a Google Map and creates an empty array to hold markers.

  3. It iterates through each item in the map data, creating a marker on the map for each place listed in the map data. Each marker has a title, a click event listener, and an info window that displays additional information about the place when clicked.

  4. It sets up icons and event listeners for buttons within the info window, allowing actions like sharing, locating on Google Maps, editing, and bookmarking the place.

  5. The markers are clustered together for better visualization when there are multiple markers in close proximity.

updateTable

// Method to update the data table UI with the provided data
updateTable(data) {
  // Create a deep copy of the data to prevent direct modification of the original data.
  const duplicate_data = JSON.parse(JSON.stringify(data));
  // Remove the 'WKT' property from each item in the data, if it exists.
  duplicate_data.forEach((item) => {
    if (item.hasOwnProperty("WKT")) {
      delete item.WKT;
    }
  });
  // Create a proxy for the duplicate data to handle get and set operations dynamically.
  const proxy_data = new Proxy(duplicate_data, {
    get: function (target, prop) {
      return target[prop];
    },
    set: function (target, prop, value) {
      target[prop] = value;
      return true;
    },
  });
  // Get the table head and body elements from the DOM.
  const tableHeadRow = document.getElementById("table-tr");
  const tableBody = document.getElementById("mtsTbody");
  // Clear any existing content in the table head and body.
  tableHeadRow.innerHTML = "";
  tableBody.innerHTML = "";
  // Check if there is data to display.
  if (proxy_data.length != 0) {
    // Collect all unique property names from the data.
    const allProperties = new Set();
    proxy_data.forEach((item) => {
      Object.keys(item).forEach((propertyName) => {
        allProperties.add(propertyName);
      });
    });
    // Create header cells for each property and add them to the table head.
    allProperties.forEach((propertyName) => {
      const th = document.createElement("th");
      th.textContent = propertyName;
      tableHeadRow.appendChild(th);
    });
    // Add headers for 'EDIT' and 'OTHER ACTION' functionalities.
    var th = document.createElement("th");
    th.textContent = "EDIT";
    tableHeadRow.appendChild(th);
    th = document.createElement("th");
    th.textContent = "OTHER ACTION";
    tableHeadRow.appendChild(th);
    // Create and populate rows for each item in the data.
    proxy_data.forEach((item) => {
      const row = document.createElement("tr");
      // Create cells for each property value.
      allProperties.forEach((propertyName) => {
        const cell = document.createElement("td");
        cell.style.textAlign = "center";
        const propertyValue = item[propertyName];
        if (propertyValue !== undefined && propertyValue !== null) {
          cell.textContent = propertyValue;
        } else {
          // Optionally, style cells differently if the value is undefined or null.
          // cell.style.backgroundColor = "#adadad";
        }
        row.appendChild(cell);
      });
      // Add 'Edit' button to each row.
      const editCell = document.createElement("td");
      const editButton = document.createElement("button");
      editButton.setAttribute("class", "button");
      editButton.innerHTML = "<i class='fas fa-edit'></i>";
      editButton.addEventListener("click", () => this.editRow(item));
      editCell.appendChild(editButton);
      row.appendChild(editCell);
      // Add 'Other Action' buttons (delete, marker, share) to each row.
      const otherCell = document.createElement("td");
      // Delete button
      const deleteButton = document.createElement("button");
      deleteButton.classList.add("button");
      deleteButton.innerHTML = "<i class='fa fa-trash'></i>";
      deleteButton.addEventListener("click", () => this.deleteRow(item));
      otherCell.appendChild(deleteButton);
      // Marker button
      const markerButton = document.createElement("a");
      markerButton.href =
        "https://www.google.com/maps/search/?api=1&query=" +
        item["LATITUDE"] +
        "%2C" +
        item["LONGITUDE"];
      markerButton.target = "_blank";
      markerButton.innerHTML =
        "<button class='button'><i class='fa fa-map-marker'></i></button>";
      otherCell.appendChild(markerButton);
      // Share button
      const shareButton = document.createElement("button");
      shareButton.classList.add("button");
      shareButton.innerHTML = "<i class='fa fa-share-nodes'></i>";
      shareButton.addEventListener("click", () => this.shareRow(item));
      otherCell.appendChild(shareButton);
      row.appendChild(otherCell);
      tableBody.appendChild(row);
    });
  }
}

To explain what this updateTable method does:

  1. It creates a deep copy of the map data using JSON serialization and parsing. This is done to avoid modifying the original data.

  2. It removes the "WKT" property from each object in map data if it exists.

  3. It creates a Proxy object of the map data that allows getting and setting properties while ensuring they are always set successfully.

  4. It selects the table's header row and body elements in the HTML document.

  5. It clears the existing content of the table's header row and body.

  6. If Proxy object is not empty, it proceeds to dynamically generate the table's header based on the properties of the objects in the Proxy object.

  7. It iterates over the properties of the objects to create table header cells and appends them to the header row.

  8. For each object in the Proxy object, it generates a new row in the table body, filling in cells with property values.

  9. It adds buttons to each row in the "EDIT" and "OTHER ACTION" column. Clicking these buttons triggers specific actions, such as editing, deleting, opening the location on Google Maps, or sharing the row.

bookmarkRow

// Method to bookmark a specific map item for the logged-in user
bookmarkRow(itemForBookmark) {
  // Check if the user is logged in by verifying the presence of a 'userId' cookie.
  if (!this.$cookie.isCookieAvailable("L_userId")) {
    // Alert the user that login is required if the 'userId' cookie is not found.
    alert("LOGIN REQUIRED.");
  } else {
    // Prepare a request object with the user's ID, obtained from a cookie.
    const request = {
      userId: this.$cookie.getCookie("L_userId"),
    };
    // Send a POST request to the backend to retrieve the user's current bookmarks.
    axios
      .post(this.domainBackend + "/api/getBookmark", request)
      .then((response) => {
        // Check if the request was successful.
        if (response.data.SUCCESS == 1) {
          // Store the received bookmark data.
          const data = response.data.DATA;
          // Initialize a flag to check if the item already exists as a bookmark.
          var isExist = false;
          // Loop through each bookmarked item to check if the current item is already bookmarked.
          data.forEach((item) => {
            if (
              item.LATITUDE.toString() ===
                itemForBookmark.LATITUDE.toString() &&
              item.LONGITUDE.toString() ===
                itemForBookmark.LONGITUDE.toString()
            ) {
              // If the item is found, set the flag to true and exit the loop.
              isExist = true;
              return;
            }
          });
          // Check if the item already exists in the bookmarks.
          if (isExist == true) {
            // Alert the user that the item is already bookmarked.
            alert("BOOKMARK ALREADY EXISTS.");
          } else {
            // If the item is not already bookmarked, add it to the bookmarks data.
            data.push(itemForBookmark);
            // Prepare a request object with the updated bookmarks data.
            const request = {
              jsonData: JSON.stringify({
                USER_ID: this.$cookie.getCookie("L_userId"),
                DATA: data,
              }),
            };
            // Send a POST request to the backend to update the bookmarks with the new item.
            axios
              .post(this.domainBackend + "/api/storeBookmark", request)
              .then((response) => {
                // Check if the update was successful.
                if (response.data.SUCCESS == 1) {
                  // Alert the user that the bookmark has been updated.
                  alert("BOOKMARK UPDATED.");
                }
              });
          }
        }
      });
  }
}

To explain what this bookmarkRow method does:

  1. If the user is not logged in, it displays an alert, indicating that the user must be logged in to use this feature.

  2. it sends the user's ID to the backend to retrieve the data in the user's bookmark file.

  3. It then checks if the going-to-be-added bookmark item already exists in the user's bookmark data.

  4. If the item already exists in the user's bookmark data, displays an alert, indicating that the bookmark already exists.

  5. If the item does not exist in the user's bookmark data, it adds the item to the user's bookmark data and sends the bookmark data to update the user's bookmark file.

deleteRow

// Method to delete a specific row from the current map data
deleteRow(item) {
  // Loop through the current data set to find the item to delete.
  for (let i = 0; i < this.currentData.DATA.length; i++) {
    // Check if the ID of the current item matches the ID of the item to be deleted.
    if (this.currentData.DATA[i].ID == item.ID) {
      // Remove the item from the current data set.
      this.currentData.DATA.splice(i, 1);
      // Break the loop as the item has been found and removed.
      break;
    }
  }
  // Loop through the entire data set (allData) to update it with the changes.
  for (let i = 0; i < this.allData.length; i++) {
    for (let j = 0; j < this.allData[i].DATA.length; j++) {
      // Check if the ID of the current item matches the ID of the updated current data.
      if (this.allData[i].DATA[j].ID == this.currentData.ID) {
        // Update the allData item with the new current data.
        this.allData[i] = this.currentData;
        // Break the loop as the item has been found and updated.
        break;
      }
    }
  }
  // Check if the current data set is now empty.
  if (this.currentData.DATA.length == 0) {
    // If empty, reset the map and reinitialize it.
    this.map = null;
    this.initMap();
  } else {
    // If not empty, update the map markers with the current data.
    this.addMarkers(this.currentData.DATA);
  }
  // Update the table and map buttons to reflect the changes in the data.
  this.updateTable(this.currentData.DATA);
  this.updateMapButtons();
  // Update the cookies to store the current state of allData and currentData.
  this.$cookie.setCookie("ETM_allData", JSON.stringify(this.allData));
  this.$cookie.setCookie(
    "ETM_currentData",
    JSON.stringify(this.currentData)
  );
  // Check if the user is logged in (indicated by the availability of a user ID cookie).
  if (this.$cookie.isCookieAvailable("L_userId")) {
    // If logged in, store the updated map data to the backend or server.
    this.storeMap();
  }
}

To explain what this deleteRow method does:

  1. It loops through the currently selected map data array to find the item with an ID that matches the ID of the item that is passed in the method.

  2. Once the matching item is found, it removes it from the currently selected map data array.

  3. It then iterates through the map data array and its nested DATA arrays to find and update the item with the same ID as the ID of the currently selected map data with the modified currently selected map data.

  4. If there are no more items left in the currently selected map data, it sets the map to null and initializes a new map.

  5. If there are still items in the currently selected map data, it add the markers in the map, populates the table, and updates the map entries in the selection section.

  6. The map data array and currently selected map data are saved to the cookie.

  7. If the user is logged in, then the JSON representation of the map data array is sent to the backend so that the backend can write the JSON data to the map file of the user.

shareRow

// Method to prepare and open a sharing interface for a specific map item
shareRow(item) {
  // Retrieve the HTML element with the ID 'urlLink' from the DOM.
  const urlLink = document.getElementById("urlLink");
  // Set the value of the 'urlLink' element to a Google Maps search URL.
  // This URL is constructed using the latitude and longitude values from the 'item' object.
  // The 'item' object is expected to have 'LATITUDE' and 'LONGITUDE' properties.
  urlLink.value =
    "https://www.google.com/maps/search/?api=1&query=" +
    item["LATITUDE"] +
    "%2C" +
    item["LONGITUDE"];
  // Retrieve the HTML element with the ID 'containerShareToSocial' from the DOM.
  // This element likely acts as a container or interface for sharing functionality.
  const containerExportMap = document.getElementById(
    "containerShareToSocial"
  );
  // Set the display style of the 'containerShareToSocial' element to 'block',
  // making it visible on the webpage.
  // This action likely shows the interface for sharing the map location.
  containerExportMap.style.display = "block";
}

To explain what this shareRow method does:

  1. A pop-up box will be shown where the user can copy the Google Maps link of the map item.

editRow

// Method to open an interface for editing a specific map item
editRow(item) {
  // Select the element with the 'editMap' class and toggle the 'show' class.
  // This likely shows or hides the editing interface.
  const editMap = document.querySelector(".editMap");
  editMap.classList.toggle("show");
  // Set the inner HTML of the element with ID 'editMap_Title' to the item's name, wrapped in <strong> tags.
  // This displays the name of the item being edited.
  document.getElementById("editMap_Title").innerHTML =
    "<strong>" + item["NAME"] + "</strong>";
  // Select the 'editMap_table' class and reset its inner HTML.
  // This prepares the space to display editable fields for the item.
  const editMap_table = document.querySelector(".editMap_table");
  editMap_table.innerHTML = "";
  // Select the element with ID 'labelClassesContainerEdit' and reset its inner HTML.
  // This container might be used for additional editable fields.
  const labelClassesContainerEdit = document.getElementById(
    "labelClassesContainerEdit"
  );
  labelClassesContainerEdit.innerHTML = "";
  // Create a deep copy of the 'item' object to avoid modifying the original.
  const modifiedItem = JSON.parse(JSON.stringify(item));
  const propertyList = Object.keys(modifiedItem);
  // Iterate over the properties of the item.
  for (let i = 0; i < propertyList.length; i++) {
    const propertyName = propertyList[i];
    const propertyValue = modifiedItem[propertyName];
    // Create a div for each property with a label and input field.
    const divElement = document.createElement("div");
    divElement.className = "form-group";
    const labelElement = document.createElement("label");
    labelElement.className = "form-label";
    labelElement.innerHTML =
      "<div style='padding:5px;display:inline-block'>" +
      propertyName +
      "</div>";
    labelElement.setAttribute("name", propertyName);
    divElement.appendChild(labelElement);
    // Exclude certain properties from being editable (like ID, NAME, LATITUDE, LONGITUDE).
    if (
      propertyName != "ID" &&
      propertyName != "NAME" &&
      propertyName != "LATITUDE" &&
      propertyName != "LONGITUDE"
    ) {
      // Create a delete button for additional properties (excluding the excluded ones).
      var deleteButton = document.createElement("div");
      deleteButton.setAttribute("class", "buttonFormElementDelete");
      deleteButton.textContent = "DELETE";
      deleteButton.addEventListener("click", () => {
        divElement.remove();
      });
      divElement.appendChild(deleteButton);
    }
    // Create an input field for each property.
    const inputElement = document.createElement("input");
    inputElement.id = propertyName;
    inputElement.type = "text";
    inputElement.className = "form-control";
    inputElement.value = propertyValue;
    inputElement.readOnly =
      propertyName == "ID" ||
      propertyName == "LATITUDE" ||
      propertyName == "LONGITUDE";
    divElement.appendChild(inputElement);
    // Append the div element to the editMap_table.
    editMap_table.appendChild(divElement);
  }
  // When the 'saveChanges' button is clicked, update the item with new values from the input fields.
  const saveChanges = document.getElementById("saveChanges");
  saveChanges.addEventListener("click", () => {
    // Iterate over all labels and update the item object with new values.
    var labelList = document.querySelectorAll(
      "#editMap_table label, #labelClassesContainerEdit label"
    );
    var item = {};
    for (let i = 0; i < labelList.length; i++) {
      const label = labelList[i];
      const labelText = label.getAttribute("name");
      const inputName = document.getElementById(labelText).value;
      item[labelText] = inputName;
    }
    // Create a proxy for the item to handle get and set operations dynamically.
    const proxy_item = new Proxy(item, {
      get: function (target, prop) {
        return target[prop];
      },
      set: function (target, prop, value) {
        target[prop] = value;
        return true;
      },
    });
    // Update the currentData and allData arrays with the modified item.
    for (let i = 0; i < this.currentData.DATA.length; i++) {
      if (this.currentData.DATA[i].ID == proxy_item.ID) {
        this.currentData.DATA[i] = proxy_item;
      }
    }
    for (let i = 0; i < this.allData.length; i++) {
      for (let j = 0; j < this.allData[i].DATA.length; j++) {
        if (this.allData[i].DATA[j].ID == this.currentData.ID) {
          this.allData[i] = this.currentData;
        }
      }
    }
    // Update the map, table, and buttons with the new data.
    this.addMarkers(this.currentData.DATA);
    this.updateTable(this.currentData.DATA);
    this.updateMapButtons();
    // Update the local cookies with the new allData and currentData.
    this.$cookie.setCookie("ETM_allData", JSON.stringify(this.allData));
    this.$cookie.setCookie(
      "ETM_currentData",
      JSON.stringify(this.currentData)
    );
    // If the user is logged in, store the updated map data to the server.
    if (this.$cookie.isCookieAvailable("L_userId")) {
      this.storeMap();
    }
  });
}

To explain what this editRow method does:

  1. It toggles the visibility of the pop-up box for editing the row.

  2. It updates the title of the pop-up box to display the name of the map item being edited.

  3. It retrieves the list of property names from map item and iterates through them.

  4. For each property, it creates an input field in the editing interface and populates it with the property's value. It also adds a "DELETE" button for properties other than "ID", "NAME", "LATITUDE", and "LONGITUDE." The "DELETE" button allows removing properties from the item.

  5. When the "Save Changes" button is clicked, it retrieves the updated values of the properties from the input fields.

  6. It uses a Proxy object to allow getting and setting properties in the item.

  7. It updates the corresponding map item in the currently selected map data with the edited map item.

  8. It also updated the map data array with the modified currently selected data.

  9. It add the markers in the map, populates the table, and updates the map entries in the selection section.

  10. The map data array and currently selected map data are saved to the cookie.

  11. If the user is logged in, then the JSON representation of the map data array is sent to the backend so that the backend can write the JSON data to the map file of the user.

addLabelClassToEdit

// Method to add a new label input field to the map item editing interface
addLabelClassToEdit() {
  // Display a prompt asking the user for a label title.
  var labelTitle = prompt("LABEL:");
  // Check if the user entered a non-empty label title.
  if (labelTitle !== null && labelTitle.trim() !== "") {
    // Retrieve the element with the ID 'labelClassesContainerEdit'.
    // This is likely a container where the new label and input will be added.
    var labelClassesContainerEdit = document.getElementById(
      "labelClassesContainerEdit"
    );
    // Create a new div element to act as a container for the new form group.
    var labelClassContainer = document.createElement("div");
    labelClassContainer.classList.add("form-group");
    // Create a new label element for the form group.
    var titleLabel = document.createElement("label");
    titleLabel.classList.add("form-label");
    // Set the inner HTML of the label to the user-provided title, converted to uppercase.
    titleLabel.innerHTML =
      "<div style='padding:5px;display:inline-block'>" +
      labelTitle.toUpperCase() +
      "</div>";
    // Set the text content of the label to the user-provided title, converted to uppercase.
    titleLabel.textContent = labelTitle.toUpperCase();
    // Set a 'name' attribute to the label with the title.
    titleLabel.setAttribute("name", labelTitle.toUpperCase());
    // Create a new div element to act as a delete button.
    var deleteButton = document.createElement("div");
    deleteButton.setAttribute("class", "buttonFormElementDelete");
    deleteButton.textContent = "DELETE";
    // Add an event listener to the delete button to remove the labelClassContainer when clicked.
    deleteButton.addEventListener("click", () => {
      labelClassContainer.remove();
    });
    // Create a new input element for the form group.
    var valueInput = document.createElement("input");
    valueInput.setAttribute("type", "text");
    valueInput.setAttribute("class", "form-control");
    valueInput.setAttribute("id", labelTitle.toUpperCase());
    valueInput.setAttribute("value", "");
    // Append the label, delete button, and input to the label class container.
    labelClassContainer.appendChild(titleLabel);
    labelClassContainer.appendChild(deleteButton);
    labelClassContainer.appendChild(valueInput);
    // Append the new label class container to the labelClassesContainerEdit element.
    labelClassesContainerEdit.appendChild(labelClassContainer);
  }
}

To explain what this addLabelClassToEdit method does:

  1. It prompts the user for a label title, and it stores the user's.

  2. If the label title is not null and is not just whitespace, it proceeds to create and add an HTML element for this label in the pop-up box for editing the row.

  3. It creates a label element for the title.

  4. It creates an input field for the label's value, setting its initial value to an empty string.

  5. It creates a "DELETE" button for removing the label, which triggers the removal of the label when clicked.

closeEditMap

// Method to close the map editing interface
closeEditMap() {
  // Select the element with the class 'editMap' from the document.
  // This element is assumed to be the container for the map editing interface.
  const editMap = document.querySelector(".editMap");
  // Remove the 'show' class from the 'editMap' element.
  // The 'show' class likely controls the visibility of the editing interface,
  // so removing it would hide or close the editing interface.
  editMap.classList.remove("show");
}

To explain what this closeEditMap method does:

  1. It will the close the pop-up box for editing the row.

Last updated