MapToExcel.vue

This is where users can create their own map with a set of places by adding markers onto 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
import axios from "axios";

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.

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: "",
    latitude: "",
    longitude: "",
    allData: [],
    map: null,
    countId: 1,
  };
}

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() {
  // Scrolls the window to the top when the component is first loaded.
  window.scrollTo(0, 0);
  // Retrieves and sets the backend domain from the Vuex store.
  this.domainBackend = this.$store.state.domainBackend;
  // Checks if cookies for 'MTE_allData' and 'MTE_countId' are available. If not, initializes them.
  if (
    !this.$cookie.isCookieAvailable("MTE_allData") ||
    !this.$cookie.isCookieAvailable("MTE_countId")
  ) {
    this.$cookie.setCookie("MTE_allData", JSON.stringify([]));
    this.$cookie.setCookie("MTE_countId", 1);
  } else {
    // If cookies are available, parses and sets the 'allData' and 'countId' from the cookies.
    this.allData = JSON.parse(this.$cookie.getCookie("MTE_allData"));
    this.countId = parseInt(this.$cookie.getCookie("MTE_countId"));
  }
  // Initializes the map when the component is mounted.
  this.initMap();
  // Updates the table with data if 'allData' is not empty.
  if (this.allData.length != 0) {
    this.updateTable(this.allData);
  }
}

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 domain from what is defined in the Vuex.

  3. 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, it will just initialize an empty map to display and populates the table with rows.

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

openExportMap

// Method to display the export map container
openExportMap() {
  // Get the HTML element with the id "containerExportMap"
  const containerExportMap = document.getElementById("containerExportMap");
  // Set the display style property to "block" to make it visible
  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 hide the export map container
closeExportMap() {
  // Get the HTML element with the id "containerExportMap"
  const containerExportMap = document.getElementById("containerExportMap");
  // Set the display style property to "none" to hide the container
  containerExportMap.style.display = "none";
}

To explain what this closeExportMap method does:

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

convertJsonToGoogleSheet

// Method to convert JSON data to a Google Sheet
convertJsonToGoogleSheet() {
  // Create a deep copy of the data to avoid modifying the original
  const data = JSON.parse(JSON.stringify(this.allData));
  // Remove LATITUDE and LONGITUDE properties from each item in the data
  data.forEach((item) => {
    delete item.LATITUDE;
    delete item.LONGITUDE;
  });
  // Generate a unique title using a hash function
  const title = this.makeHash(32);
  // Convert the data to a JSON string
  const jsonData = JSON.stringify(data);
  // Get the backend domain from the component's state
  const domainBackend = this.domainBackend;
  // Set properties for the popup window
  const popupProperties = "width=500,height=800,scrollbars=yes";
  // Initialize a popup window
  let windowPopUp;
  windowPopUp = window.open("", "Google", popupProperties);
  // Authenticate Google account for Google Sheets API
  axios
    .get(
      this.domainBackend + "/api/authenticateGoogleAccountForGoogleSheet"
    )
    .then((response) => {
      if (response.data.SUCCESS == 1) {
        // Get the authentication URL and set the popup window's location
        const url = response.data.AUTH_URL;
        windowPopUp.location.href = url;
        // Monitor changes in the popup window's URL
        monitorUrlChanges();
      }
    });
  // Function to monitor changes in the popup window's URL
  function monitorUrlChanges() {
    const interval = setInterval(() => {
      if (windowPopUp && !windowPopUp.closed) {
        // Check if the URL contains the authorization code
        const urlPopUp = windowPopUp.location.href;
        if (urlPopUp.includes("?code=")) {
          // Extract the code from the URL
          const params = new URLSearchParams(urlPopUp.split("?")[1]);
          const code = params.get("code");
          // Send the JSON data and code for Google Sheets conversion
          sendJson(code);
          clearInterval(interval);
        }
      } else {
        // Clear the interval if the popup window is closed
        clearInterval(interval);
      }
    }, 1000);
  }
  // Function to send JSON data and code for Google Sheets conversion
  function sendJson(code) {
    const request = {
      title: title,
      jsonData: jsonData,
      code: code,
    };
    // Make a POST request to convert JSON to Google Sheets
    axios
      .post(domainBackend + "/api/convertJsonToGoogleSheet", request)
      .then((response) => {
        // Close the popup window and open the generated Google Sheets link in a new tab
        windowPopUp.close();
        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 data to a CSV file
convertJsonToCsv() {
  // Create a deep copy of the data to avoid modifying the original
  const data = JSON.parse(JSON.stringify(this.allData));
  // Remove LATITUDE and LONGITUDE properties from each item in the data
  data.forEach((item) => {
    delete item.LATITUDE;
    delete item.LONGITUDE;
  });
  // Create a request object containing the JSON data
  const request = {
    jsonData: JSON.stringify(data),
  };
  // Make a POST request to convert JSON to CSV on the backend
  axios
    .post(this.domainBackend + "/api/convertJsonToCsv", request)
    .then((response) => {
      // Check if the conversion was successful
      if (response.data.SUCCESS == 1) {
        // Create a Blob containing the CSV data
        const blob = new Blob([response.data.DATA], { type: "text/csv" });
        // Create a download URL for the Blob
        const url = window.URL.createObjectURL(blob);
        // Create a link element for initiating the download
        const a = document.createElement("a");
        a.href = url;
        // Set the download attribute with a unique filename (using a hash)
        a.download = this.makeHash(32) + ".csv";
        // Append the link to the document body
        document.body.appendChild(a);
        // Simulate a click on the link to initiate the download
        a.click();
        // Revoke the download URL to free up resources
        window.URL.revokeObjectURL(url);
        // Remove the link element from the document body
        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.

downloadKml

// Method to download data as a KML file
downloadKml() {
  // Create a deep copy of the data to avoid modifying the original
  const data = JSON.parse(JSON.stringify(this.allData));
  // Remove LATITUDE and LONGITUDE properties from each item in the data
  data.forEach((item) => {
    delete item.LATITUDE;
    delete item.LONGITUDE;
  });
  // Create a request object containing the JSON data
  const request = {
    jsonData: JSON.stringify(data),
  };
  // Make a POST request to convert JSON to KML on the backend
  axios
    .post(this.domainBackend + "/api/convertJsonToKml", request)
    .then((response) => {
      // Check if the conversion was successful
      if (response.data.SUCCESS == 1) {
        // Create a Blob containing the KML data
        const blob = new Blob([response.data.DATA], { type: "text/kml" });
        // Create a download URL for the Blob
        const url = window.URL.createObjectURL(blob);
        // Create a link element for initiating the download
        const a = document.createElement("a");
        a.href = url;
        // Set the download attribute with a unique filename (using a hash)
        a.download = this.makeHash(32) + ".kml";
        // Append the link to the document body
        document.body.appendChild(a);
        // Simulate a click on the link to initiate the download
        a.click();
        // Revoke the download URL to free up resources
        window.URL.revokeObjectURL(url);
        // Remove the link element from the document body
        document.body.removeChild(a);
      }
    });
}

To explain what this downloadKml method does:

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

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

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

downloadJson

// Method to download data in JSON format
downloadJson() {
  // Create a deep copy of the data to avoid modifying the original
  const data = JSON.parse(JSON.stringify(this.allData));
  // Remove LATITUDE and LONGITUDE properties from each item in the data
  data.forEach((item) => {
    delete item.LATITUDE;
    delete item.LONGITUDE;
  });
  // Convert the filtered data to a nicely formatted JSON string
  const jsonData = JSON.stringify(data, null, 2);
  // Create a Blob containing the JSON data
  const blob = new Blob([jsonData], { type: "application/json" });
  // Create a download URL for the Blob
  const url = window.URL.createObjectURL(blob);
  // Create a link element for initiating the download
  const a = document.createElement("a");
  a.href = url;
  // Set the download attribute with a unique filename (using a hash)
  a.download = this.makeHash(32) + ".json";
  // Append the link to the document body
  document.body.appendChild(a);
  // Simulate a click on the link to initiate the download
  a.click();
  // Revoke the download URL to free up resources
  window.URL.revokeObjectURL(url);
  // Remove the link element from the document body
  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.

makeHash

// Method to generate a random hash of a specified length
makeHash(length) {
  // Initialize an empty string to store the generated hash
  let result = "";
  // Define the characters that can be used in the hash
  const characters =
    "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
  // Get the total number of characters available
  const charactersLength = characters.length;
  // Counter to keep track of the number of characters added to the hash
  let counter = 0;
  // Loop until the desired length of the hash is reached
  while (counter < length) {
    // Append a random character from the available set to the hash
    result += characters.charAt(
      Math.floor(Math.random() * charactersLength)
    );
    // Increment the counter
    counter += 1;
  }
  // Return the generated hash
  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.

initMap

// Method to initialize the Google Map
initMap() {
  // Create a new Google Map instance and assign it to the 'map' property
  this.map = new google.maps.Map(document.getElementById("map"), {
    center: { lat: 0, lng: 0 }, // Set initial center coordinates
    zoom: 2, // Set initial zoom level
    markers: [], // Initialize an array to store markers on the map
  });
  // Add a click event listener to the map to handle marker placement
  google.maps.event.addListener(this.map, "click", (event) => {
    this.placeMarker(event.latLng); // Call the placeMarker function on map click
  });
  // Check if geolocation is supported by the browser
  if ("geolocation" in navigator) {
    // Get the current geolocation of the user
    navigator.geolocation.getCurrentPosition((position) => {
      // Create a LatLng object using the user's coordinates
      var userLatLng = new google.maps.LatLng(
        position.coords.latitude,
        position.coords.longitude
      );
      // Create a marker for the user's position
      var userMarker = new google.maps.Marker({
        position: userLatLng,
        map: this.map,
        icon: {
          url: "https://cdn-icons-png.flaticon.com/512/1673/1673221.png", // Set custom marker icon
          scaledSize: new google.maps.Size(52, 52), // Set custom marker size
        },
      });
      // Set the zoom level and center of the map to the user's location
      var zoomLevel = 16;
      this.map.setZoom(zoomLevel);
      this.map.setCenter(userLatLng);
      // Add the user's marker to the array of markers on the map
      this.map.markers.push(userMarker);
    });
  }
}

To explain what this initMap method does:

  1. It initializes a Google Map on the page.

  2. If the user allows the browser's location permission, then the map will pinpoint their current location on the map.

placeMarker

// Method to place a marker on the map at a specified location
placeMarker(location) {
  // Check if a marker already exists, and if so, remove it from the map
  if (this.marker) {
    this.marker.setMap(null);
  }
  // Create a new marker at the specified location and assign it to the 'marker' property
  this.marker = new google.maps.Marker({
    position: location, // Set the marker's position to the specified location
    map: this.map, // Set the map on which the marker will be placed
  });
  // Extract latitude and longitude from the marker's position
  const lat = location.lat();
  const lng = location.lng();
  // Update the 'latitude' and 'longitude' properties with the marker's coordinates
  this.latitude = lat.toString(); // Convert latitude to string
  this.longitude = lng.toString(); // Convert longitude to string
}

To explain what this placeMarker method does:

  1. It takes a location, which is the latitude and longitude of a place.

  2. It checks if a previously created marker exists. If it does, it removes it from the map by setting its map property to null.

  3. It creates a new Google Maps marker, positioning it at the specified location.

  4. It stores the latitude and longitude value.

addLabelClassToEdit

// Method to add a new label class in the edit interface
addLabelClassToEdit() {
  // Prompt the user to enter a label for the class
  var labelTitle = prompt("LABEL:");
  // Check if a label was provided and it is not empty
  if (labelTitle !== null && labelTitle.trim() !== "") {
    // Get the container element for editing label classes
    var labelClassesContainerEdit = document.getElementById(
      "labelClassesContainerEdit"
    );
    // Create a new container div for the labeled class
    var labelClassContainer = document.createElement("div");
    labelClassContainer.classList.add("form-group");
    // Create a label element to display the title
    var titleLabel = document.createElement("label");
    titleLabel.classList.add("form-label");
    titleLabel.innerHTML =
      "<div style='padding:5px;display:inline-block'>" +
      labelTitle.toUpperCase() +
      "</div>";
    titleLabel.textContent = labelTitle.toUpperCase();
    titleLabel.setAttribute("name", labelTitle.toUpperCase());
    // Create a delete button to remove the labeled class
    var deleteButton = document.createElement("div");
    deleteButton.setAttribute("class", "buttonFormElementDelete");
    deleteButton.textContent = "DELETE";
    deleteButton.addEventListener("click", () => {
      labelClassContainer.remove();
    });
    // Create an input element for the value of the labeled class
    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 value input to the container div
    labelClassContainer.appendChild(titleLabel);
    labelClassContainer.appendChild(deleteButton);
    labelClassContainer.appendChild(valueInput);
    // Append the container div to the container for editing label classes
    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.

saveChanges

// Method to save changes made in the edit interface
saveChanges() {
  // Get the table element for editing the map
  var editMap_table = document.getElementById("editMap_table");
  // Get a list of all label elements within the edit map table
  var labelList = editMap_table.querySelectorAll("label");
  // Create a new item object to store the data
  var item = {};
  // Assign a unique ID to the item
  item["ID"] = this.countId;
  this.countId = this.countId + 1;
  this.$cookie.setCookie("MTE_countId", this.countId);
  // Initialize latitude and longitude variables
  var latitude = "";
  var longitude = "";
  // Iterate through each label in the edit map table
  for (let i = 0; i < labelList.length; i++) {
    const label = labelList[i];
    // Get the label's text, which represents the property name
    const labelText = label.getAttribute("name");
    // Get the input value corresponding to the property
    const inputName = document.getElementById(labelText).value;
    // Check if the property is latitude or longitude
    if (labelText == "LONGITUDE") {
      longitude = inputName;
    } else if (labelText == "LATITUDE") {
      latitude = inputName;
    }
    // Add the property and its value to the item object
    item[labelText] = inputName;
  }
  // Add a "WKT" property to the item representing a point with latitude and longitude
  item["WKT"] = `POINT (${longitude} ${latitude})`;
  // Get the container for editing label classes
  var labelClassesContainerEdit = document.getElementById(
    "labelClassesContainerEdit"
  );
  // Get a list of all label elements within the label classes container
  labelList = labelClassesContainerEdit.querySelectorAll("label");
  // Iterate through each label in the label classes container
  for (let i = 0; i < labelList.length; i++) {
    const label = labelList[i];
    // Get the label's text, which represents the property name
    const labelText = label.getAttribute("name");
    // Get the input value corresponding to the property
    const inputName = document.getElementById(labelText).value;
    // Add the property and its value to the item object
    item[labelText] = inputName;
  }
  // Create a proxy for the item object to observe changes
  const proxy_item = new Proxy(item, {
    get: function (target, prop) {
      return target[prop];
    },
    set: function (target, prop, value) {
      target[prop] = value;
      return true;
    },
  });
  // Add the proxy_item to the array of all data
  this.allData.push(proxy_item);
  // Save the updated data and update the table
  this.$cookie.setCookie("MTE_allData", JSON.stringify(this.allData));
  this.updateTable(this.allData);
}

To explain what this saveChanges method does:

  1. It retrieves all the label elements within the map adding section.

  2. It initializes an empty object and assigns a unique ID (incremented by 1) to its "ID" property. It also updates and saves this ID in the cookie.

  3. It iterates through the label elements and extracts the value of associated input elements. Depending on whether the label represents "LONGITUDE" or "LATITUDE," it stores these values in separate variables.

  4. It constructs a "WKT" (Well-Known Text) string using the longitude and latitude values and assigns it to the "WKT" property of the item.

  5. It retrieves all label elements within the user-added-labels section. It then iterates through these elements, extracting input values and adding them as properties to the item.

  6. It creates a Proxy object to watch for changes to the item object properties, allowing for custom behavior when getting or setting properties.

  7. It adds the Proxy object to the map data array.

  8. It saves the updated map data array to the cookie

  9. Finally, it populates the table with the new map data array.

updateTable

// Method to update the table with new or modified data
updateTable(data) {
  // Create a deep copy of the data to avoid modifying the original
  const duplicate_data = JSON.parse(JSON.stringify(data));
  // Remove the "WKT" property from each item in the duplicated data
  duplicate_data.forEach((item) => {
    if (item.hasOwnProperty("WKT")) {
      delete item.WKT;
    }
  });
  // Create a Proxy for the duplicated data to observe changes
  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 row and table body elements
  const tableHeadRow = document.getElementById("table-tr");
  const tableBody = document.getElementById("mtsTbody");
  // Clear the existing content of the table head and body
  tableHeadRow.innerHTML = "";
  tableBody.innerHTML = "";
  // Check if there is data to display
  if (proxy_data.length != 0) {
    // Create a set to store all unique property names across items
    const allProperties = new Set();
    // Iterate through each item to collect all property names
    proxy_data.forEach((item) => {
      Object.keys(item).forEach((propertyName) => {
        allProperties.add(propertyName);
      });
    });
    // Create table header cells for each property name
    allProperties.forEach((propertyName) => {
      const th = document.createElement("th");
      th.textContent = propertyName;
      tableHeadRow.appendChild(th);
    });
    // Create "EDIT" and "OTHER ACTION" header cells
    var th = document.createElement("th");
    th.textContent = "EDIT";
    tableHeadRow.appendChild(th);
    th = document.createElement("th");
    th.textContent = "OTHER ACTION";
    tableHeadRow.appendChild(th);
    // Iterate through each item to populate table rows
    proxy_data.forEach((item) => {
      // Create a new table row for each item
      const row = document.createElement("tr");
      // Populate cells with data for each property
      allProperties.forEach((propertyName) => {
        const cell = document.createElement("td");
        cell.style.textAlign = "center";
        const propertyValue = item[propertyName];
        // Display property value if not undefined or null
        if (propertyValue !== undefined && propertyValue !== null) {
          cell.textContent = propertyValue;
        } else {
          // Optionally handle undefined or null values
          // cell.style.backgroundColor = "#adadad";
        }
        row.appendChild(cell);
      });
      // Create "EDIT" cell with an edit button
      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);
      // Create "OTHER ACTION" cell with delete, marker, and share buttons
      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 for Google Maps link
      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);
      // Append "OTHER ACTION" cell to the row
      row.appendChild(otherCell);
      // Append the row to the table body
      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.

deleteRow

// Method to delete a row of data
deleteRow(item) {
  // Iterate through the data to find the item with matching ID
  for (let i = 0; i < this.allData.length; i++) {
    // Check if the ID of the current item matches the target item's ID
    if (this.allData[i].ID === item.ID) {
      // Remove the item from the data array
      this.allData.splice(i, 1);
      // Exit the loop since the item has been found and removed
      break;
    }
  }
  // Create a data object to store the modified data
  const data = {
    DATA: [],
  };
  // Deep copy each item in the data and add it to the new data object
  this.allData.forEach((item) => {
    const jsonData = JSON.parse(JSON.stringify(item));
    data.DATA.push(jsonData);
  });
  // Check if there are no more items in the data
  if (this.allData.length == 0) {
    // If there are no items, clear the table head and body
    const tableHeadRow = document.getElementById("table-tr");
    const tableBody = document.getElementById("mtsTbody");
    tableHeadRow.innerHTML = "";
    tableBody.innerHTML = "";
  } else {
    // If there are remaining items, update the table with the modified data
    this.updateTable(data.DATA);
  }
  // Update the cookie with the modified data
  this.$cookie.setCookie("MTE_allData", JSON.stringify(this.allData));
}

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 share a specific row of data
shareRow(item) {
  // Get the input field for the URL link
  const urlLink = document.getElementById("urlLink");
  // Construct the Google Maps URL with latitude and longitude from the item
  urlLink.value =
    "https://www.google.com/maps/search/?api=1&query=" +
    item["LATITUDE"] +
    "%2C" +
    item["LONGITUDE"];
  // Get the container for sharing on social media
  const containerShareToSocial = document.getElementById(
    "containerShareToSocial"
  );
  // Display the container for sharing on social media
  containerShareToSocial.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 edit a specific row of data
editRow(item) {
  // Get the edit map pop-up container
  const editMapPopUp = document.getElementById("editMapPopUp");
  // Display the edit map pop-up
  editMapPopUp.style.display = "block";
  // Set the title of the pop-up with the row's name
  document.getElementById("editMap_TitlePopUp").innerHTML =
    "<strong>" + item["NAME"] + "</strong>";
  // Get the table and label classes container for the pop-up
  const editMap_tablePopUp = document.getElementById("editMap_tablePopUp");
  const labelClassesContainerEditPopUp = document.getElementById(
    "labelClassesContainerEditPopUp"
  );
  // Clear the contents of the table and label classes container
  editMap_tablePopUp.innerHTML = "";
  labelClassesContainerEditPopUp.innerHTML = "";
  // Create a deep copy of the item to avoid modifying the original data
  const modifiedItem = JSON.parse(JSON.stringify(item));
  // Get the list of properties from the modified item
  const propertyList = Object.keys(modifiedItem);
  // Loop through each property in the item
  for (let i = 0; i < propertyList.length; i++) {
    const propertyName = propertyList[i];
    const propertyValue = modifiedItem[propertyName];
    // Create a div element for each property
    const divElement = document.createElement("div");
    divElement.className = "form-group";
    // Create a label element for the property
    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);
    // Check if the property should be deletable
    if (
      propertyName == "ID" ||
      propertyName == "NAME" ||
      propertyName == "LATITUDE" ||
      propertyName == "LONGITUDE"
    ) {
      // Do nothing for non-deletable properties
    } else {
      // Create a delete button for deletable properties
      var deleteButton = document.createElement("div");
      deleteButton.setAttribute("class", "buttonFormElementDelete");
      deleteButton.textContent = "DELETE";
      // Add an event listener to delete the div element when the button is clicked
      deleteButton.addEventListener("click", () => {
        divElement.remove();
      });
      // Append the delete button to the div element
      divElement.appendChild(deleteButton);
    }
    // Create an input element for the property
    const inputElement = document.createElement("input");
    inputElement.id = propertyName + "PopUp";
    inputElement.type = "text";
    inputElement.className = "form-control";
    inputElement.value = propertyValue;
    // Set readOnly property for certain fields
    if (propertyName == "ID" || propertyName == "WKT") {
      inputElement.readOnly = true;
    } else {
      inputElement.readOnly = false;
    }
    // Append the input element to the div element
    divElement.appendChild(inputElement);
    // Hide the "ID" field in the pop-up
    if (propertyName == "ID") {
      divElement.style.display = "none";
    }
    // Append the div element to the table in the pop-up
    editMap_tablePopUp.appendChild(divElement);
  }
  // Get the "Save Changes" button in the pop-up
  const saveChangesPopUp = document.getElementById("saveChangesPopUp");
  // Add an event listener to save the changes when the button is clicked
  saveChangesPopUp.addEventListener("click", () => {
    // Get the table and label list in the pop-up
    var editMap_tablePopUp = document.getElementById("editMap_tablePopUp");
    var labelList = editMap_tablePopUp.querySelectorAll("label");
    // Create an object to store the edited item
    var item = {};
    // Loop through each label in the pop-up
    for (let i = 0; i < labelList.length; i++) {
      const label = labelList[i];
      const labelText = label.getAttribute("name");
      // Get the input value corresponding to the label
      const inputName = document.getElementById(labelText + "PopUp").value;
      // Add the property and its value to the item object
      item[labelText] = inputName;
    }
    // Get the label list in the label classes container
    labelClassesContainerEditPopUp = document.getElementById(
      "labelClassesContainerEditPopUp"
    );
    labelList = labelClassesContainerEditPopUp.querySelectorAll("label");
    // Loop through each label in the label classes container
    for (let i = 0; i < labelList.length; i++) {
      const label = labelList[i];
      const labelText = label.getAttribute("name");
      // Get the input value corresponding to the label
      const inputName = document.getElementById(labelText + "PopUp").value;
      // Add the property and its value to the item object
      item[labelText] = inputName;
    }
    // Create a Proxy for the item object
    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 original data with the edited item
    for (let i = 0; i < this.allData.length; i++) {
      if (this.allData[i].ID == proxy_item.ID) {
        this.allData[i] = proxy_item;
      }
    }
    // Create an object to store the data for updating the table
    const data = {
      DATA: [],
    };
    // Add each item in the updated data to the data object
    this.allData.forEach((item) => {
      const jsonData = JSON.parse(JSON.stringify(item));
      data.DATA.push(jsonData);
    });
    // Update the table with the new data
    this.updateTable(data.DATA);
    // Save the updated data to cookies
    this.$cookie.setCookie("MTE_allData", JSON.stringify(this.allData));
  });
}

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 class in the edit pop-up interface
addLabelClassToEditPopUp() {
  // Prompt the user for a label title
  var labelTitle = prompt("LABEL:");
  // Check if a valid label title is provided
  if (labelTitle !== null && labelTitle.trim() !== "") {
    // Get the container for label classes in the edit pop-up
    var labelClassesContainerEditPopUp = document.getElementById(
      "labelClassesContainerEditPopUp"
    );
    // Create a container div for the label class
    var labelClassContainer = document.createElement("div");
    labelClassContainer.classList.add("form-group");
    // Create a label element for the title
    var titleLabel = document.createElement("label");
    titleLabel.classList.add("form-label");
    titleLabel.innerHTML =
      "<div style='padding:5px;display:inline-block'>" +
      labelTitle.toUpperCase() +
      "</div>";
    titleLabel.textContent = labelTitle.toUpperCase();
    titleLabel.setAttribute("name", labelTitle.toUpperCase());
    // Create a delete button for the label class
    var deleteButton = document.createElement("div");
    deleteButton.setAttribute("class", "buttonFormElementDelete");
    deleteButton.textContent = "DELETE";
    // Add an event listener to delete the label class container when the button is clicked
    deleteButton.addEventListener("click", () => {
      labelClassContainer.remove();
    });
    // Create an input element for the value
    var valueInput = document.createElement("input");
    valueInput.setAttribute("type", "text");
    valueInput.setAttribute("class", "form-control");
    valueInput.setAttribute("id", labelTitle.toUpperCase() + "PopUp");
    valueInput.setAttribute("value", "");
    // Append the title, delete button, and value input to the label class container
    labelClassContainer.appendChild(titleLabel);
    labelClassContainer.appendChild(deleteButton);
    labelClassContainer.appendChild(valueInput);
    // Append the label class container to the label classes container in the edit pop-up
    labelClassesContainerEditPopUp.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 edit map pop-up
closeEditMap() {
  // Get the edit map pop-up element by its ID
  const editMapPopUp = document.getElementById("editMapPopUp");
  // Set the display style to "none" to hide the edit map pop-up
  editMapPopUp.style.display = "none";
}

To explain what this closeEditMap method does:

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

Last updated